19 package org.sleuthkit.autopsy.modules.photoreccarver;
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.List;
35 import java.util.concurrent.ConcurrentHashMap;
36 import java.util.logging.Level;
37 import org.openide.modules.InstalledFileLocator;
38 import org.openide.util.NbBundle;
66 private static final String PHOTOREC_DIRECTORY =
"photorec_exec";
67 private static final String PHOTOREC_EXECUTABLE =
"photorec_win.exe";
68 private static final String PHOTOREC_RESULTS_BASE =
"results";
69 private static final String PHOTOREC_RESULTS_EXTENDED =
"results.1";
70 private static final String PHOTOREC_REPORT =
"report.xml";
71 private static final String LOG_FILE =
"run_log.txt";
72 private static final String TEMP_DIR_NAME =
"temp";
73 private static final Logger logger =
Logger.
getLogger(PhotoRecCarverFileIngestModule.class.getName());
75 private static final Map<Long, WorkingPaths> pathsByJob =
new ConcurrentHashMap<>();
77 private Path rootOutputDirPath;
78 private File executableFile;
86 this.context = context;
97 this.rootOutputDirPath = PhotoRecCarverFileIngestModule.createModuleOutputDirectoryForCase();
99 Path execName = Paths.get(PHOTOREC_DIRECTORY, PHOTOREC_EXECUTABLE);
100 executableFile = locateExecutable(execName.toString());
102 if (PhotoRecCarverFileIngestModule.refCounter.incrementAndGet(
this.context.getJobId()) == 1) {
105 DateFormat dateFormat =
new SimpleDateFormat(
"MM-dd-yyyy-HH-mm-ss-SSSS");
106 Date date =
new Date();
108 Path outputDirPath = Paths.get(this.rootOutputDirPath.toAbsolutePath().toString(), folder);
109 Files.createDirectories(outputDirPath);
112 Path tempDirPath = Paths.get(outputDirPath.toString(), PhotoRecCarverFileIngestModule.TEMP_DIR_NAME);
113 Files.createDirectory(tempDirPath);
116 PhotoRecCarverFileIngestModule.pathsByJob.put(this.context.
getJobId(),
new WorkingPaths(outputDirPath, tempDirPath));
118 catch (SecurityException | IOException | UnsupportedOperationException ex) {
134 Path tempFilePath = null;
136 long id = getRootId(file);
139 return ProcessResult.ERROR;
143 if (null == this.executableFile) {
144 logger.log(Level.SEVERE,
"PhotoRec carver called after failed start up");
153 if ((freeDiskSpace != -1) && ((file.
getSize() * 1.2) > freeDiskSpace)) {
154 logger.log(Level.SEVERE,
"PhotoRec error processing {0} with {1} Not enough space on primary disk to save unallocated space.",
155 new Object[]{file.getName(), PhotoRecCarverIngestModuleFactory.getModuleName()});
157 NbBundle.getMessage(
this.getClass(),
"PhotoRecIngestModule.NotEnoughDiskSpace"));
163 WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.get(this.context.
getJobId());
164 tempFilePath = Paths.get(paths.getTempDirPath().toString(), file.
getName());
168 Path outputDirPath = Paths.get(paths.getOutputDirPath().toString(), file.
getName());
169 Files.createDirectory(outputDirPath);
170 File log =
new File(Paths.get(outputDirPath.toString(), LOG_FILE).toString());
173 ProcessBuilder processAndSettings =
new ProcessBuilder(
174 "\"" + executableFile +
"\"",
176 "\"" + outputDirPath.toAbsolutePath() + File.separator + PHOTOREC_RESULTS_BASE +
"\"",
178 "\"" + tempFilePath.toFile() +
"\"",
182 processAndSettings.environment().put(
"__COMPAT_LAYER",
"RunAsInvoker");
183 processAndSettings.redirectErrorStream(
true);
184 processAndSettings.redirectOutput(Redirect.appendTo(log));
190 cleanup(outputDirPath, tempFilePath);
191 logger.log(Level.INFO,
"PhotoRec cancelled by user");
193 }
else if (0 != exitValue) {
195 cleanup(outputDirPath, tempFilePath);
196 logger.log(Level.SEVERE,
"PhotoRec carver returned error exit value = {0} when scanning {1}",
197 new Object[]{exitValue, file.getName()});
202 java.io.File oldAuditFile =
new java.io.File(Paths.get(outputDirPath.toString(), PHOTOREC_RESULTS_EXTENDED, PHOTOREC_REPORT).toString());
203 java.io.File newAuditFile =
new java.io.File(Paths.get(outputDirPath.toString(), PHOTOREC_REPORT).toString());
204 oldAuditFile.renameTo(newAuditFile);
206 Path pathToRemove = Paths.get(outputDirPath.toAbsolutePath().toString());
207 try (DirectoryStream<Path> stream = Files.newDirectoryStream(pathToRemove)) {
208 for (Path entry : stream) {
209 if (Files.isDirectory(entry)) {
216 PhotoRecCarverOutputParser parser =
new PhotoRecCarverOutputParser(outputDirPath);
217 List<LayoutFile> theList = parser.parse(newAuditFile,
id, file);
218 if (theList != null) {
223 catch (IOException ex) {
224 logger.log(Level.SEVERE,
"Error processing " + file.
getName() +
" with PhotoRec carver", ex);
229 if (null != tempFilePath && Files.exists(tempFilePath)) {
231 tempFilePath.toFile().delete();
238 private void cleanup(Path outputDirPath, Path tempFilePath) {
241 if (null != tempFilePath && Files.exists(tempFilePath)) {
242 tempFilePath.toFile().delete();
251 public void shutDown() {
252 if (this.context != null && refCounter.
decrementAndGet(
this.context.getJobId()) == 0) {
256 WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.remove(this.context.
getJobId());
259 catch (SecurityException ex) {
260 logger.log(Level.SEVERE,
"Error shutting down PhotoRec carver module", ex);
275 Path getOutputDirPath() {
279 Path getTempDirPath() {
290 synchronized static Path createModuleOutputDirectoryForCase() throws IngestModule.IngestModuleException {
291 Path path = Paths.get(Case.getCurrentCase().getModulesOutputDirAbsPath(), PhotoRecCarverIngestModuleFactory.getModuleName());
293 Files.createDirectory(path);
295 catch (FileAlreadyExistsException ex) {
298 catch (IOException | SecurityException | UnsupportedOperationException ex) {
299 throw new IngestModule.IngestModuleException(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class,
"cannotCreateOutputDir.message", ex.getLocalizedMessage()));
310 private static long getRootId(AbstractFile file) {
312 Content parent = null;
314 parent = file.getParent();
315 while (parent != null) {
316 if (parent instanceof Volume || parent instanceof Image) {
320 parent = parent.getParent();
323 catch (TskCoreException ex) {
324 logger.log(Level.SEVERE,
"PhotoRec carver exception while trying to get parent of AbstractFile.", ex);
336 public static File locateExecutable(String executableToFindName)
throws IngestModule.IngestModuleException {
338 if (!PlatformUtil.isWindowsOS()) {
339 throw new IngestModule.IngestModuleException(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class,
"unsupportedOS.message"));
342 File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, PhotoRecCarverFileIngestModule.class.getPackage().getName(),
false);
343 if (null == exeFile) {
344 throw new IngestModule.IngestModuleException(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class,
"missingExecutable.message"));
347 if (!exeFile.canExecute()) {
348 throw new IngestModule.IngestModuleException(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class,
"cannotRunExecutable.message"));
synchronized long decrementAndGet(long jobId)
static< T, V > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, SwingWorker< T, V > worker, boolean source)
static int execute(ProcessBuilder processBuilder)
TskData.TSK_DB_FILES_TYPE_ENUM getType()
void addFilesToJob(List< AbstractFile > files)
boolean fileIngestIsCancelled()
void fireModuleContentEvent(ModuleContentEvent moduleContentEvent)
static void error(String title, String message)
static boolean deleteDir(File dirPath)
static Logger getLogger(String name)
boolean processingUnallocatedSpace()
static synchronized IngestServices getInstance()