Autopsy  4.19.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
PhotoRecCarverFileIngestModule.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-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.photoreccarver;
20 
21 import java.io.File;
22 import java.io.IOException;
23 import java.lang.ProcessBuilder.Redirect;
24 import java.nio.file.DirectoryStream;
25 import java.nio.file.FileAlreadyExistsException;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.text.DateFormat;
30 import java.text.SimpleDateFormat;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Date;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.concurrent.atomic.AtomicLong;
39 import java.util.logging.Level;
40 import java.util.stream.Collectors;
41 import org.openide.modules.InstalledFileLocator;
42 import org.openide.util.NbBundle;
62 import org.sleuthkit.datamodel.AbstractFile;
63 import org.sleuthkit.datamodel.LayoutFile;
64 import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
65 import org.sleuthkit.datamodel.TskData;
66 
71 @NbBundle.Messages({
72  "PhotoRecIngestModule.PermissionsNotSufficient=Insufficient permissions accessing",
73  "PhotoRecIngestModule.PermissionsNotSufficientSeeReference=See 'Shared Drive Authentication' in Autopsy help.",
74  "# {0} - output directory name", "cannotCreateOutputDir.message=Unable to create output directory: {0}.",
75  "unallocatedSpaceProcessingSettingsError.message=The selected file ingest filter ignores unallocated space. This module carves unallocated space. Please choose a filter which does not ignore unallocated space or disable this module.",
76  "unsupportedOS.message=PhotoRec module is supported on Windows platforms only.",
77  "missingExecutable.message=Unable to locate PhotoRec executable.",
78  "cannotRunExecutable.message=Unable to execute PhotoRec.",
79  "PhotoRecIngestModule.nonHostnameUNCPathUsed=PhotoRec cannot operate with a UNC path containing IP addresses."
80 })
81 final class PhotoRecCarverFileIngestModule implements FileIngestModule {
82 
83  static final boolean DEFAULT_CONFIG_KEEP_CORRUPTED_FILES = false;
84  static final PhotoRecCarverIngestJobSettings.ExtensionFilterOption DEFAULT_CONFIG_EXTENSION_FILTER
85  = PhotoRecCarverIngestJobSettings.ExtensionFilterOption.NO_FILTER;
86 
87  static final boolean DEFAULT_CONFIG_INCLUDE_ELSE_EXCLUDE = false;
88 
89  private static final String PHOTOREC_TEMP_SUBDIR = "PhotoRec Carver"; // NON-NLS Note that we need the space in this dir name (JIRA-6878)
90  private static final String PHOTOREC_DIRECTORY = "photorec_exec"; //NON-NLS
91  private static final String PHOTOREC_SUBDIRECTORY = "bin"; //NON-NLS
92  private static final String PHOTOREC_EXECUTABLE = "photorec_win.exe"; //NON-NLS
93  private static final String PHOTOREC_LINUX_EXECUTABLE = "photorec";
94  private static final String PHOTOREC_RESULTS_BASE = "results"; //NON-NLS
95  private static final String PHOTOREC_RESULTS_EXTENDED = "results.1"; //NON-NLS
96  private static final String PHOTOREC_REPORT = "report.xml"; //NON-NLS
97  private static final String LOG_FILE = "run_log.txt"; //NON-NLS
98  private static final String SEP = System.getProperty("line.separator");
99  private static final Logger logger = Logger.getLogger(PhotoRecCarverFileIngestModule.class.getName());
100  private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
101  private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
102  private static final Map<Long, WorkingPaths> pathsByJob = new ConcurrentHashMap<>();
103  private IngestJobContext context;
104  private Path rootOutputDirPath;
105  private Path rootTempDirPath;
106  private File executableFile;
107  private IngestServices services;
108  private final UNCPathUtilities uncPathUtilities = new UNCPathUtilities();
109  private final PhotoRecCarverIngestJobSettings settings;
110  private String optionsString;
111  private long jobId;
112 
113  private static class IngestJobTotals {
114 
115  private final AtomicLong totalItemsRecovered = new AtomicLong(0);
116  private final AtomicLong totalItemsWithErrors = new AtomicLong(0);
117  private final AtomicLong totalWritetime = new AtomicLong(0);
118  private final AtomicLong totalParsetime = new AtomicLong(0);
119  }
120 
126  PhotoRecCarverFileIngestModule(PhotoRecCarverIngestJobSettings settings) {
127  this.settings = settings;
128  }
129 
138  private String getPhotorecOptions(PhotoRecCarverIngestJobSettings settings) {
139  List<String> toRet = new ArrayList<String>();
140 
141  if (settings.isKeepCorruptedFiles()) {
142  toRet.addAll(Arrays.asList("options", "keep_corrupted_file"));
143  }
144 
145  if (settings.getExtensionFilterOption()
146  != PhotoRecCarverIngestJobSettings.ExtensionFilterOption.NO_FILTER) {
147 
148  // add the file opt menu item
149  toRet.add("fileopt");
150 
151  String enable = "enable";
152  String disable = "disable";
153 
154  // if we are including file extensions, then we are excluding
155  // everything else and vice-versa.
156  String everythingEnable = settings.getExtensionFilterOption()
157  == PhotoRecCarverIngestJobSettings.ExtensionFilterOption.INCLUDE
158  ? disable : enable;
159 
160  toRet.addAll(Arrays.asList("everything", everythingEnable));
161 
162  final String itemEnable = settings.getExtensionFilterOption()
163  == PhotoRecCarverIngestJobSettings.ExtensionFilterOption.INCLUDE
164  ? enable : disable;
165 
166  settings.getExtensions().forEach((extension) -> {
167  toRet.addAll(Arrays.asList(extension, itemEnable));
168  });
169  }
170 
171  toRet.add("search");
172  return String.join(",", toRet);
173  }
174 
175  private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) {
176  IngestJobTotals totals = totalsForIngestJobs.get(ingestJobId);
177  if (totals == null) {
178  totals = new PhotoRecCarverFileIngestModule.IngestJobTotals();
179  totalsForIngestJobs.put(ingestJobId, totals);
180  }
181  return totals;
182  }
183 
184  private static synchronized void initTotalsForIngestJob(long ingestJobId) {
185  IngestJobTotals totals = new PhotoRecCarverFileIngestModule.IngestJobTotals();
186  totalsForIngestJobs.put(ingestJobId, totals);
187  }
188 
192  @Override
193  @NbBundle.Messages({
194  "# {0} - extensions",
195  "PhotoRecCarverFileIngestModule_startUp_invalidExtensions_description=The following extensions are invalid: {0}",
196  "PhotoRecCarverFileIngestModule_startUp_noExtensionsProvided_description=No extensions provided for PhotoRec to carve."
197  })
198  public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
199  // validate settings
200  if (this.settings.getExtensionFilterOption() != PhotoRecCarverIngestJobSettings.ExtensionFilterOption.NO_FILTER) {
201  if (this.settings.getExtensions().isEmpty()
202  && this.settings.getExtensionFilterOption() == PhotoRecCarverIngestJobSettings.ExtensionFilterOption.INCLUDE) {
203 
204  throw new IngestModule.IngestModuleException(
205  Bundle.PhotoRecCarverFileIngestModule_startUp_noExtensionsProvided_description());
206  }
207 
208  List<String> invalidExtensions = this.settings.getExtensions().stream()
209  .filter((ext) -> !PhotoRecCarverFileOptExtensions.isValidExtension(ext))
210  .collect(Collectors.toList());
211 
212  if (!invalidExtensions.isEmpty()) {
213  throw new IngestModule.IngestModuleException(
214  Bundle.PhotoRecCarverFileIngestModule_startUp_invalidExtensions_description(
215  String.join(",", invalidExtensions)));
216  }
217  }
218 
219  this.optionsString = getPhotorecOptions(this.settings);
220 
221  this.context = context;
222  this.services = IngestServices.getInstance();
223  this.jobId = this.context.getJobId();
224 
225  // If the global unallocated space processing setting and the module
226  // process unallocated space only setting are not in sych, throw an
227  // exception. Although the result would not be incorrect, it would be
228  // unfortunate for the user to get an accidental no-op for this module.
229  if (!this.context.processingUnallocatedSpace()) {
230  throw new IngestModule.IngestModuleException(Bundle.unallocatedSpaceProcessingSettingsError_message());
231  }
232 
233  this.rootOutputDirPath = createModuleOutputDirectoryForCase();
234  this.rootTempDirPath = createTempOutputDirectoryForCase();
235 
236  //Set photorec executable directory based on operating system.
237  executableFile = locateExecutable();
238 
239  if (PhotoRecCarverFileIngestModule.refCounter.incrementAndGet(this.jobId) == 1) {
240  try {
241  // The first instance creates an output subdirectory with a date and time stamp
242  DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-SSSS"); // NON-NLS
243  Date date = new Date();
244  String folder = this.context.getDataSource().getId() + "_" + dateFormat.format(date);
245  Path outputDirPath = Paths.get(this.rootOutputDirPath.toAbsolutePath().toString(), folder);
246  Files.createDirectories(outputDirPath);
247 
248  // A temp subdirectory is also created as a location for writing unallocated space files to disk.
249  Path tempDirPath = Paths.get(this.rootTempDirPath.toString(), folder);
250  Files.createDirectory(tempDirPath);
251 
252  // Save the directories for the current job.
253  PhotoRecCarverFileIngestModule.pathsByJob.put(this.jobId, new WorkingPaths(outputDirPath, tempDirPath));
254 
255  // Initialize job totals
256  initTotalsForIngestJob(jobId);
257  } catch (SecurityException | IOException | UnsupportedOperationException ex) {
258  throw new IngestModule.IngestModuleException(Bundle.cannotCreateOutputDir_message(ex.getLocalizedMessage()), ex);
259  }
260  }
261  }
262 
266  @Override
267  public IngestModule.ProcessResult process(AbstractFile file) {
268  // Skip everything except unallocated space files.
269  if (file.getType() != TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
270  return IngestModule.ProcessResult.OK;
271  }
272 
273  // Safely get a reference to the totalsForIngestJobs object
274  IngestJobTotals totals = getTotalsForIngestJobs(jobId);
275 
276  Path tempFilePath = null;
277  try {
278  // Verify initialization succeeded.
279  if (null == this.executableFile) {
280  logger.log(Level.SEVERE, "PhotoRec carver called after failed start up"); // NON-NLS
281  return IngestModule.ProcessResult.ERROR;
282  }
283 
284  // Check that we have roughly enough disk space left to complete the operation
285  // Some network drives always return -1 for free disk space.
286  // In this case, expect enough space and move on.
287  long freeDiskSpace = IngestServices.getInstance().getFreeDiskSpace();
288  if ((freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN) && ((file.getSize() * 1.2) > freeDiskSpace)) {
289  logger.log(Level.SEVERE, "PhotoRec error processing {0} with {1} Not enough space on primary disk to save unallocated space.", // NON-NLS
290  new Object[]{file.getName(), PhotoRecCarverIngestModuleFactory.getModuleName()}); // NON-NLS
291  MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.UnableToCarve", file.getName()),
292  NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.NotEnoughDiskSpace"));
293  return IngestModule.ProcessResult.ERROR;
294  }
295  if (this.context.fileIngestIsCancelled() == true) {
296  // if it was cancelled by the user, result is OK
297  logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
298  MessageNotifyUtil.Notify.info(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.cancelledByUser"));
299  return IngestModule.ProcessResult.OK;
300  }
301 
302  // Write the file to disk.
303  long writestart = System.currentTimeMillis();
304  WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.get(this.jobId);
305  tempFilePath = Paths.get(paths.getTempDirPath().toString(), file.getName());
306  ContentUtils.writeToFile(file, tempFilePath.toFile(), context::fileIngestIsCancelled);
307 
308  if (this.context.fileIngestIsCancelled() == true) {
309  // if it was cancelled by the user, result is OK
310  logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
311  MessageNotifyUtil.Notify.info(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.cancelledByUser"));
312  return IngestModule.ProcessResult.OK;
313  }
314 
315  // Create a subdirectory for this file.
316  Path outputDirPath = Paths.get(paths.getOutputDirPath().toString(), file.getName());
317  Files.createDirectory(outputDirPath);
318  File log = new File(Paths.get(outputDirPath.toString(), LOG_FILE).toString()); //NON-NLS
319 
320  // Scan the file with Unallocated Carver.
321  ProcessBuilder processAndSettings = new ProcessBuilder(
322  executableFile.toString(),
323  "/d", // NON-NLS
324  outputDirPath.toAbsolutePath().toString() + File.separator + PHOTOREC_RESULTS_BASE,
325  "/cmd", // NON-NLS
326  tempFilePath.toFile().toString());
327 
328  processAndSettings.command().add(this.optionsString);
329 
330  // Add environment variable to force PhotoRec to run with the same permissions Autopsy uses
331  processAndSettings.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
332  processAndSettings.redirectErrorStream(true);
333  processAndSettings.redirectOutput(Redirect.appendTo(log));
334 
335  FileIngestModuleProcessTerminator terminator = new FileIngestModuleProcessTerminator(this.context, true);
336  int exitValue = ExecUtil.execute(processAndSettings, terminator);
337 
338  if (this.context.fileIngestIsCancelled() == true) {
339  // if it was cancelled by the user, result is OK
340  cleanup(outputDirPath, tempFilePath);
341  logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
342  MessageNotifyUtil.Notify.info(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.cancelledByUser"));
343  return IngestModule.ProcessResult.OK;
344  } else if (terminator.getTerminationCode() == ProcTerminationCode.TIME_OUT) {
345  cleanup(outputDirPath, tempFilePath);
346  String msg = NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.processTerminated") + file.getName(); // NON-NLS
347  MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.moduleError"), msg); // NON-NLS
348  logger.log(Level.SEVERE, msg);
349  return IngestModule.ProcessResult.ERROR;
350  } else if (0 != exitValue) {
351  // if it failed or was cancelled by timeout, result is ERROR
352  cleanup(outputDirPath, tempFilePath);
353  totals.totalItemsWithErrors.incrementAndGet();
354  logger.log(Level.SEVERE, "PhotoRec carver returned error exit value = {0} when scanning {1}", // NON-NLS
355  new Object[]{exitValue, file.getName()}); // NON-NLS
356  MessageNotifyUtil.Notify.error(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.error.exitValue", // NON-NLS
357  new Object[]{exitValue, file.getName()}));
358  return IngestModule.ProcessResult.ERROR;
359  }
360 
361  // Move carver log file to avoid placement into Autopsy results. PhotoRec appends ".1" to the folder name.
362  java.io.File oldAuditFile = new java.io.File(Paths.get(outputDirPath.toString(), PHOTOREC_RESULTS_EXTENDED, PHOTOREC_REPORT).toString()); //NON-NLS
363  java.io.File newAuditFile = new java.io.File(Paths.get(outputDirPath.toString(), PHOTOREC_REPORT).toString()); //NON-NLS
364  oldAuditFile.renameTo(newAuditFile);
365 
366  if (this.context.fileIngestIsCancelled() == true) {
367  // if it was cancelled by the user, result is OK
368  logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
369  MessageNotifyUtil.Notify.info(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.cancelledByUser"));
370  return IngestModule.ProcessResult.OK;
371  }
372  Path pathToRemove = Paths.get(outputDirPath.toAbsolutePath().toString());
373  try (DirectoryStream<Path> stream = Files.newDirectoryStream(pathToRemove)) {
374  for (Path entry : stream) {
375  if (Files.isDirectory(entry)) {
376  FileUtil.deleteDir(new File(entry.toString()));
377  }
378  }
379  }
380  long writedelta = (System.currentTimeMillis() - writestart);
381  totals.totalWritetime.addAndGet(writedelta);
382 
383  // Now that we've cleaned up the folders and data files, parse the xml output file to add carved items into the database
384  long calcstart = System.currentTimeMillis();
385  PhotoRecCarverOutputParser parser = new PhotoRecCarverOutputParser(outputDirPath);
386  if (this.context.fileIngestIsCancelled() == true) {
387  // if it was cancelled by the user, result is OK
388  logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
389  MessageNotifyUtil.Notify.info(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.cancelledByUser"));
390  return IngestModule.ProcessResult.OK;
391  }
392  List<LayoutFile> carvedItems = parser.parse(newAuditFile, file, context);
393  long calcdelta = (System.currentTimeMillis() - calcstart);
394  totals.totalParsetime.addAndGet(calcdelta);
395  if (carvedItems != null && !carvedItems.isEmpty()) { // if there were any results from carving, add the unallocated carving event to the reports list.
396  totals.totalItemsRecovered.addAndGet(carvedItems.size());
397  context.addFilesToJob(new ArrayList<>(carvedItems));
398  services.fireModuleContentEvent(new ModuleContentEvent(carvedItems.get(0))); // fire an event to update the tree
399  }
400  } catch (ReadContentInputStreamException ex) {
401  totals.totalItemsWithErrors.incrementAndGet();
402  logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d) with the PhotoRec carver.", file.getName(), file.getId()), ex); // NON-NLS
403  MessageNotifyUtil.Notify.error(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.error.msg", file.getName()));
404  return IngestModule.ProcessResult.ERROR;
405  } catch (IOException ex) {
406  totals.totalItemsWithErrors.incrementAndGet();
407  logger.log(Level.SEVERE, String.format("Error writing or processing file '%s' (id=%d) to '%s' with the PhotoRec carver.", file.getName(), file.getId(), tempFilePath), ex); // NON-NLS
408  MessageNotifyUtil.Notify.error(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.error.msg", file.getName()));
409  return IngestModule.ProcessResult.ERROR;
410  } finally {
411  if (null != tempFilePath && Files.exists(tempFilePath)) {
412  // Get rid of the unallocated space file.
413  tempFilePath.toFile().delete();
414  }
415  }
416  return IngestModule.ProcessResult.OK;
417 
418  }
419 
420  private void cleanup(Path outputDirPath, Path tempFilePath) {
421  // cleanup the output path
422  FileUtil.deleteDir(new File(outputDirPath.toString()));
423  if (null != tempFilePath && Files.exists(tempFilePath)) {
424  tempFilePath.toFile().delete();
425  }
426  }
427 
428  private static synchronized void postSummary(long jobId) {
429  IngestJobTotals jobTotals = totalsForIngestJobs.remove(jobId);
430 
431  StringBuilder detailsSb = new StringBuilder();
432  //details
433  detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
434 
435  detailsSb.append("<tr><td>") //NON-NLS
436  .append(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.complete.numberOfCarved"))
437  .append("</td>"); //NON-NLS
438  detailsSb.append("<td>").append(jobTotals.totalItemsRecovered.get()).append("</td></tr>"); //NON-NLS
439 
440  detailsSb.append("<tr><td>") //NON-NLS
441  .append(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.complete.numberOfErrors"))
442  .append("</td>"); //NON-NLS
443  detailsSb.append("<td>").append(jobTotals.totalItemsWithErrors.get()).append("</td></tr>"); //NON-NLS
444 
445  detailsSb.append("<tr><td>") //NON-NLS
446  .append(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.complete.totalWritetime"))
447  .append("</td><td>").append(jobTotals.totalWritetime.get()).append("</td></tr>\n"); //NON-NLS
448  detailsSb.append("<tr><td>") //NON-NLS
449  .append(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.complete.totalParsetime"))
450  .append("</td><td>").append(jobTotals.totalParsetime.get()).append("</td></tr>\n"); //NON-NLS
451  detailsSb.append("</table>"); //NON-NLS
452 
453  IngestServices.getInstance().postMessage(IngestMessage.createMessage(
454  IngestMessage.MessageType.INFO,
455  PhotoRecCarverIngestModuleFactory.getModuleName(),
456  NbBundle.getMessage(PhotoRecCarverFileIngestModule.class,
457  "PhotoRecIngestModule.complete.photoRecResults"),
458  detailsSb.toString()));
459 
460  }
461 
465  @Override
466  public void shutDown() {
467  if (this.context != null && refCounter.decrementAndGet(this.jobId) == 0) {
468  try {
469  // The last instance of this module for an ingest job cleans out
470  // the working paths map entry for the job and deletes the temp dir.
471  WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.remove(this.jobId);
472  FileUtil.deleteDir(new File(paths.getTempDirPath().toString()));
473  postSummary(jobId);
474  } catch (SecurityException ex) {
475  logger.log(Level.SEVERE, "Error shutting down PhotoRec carver module", ex); // NON-NLS
476  }
477  }
478  }
479 
480  private static final class WorkingPaths {
481 
482  private final Path outputDirPath;
483  private final Path tempDirPath;
484 
485  WorkingPaths(Path outputDirPath, Path tempDirPath) {
486  this.outputDirPath = outputDirPath;
487  this.tempDirPath = tempDirPath;
488  }
489 
490  Path getOutputDirPath() {
491  return this.outputDirPath;
492  }
493 
494  Path getTempDirPath() {
495  return this.tempDirPath;
496  }
497  }
498 
507  synchronized Path createTempOutputDirectoryForCase() throws IngestModule.IngestModuleException {
508  try {
509  Path path = Paths.get(Case.getCurrentCaseThrows().getTempDirectory(), PHOTOREC_TEMP_SUBDIR);
510  return createOutputDirectoryForCase(path);
511  } catch (NoCurrentCaseException ex) {
512  throw new IngestModule.IngestModuleException(Bundle.cannotCreateOutputDir_message(ex.getLocalizedMessage()), ex);
513  }
514  }
515 
524  synchronized Path createModuleOutputDirectoryForCase() throws IngestModule.IngestModuleException {
525  try {
526  Path path = Paths.get(Case.getCurrentCaseThrows().getModuleDirectory(), PhotoRecCarverIngestModuleFactory.getModuleName());
527  return createOutputDirectoryForCase(path);
528  } catch (NoCurrentCaseException ex) {
529  throw new IngestModule.IngestModuleException(Bundle.cannotCreateOutputDir_message(ex.getLocalizedMessage()), ex);
530  }
531  }
532 
543  private synchronized Path createOutputDirectoryForCase(Path providedPath) throws IngestModule.IngestModuleException {
544  Path path = providedPath;
545  try {
546  Files.createDirectory(path);
547  if (UNCPathUtilities.isUNC(path)) {
548  // if the UNC path is using an IP address, convert to hostname
549  path = uncPathUtilities.ipToHostName(path);
550  if (path == null) {
551  throw new IngestModule.IngestModuleException(Bundle.PhotoRecIngestModule_nonHostnameUNCPathUsed());
552  }
553  if (false == FileUtil.hasReadWriteAccess(path)) {
554  throw new IngestModule.IngestModuleException(
555  Bundle.PhotoRecIngestModule_PermissionsNotSufficient() + SEP + path.toString() + SEP
556  + Bundle.PhotoRecIngestModule_PermissionsNotSufficientSeeReference()
557  );
558  }
559  }
560  } catch (FileAlreadyExistsException ex) {
561  // No worries.
562  } catch (IOException | SecurityException | UnsupportedOperationException ex) {
563  throw new IngestModule.IngestModuleException(Bundle.cannotCreateOutputDir_message(ex.getLocalizedMessage()), ex);
564  }
565  return path;
566  }
567 
577  public static File locateExecutable() throws IngestModule.IngestModuleException {
578  File exeFile;
579  Path execName;
580  String photorec_linux_directory = "/usr/bin";
581  if (PlatformUtil.isWindowsOS()) {
582  execName = Paths.get(PHOTOREC_DIRECTORY, PHOTOREC_SUBDIRECTORY, PHOTOREC_EXECUTABLE);
583  exeFile = InstalledFileLocator.getDefault().locate(execName.toString(), PhotoRecCarverFileIngestModule.class.getPackage().getName(), false);
584  } else {
585  File usrBin = new File("/usr/bin/photorec");
586  File usrLocalBin = new File("/usr/local/bin/photorec");
587  if (usrBin.canExecute() && usrBin.exists() && !usrBin.isDirectory()) {
588  photorec_linux_directory = "/usr/bin";
589  } else if (usrLocalBin.canExecute() && usrLocalBin.exists() && !usrLocalBin.isDirectory()) {
590  photorec_linux_directory = "/usr/local/bin";
591  } else {
592  throw new IngestModule.IngestModuleException("Photorec not found");
593  }
594  execName = Paths.get(photorec_linux_directory, PHOTOREC_LINUX_EXECUTABLE);
595  exeFile = new File(execName.toString());
596  }
597 
598  if (null == exeFile) {
599  throw new IngestModule.IngestModuleException(Bundle.missingExecutable_message());
600  }
601 
602  if (!exeFile.canExecute()) {
603  throw new IngestModule.IngestModuleException(Bundle.cannotRunExecutable_message());
604  }
605 
606  return exeFile;
607  }
608 
609 }
void fireModuleContentEvent(ModuleContentEvent moduleContentEvent)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
synchronized Path ipToHostName(Path inputPath)
static synchronized IngestServices getInstance()

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