Merge pull request #3138 from eugene7646/file_visitor_fix

File visitor exception handling fix
This commit is contained in:
Brian Carrier 2017-10-06 16:37:23 -04:00 committed by GitHub
commit 8a445ca52c
3 changed files with 165 additions and 116 deletions

View File

@ -89,30 +89,34 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
* *
* @param manifest The manifest for an automated ingest job. * @param manifest The manifest for an automated ingest job.
*/ */
AutoIngestJob(Manifest manifest) { AutoIngestJob(Manifest manifest) throws AutoIngestJobException {
/* try {
* Version 0 fields. /*
*/ * Version 0 fields.
this.manifest = manifest; */
this.nodeName = ""; this.manifest = manifest;
this.caseDirectoryPath = ""; this.nodeName = "";
this.priority = DEFAULT_PRIORITY; this.caseDirectoryPath = "";
this.stage = Stage.PENDING; this.priority = DEFAULT_PRIORITY;
this.stageStartDate = manifest.getDateFileCreated(); this.stage = Stage.PENDING;
this.dataSourceProcessor = null; this.stageStartDate = manifest.getDateFileCreated();
this.ingestJob = null; this.dataSourceProcessor = null;
this.cancelled = false; this.ingestJob = null;
this.completed = false; this.cancelled = false;
this.completedDate = new Date(0); this.completed = false;
this.errorsOccurred = false; this.completedDate = new Date(0);
this.errorsOccurred = false;
/* /*
* Version 1 fields. * Version 1 fields.
*/ */
this.version = CURRENT_VERSION; this.version = CURRENT_VERSION;
this.processingStatus = ProcessingStatus.PENDING; this.processingStatus = ProcessingStatus.PENDING;
this.numberOfCrashes = 0; this.numberOfCrashes = 0;
this.stageDetails = this.getProcessingStageDetails(); this.stageDetails = this.getProcessingStageDetails();
} catch (Exception ex) {
throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex);
}
} }
/** /**
@ -122,30 +126,34 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
* @param nodeData The coordination service node data for an automated * @param nodeData The coordination service node data for an automated
* ingest job. * ingest job.
*/ */
AutoIngestJob(AutoIngestJobNodeData nodeData) { AutoIngestJob(AutoIngestJobNodeData nodeData) throws AutoIngestJobException {
/* try {
* Version 0 fields. /*
*/ * Version 0 fields.
this.manifest = new Manifest(nodeData.getManifestFilePath(), nodeData.getManifestFileDate(), nodeData.getCaseName(), nodeData.getDeviceId(), nodeData.getDataSourcePath(), Collections.emptyMap()); */
this.nodeName = nodeData.getProcessingHostName(); this.manifest = new Manifest(nodeData.getManifestFilePath(), nodeData.getManifestFileDate(), nodeData.getCaseName(), nodeData.getDeviceId(), nodeData.getDataSourcePath(), Collections.emptyMap());
this.caseDirectoryPath = nodeData.getCaseDirectoryPath().toString(); this.nodeName = nodeData.getProcessingHostName();
this.priority = nodeData.getPriority(); this.caseDirectoryPath = nodeData.getCaseDirectoryPath().toString();
this.stage = nodeData.getProcessingStage(); this.priority = nodeData.getPriority();
this.stageStartDate = nodeData.getProcessingStageStartDate(); this.stage = nodeData.getProcessingStage();
this.dataSourceProcessor = null; // Transient data not in node data. this.stageStartDate = nodeData.getProcessingStageStartDate();
this.ingestJob = null; // Transient data not in node data. this.dataSourceProcessor = null; // Transient data not in node data.
this.cancelled = false; // Transient data not in node data. this.ingestJob = null; // Transient data not in node data.
this.completed = false; // Transient data not in node data. this.cancelled = false; // Transient data not in node data.
this.completedDate = nodeData.getCompletedDate(); this.completed = false; // Transient data not in node data.
this.errorsOccurred = nodeData.getErrorsOccurred(); this.completedDate = nodeData.getCompletedDate();
this.errorsOccurred = nodeData.getErrorsOccurred();
/* /*
* Version 1 fields. * Version 1 fields.
*/ */
this.version = CURRENT_VERSION; this.version = CURRENT_VERSION;
this.processingStatus = nodeData.getProcessingStatus(); this.processingStatus = nodeData.getProcessingStatus();
this.numberOfCrashes = nodeData.getNumberOfCrashes(); this.numberOfCrashes = nodeData.getNumberOfCrashes();
this.stageDetails = this.getProcessingStageDetails(); this.stageDetails = this.getProcessingStageDetails();
} catch (Exception ex) {
throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex);
}
} }
/** /**
@ -622,5 +630,33 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
} }
} }
/**
* Exception thrown when there is a problem creating auto ingest job.
*/
final static class AutoIngestJobException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs an exception to throw when there is a problem creating
* auto ingest job.
*
* @param message The exception message.
*/
private AutoIngestJobException(String message) {
super(message);
}
/**
* Constructs an exception to throw when there is a problem creating
* auto ingest job.
*
* @param message The exception message.
* @param cause The cause of the exception, if it was an exception.
*/
private AutoIngestJobException(String message, Throwable cause) {
super(message, cause);
}
}
} }

View File

@ -61,6 +61,7 @@ import java.util.stream.Collectors;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import org.openide.util.Exceptions;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.Case.CaseType; import org.sleuthkit.autopsy.casemodule.Case.CaseType;
@ -93,6 +94,7 @@ import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration;
import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.SharedConfigurationException; import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.SharedConfigurationException;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.AutoIngestJobException;
import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob;
import org.sleuthkit.autopsy.ingest.IngestJob.CancellationReason; import org.sleuthkit.autopsy.ingest.IngestJob.CancellationReason;
import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestJobSettings;
@ -759,7 +761,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
AutoIngestJob deletedJob = new AutoIngestJob(nodeData); AutoIngestJob deletedJob = new AutoIngestJob(nodeData);
deletedJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.DELETED); deletedJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.DELETED);
this.updateCoordinationServiceNode(deletedJob); this.updateCoordinationServiceNode(deletedJob);
} catch (AutoIngestJobNodeData.InvalidDataException ex) { } catch (AutoIngestJobNodeData.InvalidDataException | AutoIngestJobException ex) {
SYS_LOGGER.log(Level.WARNING, String.format("Invalid auto ingest job node data for %s", manifestPath), ex); SYS_LOGGER.log(Level.WARNING, String.format("Invalid auto ingest job node data for %s", manifestPath), ex);
return CaseDeletionResult.PARTIALLY_DELETED; return CaseDeletionResult.PARTIALLY_DELETED;
} catch (InterruptedException | CoordinationServiceException ex) { } catch (InterruptedException | CoordinationServiceException ex) {
@ -1015,92 +1017,103 @@ public final class AutoIngestManager extends Observable implements PropertyChang
* @return TERMINATE if auto ingest is shutting down, CONTINUE if it has * @return TERMINATE if auto ingest is shutting down, CONTINUE if it has
* not. * not.
* *
* @throws IOException if an I/O error occurs, but this implementation
* does not throw.
*/ */
@Override @Override
public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs) {
if (Thread.currentThread().isInterrupted()) { if (Thread.currentThread().isInterrupted()) {
return TERMINATE; return TERMINATE;
} }
Manifest manifest = null; try {
for (ManifestFileParser parser : Lookup.getDefault().lookupAll(ManifestFileParser.class)) { Manifest manifest = null;
if (parser.fileIsManifest(filePath)) { for (ManifestFileParser parser : Lookup.getDefault().lookupAll(ManifestFileParser.class)) {
try { if (parser.fileIsManifest(filePath)) {
manifest = parser.parse(filePath); try {
break; manifest = parser.parse(filePath);
} catch (ManifestFileParserException ex) { break;
SYS_LOGGER.log(Level.SEVERE, String.format("Error attempting to parse %s with parser %s", filePath, parser.getClass().getCanonicalName()), ex); } catch (ManifestFileParserException ex) {
SYS_LOGGER.log(Level.SEVERE, String.format("Error attempting to parse %s with parser %s", filePath, parser.getClass().getCanonicalName()), ex);
}
}
if (Thread.currentThread().isInterrupted()) {
return TERMINATE;
} }
} }
if (Thread.currentThread().isInterrupted()) { if (Thread.currentThread().isInterrupted()) {
return TERMINATE; return TERMINATE;
} }
}
if (Thread.currentThread().isInterrupted()) { if (null != manifest) {
return TERMINATE; /*
}
if (null != manifest) {
/*
* Update the mapping of case names to manifest paths that is * Update the mapping of case names to manifest paths that is
* used for case deletion. * used for case deletion.
*/ */
String caseName = manifest.getCaseName(); String caseName = manifest.getCaseName();
Path manifestPath = manifest.getFilePath(); Path manifestPath = manifest.getFilePath();
if (casesToManifests.containsKey(caseName)) { if (casesToManifests.containsKey(caseName)) {
Set<Path> manifestPaths = casesToManifests.get(caseName); Set<Path> manifestPaths = casesToManifests.get(caseName);
manifestPaths.add(manifestPath); manifestPaths.add(manifestPath);
} else { } else {
Set<Path> manifestPaths = new HashSet<>(); Set<Path> manifestPaths = new HashSet<>();
manifestPaths.add(manifestPath); manifestPaths.add(manifestPath);
casesToManifests.put(caseName, manifestPaths); casesToManifests.put(caseName, manifestPaths);
} }
/* /*
* Add a job to the pending jobs queue, the completed jobs list, * Add a job to the pending jobs queue, the completed jobs list,
* or do crashed job recovery, as required. * or do crashed job recovery, as required.
*/ */
try { try {
byte[] rawData = coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestPath.toString()); byte[] rawData = coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestPath.toString());
if (null != rawData && rawData.length > 0) { if (null != rawData && rawData.length > 0) {
try { try {
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(rawData); AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(rawData);
AutoIngestJob.ProcessingStatus processingStatus = nodeData.getProcessingStatus(); AutoIngestJob.ProcessingStatus processingStatus = nodeData.getProcessingStatus();
switch (processingStatus) { switch (processingStatus) {
case PENDING: case PENDING:
addPendingJob(manifest, nodeData); addPendingJob(manifest, nodeData);
break; break;
case PROCESSING: case PROCESSING:
doRecoveryIfCrashed(manifest, nodeData); doRecoveryIfCrashed(manifest, nodeData);
break; break;
case COMPLETED: case COMPLETED:
addCompletedJob(manifest, nodeData); addCompletedJob(manifest, nodeData);
break; break;
case DELETED: case DELETED:
/* /*
* Ignore jobs marked as "deleted." * Ignore jobs marked as "deleted."
*/ */
break; break;
default: default:
SYS_LOGGER.log(Level.SEVERE, "Unknown ManifestNodeData.ProcessingStatus"); SYS_LOGGER.log(Level.SEVERE, "Unknown ManifestNodeData.ProcessingStatus");
break; break;
}
} catch (AutoIngestJobNodeData.InvalidDataException | AutoIngestJobException ex) {
SYS_LOGGER.log(Level.SEVERE, String.format("Invalid auto ingest job node data for %s", manifestPath), ex);
}
} else {
try {
addNewPendingJob(manifest);
} catch (AutoIngestJobException ex) {
SYS_LOGGER.log(Level.SEVERE, String.format("Invalid manifest data for %s", manifestPath), ex);
} }
} catch (AutoIngestJobNodeData.InvalidDataException ex) {
SYS_LOGGER.log(Level.WARNING, String.format("Invalid auto ingest job node data for %s", manifestPath), ex);
} }
} else { } catch (CoordinationServiceException ex) {
addNewPendingJob(manifest); SYS_LOGGER.log(Level.SEVERE, String.format("Error transmitting node data for %s", manifestPath), ex);
return CONTINUE;
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
return TERMINATE;
} }
} catch (CoordinationServiceException ex) {
SYS_LOGGER.log(Level.SEVERE, String.format("Error transmitting node data for %s", manifestPath), ex);
return CONTINUE;
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
return TERMINATE;
} }
} catch (Exception ex) {
// Catch all unhandled and unexpected exceptions. Otherwise one bad file
// can stop the entire input folder scanning. Given that the exception is unexpected,
// I'm hesitant to add logging which requires accessing or de-referencing data.
SYS_LOGGER.log(Level.SEVERE, "Unexpected exception in file visitor", ex);
return CONTINUE;
} }
if (!Thread.currentThread().isInterrupted()) { if (!Thread.currentThread().isInterrupted()) {
@ -1122,7 +1135,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
* blocked, i.e., if auto ingest is * blocked, i.e., if auto ingest is
* shutting down. * shutting down.
*/ */
private void addPendingJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws InterruptedException { private void addPendingJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws InterruptedException, AutoIngestJobException {
AutoIngestJob job; AutoIngestJob job;
if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) { if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) {
job = new AutoIngestJob(nodeData); job = new AutoIngestJob(nodeData);
@ -1176,7 +1189,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
* blocked, i.e., if auto ingest is * blocked, i.e., if auto ingest is
* shutting down. * shutting down.
*/ */
private void addNewPendingJob(Manifest manifest) throws InterruptedException { private void addNewPendingJob(Manifest manifest) throws InterruptedException, AutoIngestJobException {
/* /*
* Create the coordination service node data for the job. Note that * Create the coordination service node data for the job. Note that
* getting the lock will create the node for the job (with no data) * getting the lock will create the node for the job (with no data)
@ -1218,7 +1231,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
* blocked, i.e., if auto ingest is * blocked, i.e., if auto ingest is
* shutting down. * shutting down.
*/ */
private void doRecoveryIfCrashed(Manifest manifest, AutoIngestJobNodeData nodeData) throws InterruptedException { private void doRecoveryIfCrashed(Manifest manifest, AutoIngestJobNodeData nodeData) throws InterruptedException, AutoIngestJobException {
/* /*
* Try to get an exclusive lock on the coordination service node for * Try to get an exclusive lock on the coordination service node for
* the job. If the lock cannot be obtained, another host in the auto * the job. If the lock cannot be obtained, another host in the auto
@ -1314,7 +1327,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
* @throws CoordinationServiceException * @throws CoordinationServiceException
* @throws InterruptedException * @throws InterruptedException
*/ */
private void addCompletedJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws CoordinationServiceException, InterruptedException { private void addCompletedJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws CoordinationServiceException, InterruptedException, AutoIngestJobException {
Path caseDirectoryPath = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); Path caseDirectoryPath = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName());
if (null != caseDirectoryPath) { if (null != caseDirectoryPath) {
AutoIngestJob job; AutoIngestJob job;

View File

@ -265,7 +265,7 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
} }
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, String.format("Unexpected interrupt while retrieving coordination service node data for '%s'", node), ex); LOGGER.log(Level.SEVERE, String.format("Unexpected interrupt while retrieving coordination service node data for '%s'", node), ex);
} catch (AutoIngestJobNodeData.InvalidDataException ex) { } catch (AutoIngestJobNodeData.InvalidDataException | AutoIngestJob.AutoIngestJobException ex) {
LOGGER.log(Level.SEVERE, String.format("Unable to use node data for '%s'", node), ex); LOGGER.log(Level.SEVERE, String.format("Unable to use node data for '%s'", node), ex);
} }
} }