Autopsy  4.15.0
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.Date;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.concurrent.ConcurrentHashMap;
37 import java.util.concurrent.atomic.AtomicLong;
38 import java.util.logging.Level;
39 import org.openide.modules.InstalledFileLocator;
40 import org.openide.util.NbBundle;
60 import org.sleuthkit.datamodel.AbstractFile;
61 import org.sleuthkit.datamodel.LayoutFile;
62 import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
63 import org.sleuthkit.datamodel.TskData;
64 
69 @NbBundle.Messages({
70  "PhotoRecIngestModule.PermissionsNotSufficient=Insufficient permissions accessing",
71  "PhotoRecIngestModule.PermissionsNotSufficientSeeReference=See 'Shared Drive Authentication' in Autopsy help.",
72  "# {0} - output directory name", "cannotCreateOutputDir.message=Unable to create output directory: {0}.",
73  "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.",
74  "unsupportedOS.message=PhotoRec module is supported on Windows platforms only.",
75  "missingExecutable.message=Unable to locate PhotoRec executable.",
76  "cannotRunExecutable.message=Unable to execute PhotoRec.",
77  "PhotoRecIngestModule.nonHostnameUNCPathUsed=PhotoRec cannot operate with a UNC path containing IP addresses."
78 })
79 final class PhotoRecCarverFileIngestModule implements FileIngestModule {
80 
81  static final boolean DEFAULT_CONFIG_KEEP_CORRUPTED_FILES = false;
82 
83  private static final String PHOTOREC_DIRECTORY = "photorec_exec"; //NON-NLS
84  private static final String PHOTOREC_SUBDIRECTORY = "bin"; //NON-NLS
85  private static final String PHOTOREC_EXECUTABLE = "photorec_win.exe"; //NON-NLS
86  private static final String PHOTOREC_LINUX_EXECUTABLE = "photorec";
87  private static final String PHOTOREC_RESULTS_BASE = "results"; //NON-NLS
88  private static final String PHOTOREC_RESULTS_EXTENDED = "results.1"; //NON-NLS
89  private static final String PHOTOREC_REPORT = "report.xml"; //NON-NLS
90  private static final String LOG_FILE = "run_log.txt"; //NON-NLS
91  private static final String TEMP_DIR_NAME = "temp"; // NON-NLS
92  private static final String SEP = System.getProperty("line.separator");
93  private static final Logger logger = Logger.getLogger(PhotoRecCarverFileIngestModule.class.getName());
94  private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
95  private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
96  private static final Map<Long, WorkingPaths> pathsByJob = new ConcurrentHashMap<>();
97  private IngestJobContext context;
98  private Path rootOutputDirPath;
99  private File executableFile;
100  private IngestServices services;
101  private final UNCPathUtilities uncPathUtilities = new UNCPathUtilities();
102  private long jobId;
103 
104  private final boolean keepCorruptedFiles;
105 
106  private static class IngestJobTotals {
107  private final AtomicLong totalItemsRecovered = new AtomicLong(0);
108  private final AtomicLong totalItemsWithErrors = new AtomicLong(0);
109  private final AtomicLong totalWritetime = new AtomicLong(0);
110  private final AtomicLong totalParsetime = new AtomicLong(0);
111  }
117  PhotoRecCarverFileIngestModule(PhotoRecCarverIngestJobSettings settings) {
118  keepCorruptedFiles = settings.isKeepCorruptedFiles();
119  }
120 
121  private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) {
122  IngestJobTotals totals = totalsForIngestJobs.get(ingestJobId);
123  if (totals == null) {
124  totals = new PhotoRecCarverFileIngestModule.IngestJobTotals();
125  totalsForIngestJobs.put(ingestJobId, totals);
126  }
127  return totals;
128  }
129 
130  private static synchronized void initTotalsForIngestJob(long ingestJobId) {
131  IngestJobTotals totals = new PhotoRecCarverFileIngestModule.IngestJobTotals();
132  totalsForIngestJobs.put(ingestJobId, totals);
133  }
134 
138  @Override
139  public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
140  this.context = context;
141  this.services = IngestServices.getInstance();
142  this.jobId = this.context.getJobId();
143 
144  // If the global unallocated space processing setting and the module
145  // process unallocated space only setting are not in sych, throw an
146  // exception. Although the result would not be incorrect, it would be
147  // unfortunate for the user to get an accidental no-op for this module.
148  if (!this.context.processingUnallocatedSpace()) {
149  throw new IngestModule.IngestModuleException(Bundle.unallocatedSpaceProcessingSettingsError_message());
150  }
151 
152  this.rootOutputDirPath = createModuleOutputDirectoryForCase();
153 
154  //Set photorec executable directory based on operating system.
155  executableFile = locateExecutable();
156 
157  if (PhotoRecCarverFileIngestModule.refCounter.incrementAndGet(this.jobId) == 1) {
158  try {
159  // The first instance creates an output subdirectory with a date and time stamp
160  DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-SSSS"); // NON-NLS
161  Date date = new Date();
162  String folder = this.context.getDataSource().getId() + "_" + dateFormat.format(date);
163  Path outputDirPath = Paths.get(this.rootOutputDirPath.toAbsolutePath().toString(), folder);
164  Files.createDirectories(outputDirPath);
165 
166  // A temp subdirectory is also created as a location for writing unallocated space files to disk.
167  Path tempDirPath = Paths.get(outputDirPath.toString(), PhotoRecCarverFileIngestModule.TEMP_DIR_NAME);
168  Files.createDirectory(tempDirPath);
169 
170  // Save the directories for the current job.
171  PhotoRecCarverFileIngestModule.pathsByJob.put(this.jobId, new WorkingPaths(outputDirPath, tempDirPath));
172 
173  // Initialize job totals
174  initTotalsForIngestJob(jobId);
175  } catch (SecurityException | IOException | UnsupportedOperationException ex) {
176  throw new IngestModule.IngestModuleException(Bundle.cannotCreateOutputDir_message(ex.getLocalizedMessage()), ex);
177  }
178  }
179  }
180 
184  @Override
185  public IngestModule.ProcessResult process(AbstractFile file) {
186  // Skip everything except unallocated space files.
187  if (file.getType() != TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
188  return IngestModule.ProcessResult.OK;
189  }
190 
191  // Safely get a reference to the totalsForIngestJobs object
192  IngestJobTotals totals = getTotalsForIngestJobs(jobId);
193 
194  Path tempFilePath = null;
195  try {
196  // Verify initialization succeeded.
197  if (null == this.executableFile) {
198  logger.log(Level.SEVERE, "PhotoRec carver called after failed start up"); // NON-NLS
199  return IngestModule.ProcessResult.ERROR;
200  }
201 
202  // Check that we have roughly enough disk space left to complete the operation
203  // Some network drives always return -1 for free disk space.
204  // In this case, expect enough space and move on.
205  long freeDiskSpace = IngestServices.getInstance().getFreeDiskSpace();
206  if ((freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN) && ((file.getSize() * 1.2) > freeDiskSpace)) {
207  logger.log(Level.SEVERE, "PhotoRec error processing {0} with {1} Not enough space on primary disk to save unallocated space.", // NON-NLS
208  new Object[]{file.getName(), PhotoRecCarverIngestModuleFactory.getModuleName()}); // NON-NLS
209  MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.UnableToCarve", file.getName()),
210  NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.NotEnoughDiskSpace"));
211  return IngestModule.ProcessResult.ERROR;
212  }
213  if (this.context.fileIngestIsCancelled() == true) {
214  // if it was cancelled by the user, result is OK
215  logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
216  MessageNotifyUtil.Notify.info(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.cancelledByUser"));
217  return IngestModule.ProcessResult.OK;
218  }
219 
220  // Write the file to disk.
221  long writestart = System.currentTimeMillis();
222  WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.get(this.jobId);
223  tempFilePath = Paths.get(paths.getTempDirPath().toString(), file.getName());
224  ContentUtils.writeToFile(file, tempFilePath.toFile(), context::fileIngestIsCancelled);
225 
226  if (this.context.fileIngestIsCancelled() == true) {
227  // if it was cancelled by the user, result is OK
228  logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
229  MessageNotifyUtil.Notify.info(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.cancelledByUser"));
230  return IngestModule.ProcessResult.OK;
231  }
232 
233  // Create a subdirectory for this file.
234  Path outputDirPath = Paths.get(paths.getOutputDirPath().toString(), file.getName());
235  Files.createDirectory(outputDirPath);
236  File log = new File(Paths.get(outputDirPath.toString(), LOG_FILE).toString()); //NON-NLS
237 
238  // Scan the file with Unallocated Carver.
239  ProcessBuilder processAndSettings = new ProcessBuilder(
240  executableFile.toString(),
241  "/d", // NON-NLS
242  outputDirPath.toAbsolutePath().toString() + File.separator + PHOTOREC_RESULTS_BASE,
243  "/cmd", // NON-NLS
244  tempFilePath.toFile().toString());
245  if (keepCorruptedFiles) {
246  processAndSettings.command().add("options,keep_corrupted_file,search"); // NON-NLS
247  } else {
248  processAndSettings.command().add("search"); // NON-NLS
249  }
250 
251  // Add environment variable to force PhotoRec to run with the same permissions Autopsy uses
252  processAndSettings.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
253  processAndSettings.redirectErrorStream(true);
254  processAndSettings.redirectOutput(Redirect.appendTo(log));
255 
256  FileIngestModuleProcessTerminator terminator = new FileIngestModuleProcessTerminator(this.context, true);
257  int exitValue = ExecUtil.execute(processAndSettings, terminator);
258 
259  if (this.context.fileIngestIsCancelled() == true) {
260  // if it was cancelled by the user, result is OK
261  cleanup(outputDirPath, tempFilePath);
262  logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
263  MessageNotifyUtil.Notify.info(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.cancelledByUser"));
264  return IngestModule.ProcessResult.OK;
265  } else if (terminator.getTerminationCode() == ProcTerminationCode.TIME_OUT) {
266  cleanup(outputDirPath, tempFilePath);
267  String msg = NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.processTerminated") + file.getName(); // NON-NLS
268  MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.moduleError"), msg); // NON-NLS
269  logger.log(Level.SEVERE, msg);
270  return IngestModule.ProcessResult.ERROR;
271  } else if (0 != exitValue) {
272  // if it failed or was cancelled by timeout, result is ERROR
273  cleanup(outputDirPath, tempFilePath);
274  totals.totalItemsWithErrors.incrementAndGet();
275  logger.log(Level.SEVERE, "PhotoRec carver returned error exit value = {0} when scanning {1}", // NON-NLS
276  new Object[]{exitValue, file.getName()}); // NON-NLS
277  MessageNotifyUtil.Notify.error(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.error.exitValue", // NON-NLS
278  new Object[]{exitValue, file.getName()}));
279  return IngestModule.ProcessResult.ERROR;
280  }
281 
282  // Move carver log file to avoid placement into Autopsy results. PhotoRec appends ".1" to the folder name.
283  java.io.File oldAuditFile = new java.io.File(Paths.get(outputDirPath.toString(), PHOTOREC_RESULTS_EXTENDED, PHOTOREC_REPORT).toString()); //NON-NLS
284  java.io.File newAuditFile = new java.io.File(Paths.get(outputDirPath.toString(), PHOTOREC_REPORT).toString()); //NON-NLS
285  oldAuditFile.renameTo(newAuditFile);
286 
287  if (this.context.fileIngestIsCancelled() == true) {
288  // if it was cancelled by the user, result is OK
289  logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
290  MessageNotifyUtil.Notify.info(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.cancelledByUser"));
291  return IngestModule.ProcessResult.OK;
292  }
293  Path pathToRemove = Paths.get(outputDirPath.toAbsolutePath().toString());
294  try (DirectoryStream<Path> stream = Files.newDirectoryStream(pathToRemove)) {
295  for (Path entry : stream) {
296  if (Files.isDirectory(entry)) {
297  FileUtil.deleteDir(new File(entry.toString()));
298  }
299  }
300  }
301  long writedelta = (System.currentTimeMillis() - writestart);
302  totals.totalWritetime.addAndGet(writedelta);
303 
304  // Now that we've cleaned up the folders and data files, parse the xml output file to add carved items into the database
305  long calcstart = System.currentTimeMillis();
306  PhotoRecCarverOutputParser parser = new PhotoRecCarverOutputParser(outputDirPath);
307  if (this.context.fileIngestIsCancelled() == true) {
308  // if it was cancelled by the user, result is OK
309  logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
310  MessageNotifyUtil.Notify.info(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.cancelledByUser"));
311  return IngestModule.ProcessResult.OK;
312  }
313  List<LayoutFile> carvedItems = parser.parse(newAuditFile, file, context);
314  long calcdelta = (System.currentTimeMillis() - calcstart);
315  totals.totalParsetime.addAndGet(calcdelta);
316  if (carvedItems != null && !carvedItems.isEmpty()) { // if there were any results from carving, add the unallocated carving event to the reports list.
317  totals.totalItemsRecovered.addAndGet(carvedItems.size());
318  context.addFilesToJob(new ArrayList<>(carvedItems));
319  services.fireModuleContentEvent(new ModuleContentEvent(carvedItems.get(0))); // fire an event to update the tree
320  }
321  } catch (ReadContentInputStreamException ex) {
322  totals.totalItemsWithErrors.incrementAndGet();
323  logger.log(Level.WARNING, String.format("Error reading file '%s' (id=%d) with the PhotoRec carver.", file.getName(), file.getId()), ex); // NON-NLS
324  MessageNotifyUtil.Notify.error(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.error.msg", file.getName()));
325  return IngestModule.ProcessResult.ERROR;
326  } catch (IOException ex) {
327  totals.totalItemsWithErrors.incrementAndGet();
328  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
329  MessageNotifyUtil.Notify.error(PhotoRecCarverIngestModuleFactory.getModuleName(), NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.error.msg", file.getName()));
330  return IngestModule.ProcessResult.ERROR;
331  } finally {
332  if (null != tempFilePath && Files.exists(tempFilePath)) {
333  // Get rid of the unallocated space file.
334  tempFilePath.toFile().delete();
335  }
336  }
337  return IngestModule.ProcessResult.OK;
338 
339  }
340 
341  private void cleanup(Path outputDirPath, Path tempFilePath) {
342  // cleanup the output path
343  FileUtil.deleteDir(new File(outputDirPath.toString()));
344  if (null != tempFilePath && Files.exists(tempFilePath)) {
345  tempFilePath.toFile().delete();
346  }
347  }
348 
349  private static synchronized void postSummary(long jobId) {
350  IngestJobTotals jobTotals = totalsForIngestJobs.remove(jobId);
351 
352  StringBuilder detailsSb = new StringBuilder();
353  //details
354  detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
355 
356  detailsSb.append("<tr><td>") //NON-NLS
357  .append(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.complete.numberOfCarved"))
358  .append("</td>"); //NON-NLS
359  detailsSb.append("<td>").append(jobTotals.totalItemsRecovered.get()).append("</td></tr>"); //NON-NLS
360 
361  detailsSb.append("<tr><td>") //NON-NLS
362  .append(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.complete.numberOfErrors"))
363  .append("</td>"); //NON-NLS
364  detailsSb.append("<td>").append(jobTotals.totalItemsWithErrors.get()).append("</td></tr>"); //NON-NLS
365 
366  detailsSb.append("<tr><td>") //NON-NLS
367  .append(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.complete.totalWritetime"))
368  .append("</td><td>").append(jobTotals.totalWritetime.get()).append("</td></tr>\n"); //NON-NLS
369  detailsSb.append("<tr><td>") //NON-NLS
370  .append(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.complete.totalParsetime"))
371  .append("</td><td>").append(jobTotals.totalParsetime.get()).append("</td></tr>\n"); //NON-NLS
372  detailsSb.append("</table>"); //NON-NLS
373 
374  IngestServices.getInstance().postMessage(IngestMessage.createMessage(
375  IngestMessage.MessageType.INFO,
376  PhotoRecCarverIngestModuleFactory.getModuleName(),
377  NbBundle.getMessage(PhotoRecCarverFileIngestModule.class,
378  "PhotoRecIngestModule.complete.photoRecResults"),
379  detailsSb.toString()));
380 
381  }
382 
386  @Override
387  public void shutDown() {
388  if (this.context != null && refCounter.decrementAndGet(this.jobId) == 0) {
389  try {
390  // The last instance of this module for an ingest job cleans out
391  // the working paths map entry for the job and deletes the temp dir.
392  WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.remove(this.jobId);
393  FileUtil.deleteDir(new File(paths.getTempDirPath().toString()));
394  postSummary(jobId);
395  } catch (SecurityException ex) {
396  logger.log(Level.SEVERE, "Error shutting down PhotoRec carver module", ex); // NON-NLS
397  }
398  }
399  }
400 
401  private static final class WorkingPaths {
402 
403  private final Path outputDirPath;
404  private final Path tempDirPath;
405 
406  WorkingPaths(Path outputDirPath, Path tempDirPath) {
407  this.outputDirPath = outputDirPath;
408  this.tempDirPath = tempDirPath;
409  }
410 
411  Path getOutputDirPath() {
412  return this.outputDirPath;
413  }
414 
415  Path getTempDirPath() {
416  return this.tempDirPath;
417  }
418  }
419 
428  synchronized Path createModuleOutputDirectoryForCase() throws IngestModule.IngestModuleException {
429  Path path;
430  try {
431  path = Paths.get(Case.getCurrentCaseThrows().getModuleDirectory(), PhotoRecCarverIngestModuleFactory.getModuleName());
432  } catch (NoCurrentCaseException ex) {
433  throw new IngestModule.IngestModuleException(Bundle.cannotCreateOutputDir_message(ex.getLocalizedMessage()), ex);
434  }
435  try {
436  Files.createDirectory(path);
437  if (UNCPathUtilities.isUNC(path)) {
438  // if the UNC path is using an IP address, convert to hostname
439  path = uncPathUtilities.ipToHostName(path);
440  if (path == null) {
441  throw new IngestModule.IngestModuleException(Bundle.PhotoRecIngestModule_nonHostnameUNCPathUsed());
442  }
443  if (false == FileUtil.hasReadWriteAccess(path)) {
444  throw new IngestModule.IngestModuleException(
445  Bundle.PhotoRecIngestModule_PermissionsNotSufficient() + SEP + path.toString() + SEP
446  + Bundle.PhotoRecIngestModule_PermissionsNotSufficientSeeReference()
447  );
448  }
449  }
450  } catch (FileAlreadyExistsException ex) {
451  // No worries.
452  } catch (IOException | SecurityException | UnsupportedOperationException ex) {
453  throw new IngestModule.IngestModuleException(Bundle.cannotCreateOutputDir_message(ex.getLocalizedMessage()), ex);
454  }
455  return path;
456  }
457 
458 
468  public static File locateExecutable() throws IngestModule.IngestModuleException {
469  File exeFile;
470  Path execName;
471  String photorec_linux_directory = "/usr/bin";
472  if (PlatformUtil.isWindowsOS()) {
473  execName = Paths.get(PHOTOREC_DIRECTORY, PHOTOREC_SUBDIRECTORY, PHOTOREC_EXECUTABLE);
474  exeFile = InstalledFileLocator.getDefault().locate(execName.toString(), PhotoRecCarverFileIngestModule.class.getPackage().getName(), false);
475  } else {
476  File usrBin = new File("/usr/bin/photorec");
477  File usrLocalBin = new File("/usr/local/bin/photorec");
478  if (usrBin.canExecute() && usrBin.exists() && !usrBin.isDirectory()) {
479  photorec_linux_directory = "/usr/bin";
480  }else if(usrLocalBin.canExecute() && usrLocalBin.exists() && !usrLocalBin.isDirectory()){
481  photorec_linux_directory = "/usr/local/bin";
482  }else{
483  throw new IngestModule.IngestModuleException("Photorec not found");
484  }
485  execName = Paths.get(photorec_linux_directory, PHOTOREC_LINUX_EXECUTABLE);
486  exeFile = new File(execName.toString());
487  }
488 
489  if (null == exeFile) {
490  throw new IngestModule.IngestModuleException(Bundle.missingExecutable_message());
491  }
492 
493 
494  if (!exeFile.canExecute()) {
495  throw new IngestModule.IngestModuleException(Bundle.cannotRunExecutable_message());
496  }
497 
498  return exeFile;
499  }
500 
501 }
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-2020 Basis Technology. Generated on: Mon Jul 6 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.