mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 10:17:41 +00:00
Merge branch 'search_improvements' of github.com:sleuthkit/autopsy into 2121_regex_query
This commit is contained in:
commit
b813a8dadd
@ -15,6 +15,10 @@ file.reference.StixLib.jar=release/modules/ext/StixLib.jar
|
|||||||
file.reference.tika-core-1.5.jar=release/modules/ext/tika-core-1.5.jar
|
file.reference.tika-core-1.5.jar=release/modules/ext/tika-core-1.5.jar
|
||||||
file.reference.Tsk_DataModel_PostgreSQL.jar=release/modules/ext/Tsk_DataModel_PostgreSQL.jar
|
file.reference.Tsk_DataModel_PostgreSQL.jar=release/modules/ext/Tsk_DataModel_PostgreSQL.jar
|
||||||
file.reference.xmpcore-5.1.2.jar=release/modules/ext/xmpcore-5.1.2.jar
|
file.reference.xmpcore-5.1.2.jar=release/modules/ext/xmpcore-5.1.2.jar
|
||||||
|
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
|
||||||
|
file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar
|
||||||
|
file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar
|
||||||
|
file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar
|
||||||
javac.source=1.8
|
javac.source=1.8
|
||||||
javac.compilerargs=-Xlint -Xlint:-serial
|
javac.compilerargs=-Xlint -Xlint:-serial
|
||||||
javadoc.reference.metadata-extractor-2.8.1.jar=release/modules/ext/metadata-extractor-2.8.1-src.zip
|
javadoc.reference.metadata-extractor-2.8.1.jar=release/modules/ext/metadata-extractor-2.8.1-src.zip
|
||||||
@ -23,5 +27,6 @@ nbm.homepage=http://www.sleuthkit.org/
|
|||||||
nbm.module.author=Brian Carrier
|
nbm.module.author=Brian Carrier
|
||||||
nbm.needs.restart=true
|
nbm.needs.restart=true
|
||||||
source.reference.metadata-extractor-2.8.1.jar=release/modules/ext/metadata-extractor-2.8.1-src.zip!/Source/
|
source.reference.metadata-extractor-2.8.1.jar=release/modules/ext/metadata-extractor-2.8.1-src.zip!/Source/
|
||||||
|
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
|
||||||
spec.version.base=10.7
|
spec.version.base=10.7
|
||||||
|
|
||||||
|
@ -231,6 +231,7 @@
|
|||||||
<package>org.sleuthkit.autopsy.casemodule.events</package>
|
<package>org.sleuthkit.autopsy.casemodule.events</package>
|
||||||
<package>org.sleuthkit.autopsy.casemodule.services</package>
|
<package>org.sleuthkit.autopsy.casemodule.services</package>
|
||||||
<package>org.sleuthkit.autopsy.contentviewers</package>
|
<package>org.sleuthkit.autopsy.contentviewers</package>
|
||||||
|
<package>org.sleuthkit.autopsy.coordinationservice</package>
|
||||||
<package>org.sleuthkit.autopsy.core</package>
|
<package>org.sleuthkit.autopsy.core</package>
|
||||||
<package>org.sleuthkit.autopsy.core.events</package>
|
<package>org.sleuthkit.autopsy.core.events</package>
|
||||||
<package>org.sleuthkit.autopsy.corecomponentinterfaces</package>
|
<package>org.sleuthkit.autopsy.corecomponentinterfaces</package>
|
||||||
@ -326,6 +327,22 @@
|
|||||||
<runtime-relative-path>ext/Tsk_DataModel_PostgreSQL.jar</runtime-relative-path>
|
<runtime-relative-path>ext/Tsk_DataModel_PostgreSQL.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/Tsk_DataModel_PostgreSQL.jar</binary-origin>
|
<binary-origin>release/modules/ext/Tsk_DataModel_PostgreSQL.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
|
<class-path-extension>
|
||||||
|
<runtime-relative-path>ext/zookeeper-3.4.6.jar</runtime-relative-path>
|
||||||
|
<binary-origin>release/modules/ext/zookeeper-3.4.6.jar</binary-origin>
|
||||||
|
</class-path-extension>
|
||||||
|
<class-path-extension>
|
||||||
|
<runtime-relative-path>ext/curator-client-2.8.0.jar</runtime-relative-path>
|
||||||
|
<binary-origin>release/modules/ext/curator-client-2.8.0.jar</binary-origin>
|
||||||
|
</class-path-extension>
|
||||||
|
<class-path-extension>
|
||||||
|
<runtime-relative-path>ext/curator-recipes-2.8.0.jar</runtime-relative-path>
|
||||||
|
<binary-origin>release/modules/ext/curator-recipes-2.8.0.jar</binary-origin>
|
||||||
|
</class-path-extension>
|
||||||
|
<class-path-extension>
|
||||||
|
<runtime-relative-path>ext/curator-framework-2.8.0.jar</runtime-relative-path>
|
||||||
|
<binary-origin>release/modules/ext/curator-framework-2.8.0.jar</binary-origin>
|
||||||
|
</class-path-extension>
|
||||||
</data>
|
</data>
|
||||||
</configuration>
|
</configuration>
|
||||||
</project>
|
</project>
|
||||||
|
@ -133,6 +133,7 @@ Case.GetCaseTypeGivenPath.Failure=Unable to get case type
|
|||||||
Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted.
|
Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted.
|
||||||
Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk.
|
Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk.
|
||||||
Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1}
|
Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1}
|
||||||
|
Case.exception.errorLocking=Unable to open case being updated by another user.
|
||||||
CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\
|
CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\
|
||||||
Case Name\: {0}\n\
|
Case Name\: {0}\n\
|
||||||
Case Directory\: {1}
|
Case Directory\: {1}
|
||||||
|
@ -39,6 +39,12 @@ import java.util.UUID;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import org.openide.util.Exceptions;
|
import org.openide.util.Exceptions;
|
||||||
@ -57,6 +63,8 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
|||||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
|
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
|
||||||
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
|
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
|
||||||
import org.sleuthkit.autopsy.casemodule.services.Services;
|
import org.sleuthkit.autopsy.casemodule.services.Services;
|
||||||
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||||
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationServiceNamespace;
|
||||||
import org.sleuthkit.autopsy.core.RuntimeProperties;
|
import org.sleuthkit.autopsy.core.RuntimeProperties;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferencesException;
|
import org.sleuthkit.autopsy.core.UserPreferencesException;
|
||||||
@ -283,6 +291,8 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
private static final AutopsyEventPublisher eventPublisher = new AutopsyEventPublisher();
|
private static final AutopsyEventPublisher eventPublisher = new AutopsyEventPublisher();
|
||||||
private static String appName;
|
private static String appName;
|
||||||
private static Case currentCase;
|
private static Case currentCase;
|
||||||
|
private static CoordinationService.Lock currentCaseLock;
|
||||||
|
private static ExecutorService currentCaseExecutor;
|
||||||
private final CaseMetadata caseMetadata;
|
private final CaseMetadata caseMetadata;
|
||||||
private final SleuthkitCase db;
|
private final SleuthkitCase db;
|
||||||
private final Services services;
|
private final Services services;
|
||||||
@ -835,10 +845,30 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
* exception.
|
* exception.
|
||||||
*/
|
*/
|
||||||
public void closeCase() throws CaseActionException {
|
public void closeCase() throws CaseActionException {
|
||||||
|
|
||||||
|
// The unlock must happen on the same thread that created the lock
|
||||||
|
Future<Void> future = getCurrentCaseExecutor().submit(() -> {
|
||||||
|
try{
|
||||||
|
if (currentCaseLock != null) {
|
||||||
|
currentCaseLock.release();
|
||||||
|
currentCaseLock = null;
|
||||||
|
}
|
||||||
|
} catch (CoordinationService.CoordinationServiceException exx) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error releasing shared lock"), exx);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
try{
|
||||||
|
future.get();
|
||||||
|
} catch (InterruptedException | ExecutionException ex){
|
||||||
|
logger.log(Level.SEVERE, String.format("Interrupted while releasing shared lock"), ex);
|
||||||
|
}
|
||||||
|
|
||||||
changeCurrentCase(null);
|
changeCurrentCase(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
services.close();
|
services.close();
|
||||||
this.db.close();
|
this.db.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.closeCase.exception.msg"), e);
|
throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.closeCase.exception.msg"), e);
|
||||||
}
|
}
|
||||||
@ -943,6 +973,9 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
public static void create(String caseDir, String caseName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException {
|
public static void create(String caseDir, String caseName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException {
|
||||||
logger.log(Level.INFO, "Attempting to create case {0} in directory = {1}", new Object[]{caseName, caseDir}); //NON-NLS
|
logger.log(Level.INFO, "Attempting to create case {0} in directory = {1}", new Object[]{caseName, caseDir}); //NON-NLS
|
||||||
|
|
||||||
|
CoordinationService.Lock exclusiveResourceLock = null;
|
||||||
|
boolean caseCreatedSuccessfully = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create case directory if it doesn't already exist.
|
* Create case directory if it doesn't already exist.
|
||||||
*/
|
*/
|
||||||
@ -965,49 +998,113 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
} else if (caseType == CaseType.MULTI_USER_CASE) {
|
} else if (caseType == CaseType.MULTI_USER_CASE) {
|
||||||
dbName = indexName;
|
dbName = indexName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
/* If this is a multi-user case, acquire two locks:
|
||||||
|
* - a shared lock on the case to prevent it from being deleted while open
|
||||||
|
* - an exclusive lock to prevent multiple clients from executing openCase simultaneously
|
||||||
|
*/
|
||||||
|
if(caseType == CaseType.MULTI_USER_CASE){
|
||||||
|
try{
|
||||||
|
|
||||||
/*
|
// The shared lock uses case directory
|
||||||
* Create the case metadata (.aut) file.
|
// The shared lock needs to be created on a special thread so it can be released
|
||||||
*/
|
// from the same thread.
|
||||||
CaseMetadata metadata;
|
Future<Void> future = getCurrentCaseExecutor().submit(() -> {
|
||||||
try {
|
currentCaseLock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetSharedLock(CoordinationService.CategoryNode.CASES, caseDir);
|
||||||
metadata = new CaseMetadata(caseDir, caseType, caseName, caseNumber, examiner, dbName, indexName);
|
if (null == currentCaseLock) {
|
||||||
} catch (CaseMetadataException ex) {
|
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.exception.errorLocking", CaseMetadata.getFileExtension()));
|
||||||
throw new CaseActionException(Bundle.Case_creationException(), ex);
|
}
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
|
future.get();
|
||||||
|
|
||||||
/*
|
// The exclusive lock uses the unique case name.
|
||||||
* Create the case database.
|
// This lock does not need to be on a special thread since it will be released before
|
||||||
*/
|
// leaving this method
|
||||||
SleuthkitCase db = null;
|
exclusiveResourceLock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.RESOURCE,
|
||||||
try {
|
dbName, 12, TimeUnit.HOURS);
|
||||||
if (caseType == CaseType.SINGLE_USER_CASE) {
|
if (null == exclusiveResourceLock) {
|
||||||
db = SleuthkitCase.newCase(dbName);
|
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.exception.errorLocking", CaseMetadata.getFileExtension()));
|
||||||
} else if (caseType == CaseType.MULTI_USER_CASE) {
|
}
|
||||||
db = SleuthkitCase.newCase(dbName, UserPreferences.getDatabaseConnectionInfo(), caseDir);
|
} catch (Exception ex){
|
||||||
|
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.exception.errorLocking", CaseMetadata.getFileExtension()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, String.format("Error creating a case %s in %s ", caseName, caseDir), ex); //NON-NLS
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
|
||||||
});
|
|
||||||
/*
|
/*
|
||||||
* SleuthkitCase.newCase throws TskCoreExceptions with user-friendly
|
* Create the case metadata (.aut) file.
|
||||||
* messages, so propagate the exception message.
|
|
||||||
*/
|
*/
|
||||||
throw new CaseActionException(ex.getMessage(), ex); //NON-NLS
|
CaseMetadata metadata;
|
||||||
} catch (UserPreferencesException ex) {
|
try {
|
||||||
logger.log(Level.SEVERE, "Error accessing case database connection info", ex); //NON-NLS
|
metadata = new CaseMetadata(caseDir, caseType, caseName, caseNumber, examiner, dbName, indexName);
|
||||||
SwingUtilities.invokeLater(() -> {
|
} catch (CaseMetadataException ex) {
|
||||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
throw new CaseActionException(Bundle.Case_creationException(), ex);
|
||||||
});
|
}
|
||||||
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.databaseConnectionInfo.error.msg"), ex);
|
|
||||||
|
/*
|
||||||
|
* Create the case database.
|
||||||
|
*/
|
||||||
|
SleuthkitCase db = null;
|
||||||
|
try {
|
||||||
|
if (caseType == CaseType.SINGLE_USER_CASE) {
|
||||||
|
db = SleuthkitCase.newCase(dbName);
|
||||||
|
} else if (caseType == CaseType.MULTI_USER_CASE) {
|
||||||
|
db = SleuthkitCase.newCase(dbName, UserPreferences.getDatabaseConnectionInfo(), caseDir);
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error creating a case %s in %s ", caseName, caseDir), ex); //NON-NLS
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
* SleuthkitCase.newCase throws TskCoreExceptions with user-friendly
|
||||||
|
* messages, so propagate the exception message.
|
||||||
|
*/
|
||||||
|
throw new CaseActionException(ex.getMessage(), ex); //NON-NLS
|
||||||
|
} catch (UserPreferencesException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error accessing case database connection info", ex); //NON-NLS
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||||
|
});
|
||||||
|
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.databaseConnectionInfo.error.msg"), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
Case newCase = new Case(metadata, db);
|
||||||
|
changeCurrentCase(newCase);
|
||||||
|
caseCreatedSuccessfully = true;
|
||||||
|
|
||||||
|
logger.log(Level.INFO, "Created case {0} in directory = {1}", new Object[]{caseName, caseDir}); //NON-NLS
|
||||||
|
} finally {
|
||||||
|
// Release the exclusive resource lock
|
||||||
|
try {
|
||||||
|
if (exclusiveResourceLock != null) {
|
||||||
|
exclusiveResourceLock.release();
|
||||||
|
}
|
||||||
|
} catch (CoordinationService.CoordinationServiceException exx) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error releasing resource lock for case {0}", caseName), exx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an error occurred while opening the case, release the shared case lock as well
|
||||||
|
if(! caseCreatedSuccessfully){
|
||||||
|
Future<Void> future = getCurrentCaseExecutor().submit(() -> {
|
||||||
|
try {
|
||||||
|
if (currentCaseLock != null) {
|
||||||
|
currentCaseLock.release();
|
||||||
|
currentCaseLock = null;
|
||||||
|
}
|
||||||
|
} catch (CoordinationService.CoordinationServiceException exx) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error releasing shared lock for case {0}", caseName), exx);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
try{
|
||||||
|
future.get();
|
||||||
|
} catch (InterruptedException | ExecutionException ex){
|
||||||
|
logger.log(Level.SEVERE, String.format("Interrupted while releasing shared lock"), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Case newCase = new Case(metadata, db);
|
|
||||||
changeCurrentCase(newCase);
|
|
||||||
|
|
||||||
logger.log(Level.INFO, "Created case {0} in directory = {1}", new Object[]{caseName, caseDir}); //NON-NLS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1146,6 +1243,8 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
*/
|
*/
|
||||||
public static void open(String caseMetadataFilePath) throws CaseActionException {
|
public static void open(String caseMetadataFilePath) throws CaseActionException {
|
||||||
logger.log(Level.INFO, "Opening case with metadata file path {0}", caseMetadataFilePath); //NON-NLS
|
logger.log(Level.INFO, "Opening case with metadata file path {0}", caseMetadataFilePath); //NON-NLS
|
||||||
|
CoordinationService.Lock exclusiveResourceLock = null;
|
||||||
|
boolean caseOpenedSuccessfully = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Verify the extension of the case metadata file.
|
* Verify the extension of the case metadata file.
|
||||||
@ -1161,6 +1260,39 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
CaseMetadata metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
|
CaseMetadata metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
|
||||||
CaseType caseType = metadata.getCaseType();
|
CaseType caseType = metadata.getCaseType();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this is a multi-user case, acquire two locks:
|
||||||
|
* - a shared lock on the case to prevent it from being deleted while open
|
||||||
|
* - an exclusive lock to prevent multiple clients from executing openCase simultaneously
|
||||||
|
*/
|
||||||
|
if(caseType == CaseType.MULTI_USER_CASE){
|
||||||
|
try{
|
||||||
|
|
||||||
|
// The shared lock uses the case directory
|
||||||
|
// The shared lock needs to be created on a special thread so it can be released
|
||||||
|
// from the same thread.
|
||||||
|
Future<Void> future = getCurrentCaseExecutor().submit(() -> {
|
||||||
|
currentCaseLock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetSharedLock(CoordinationService.CategoryNode.CASES, metadata.getCaseDirectory());
|
||||||
|
if (null == currentCaseLock) {
|
||||||
|
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.exception.errorLocking", CaseMetadata.getFileExtension()));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
future.get();
|
||||||
|
|
||||||
|
// The exclusive lock uses the unique case name
|
||||||
|
// This lock does not need to be on a special thread since it will be released before
|
||||||
|
// leaving this method
|
||||||
|
exclusiveResourceLock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.RESOURCE,
|
||||||
|
metadata.getCaseDatabaseName(), 12, TimeUnit.HOURS);
|
||||||
|
if (null == exclusiveResourceLock) {
|
||||||
|
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.exception.errorLocking", CaseMetadata.getFileExtension()));
|
||||||
|
}
|
||||||
|
} catch (Exception ex){
|
||||||
|
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.exception.errorLocking", CaseMetadata.getFileExtension()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the case database.
|
* Open the case database.
|
||||||
*/
|
*/
|
||||||
@ -1224,6 +1356,7 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
}
|
}
|
||||||
Case openedCase = new Case(metadata, db);
|
Case openedCase = new Case(metadata, db);
|
||||||
changeCurrentCase(openedCase);
|
changeCurrentCase(openedCase);
|
||||||
|
caseOpenedSuccessfully = true;
|
||||||
|
|
||||||
} catch (CaseMetadataException ex) {
|
} catch (CaseMetadataException ex) {
|
||||||
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.metaDataFileCorrupt.exception.msg"), ex); //NON-NLS
|
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.metaDataFileCorrupt.exception.msg"), ex); //NON-NLS
|
||||||
@ -1236,6 +1369,36 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
* user-friendly messages, so propagate the exception message.
|
* user-friendly messages, so propagate the exception message.
|
||||||
*/
|
*/
|
||||||
throw new CaseActionException(ex.getMessage(), ex);
|
throw new CaseActionException(ex.getMessage(), ex);
|
||||||
|
} finally {
|
||||||
|
// Release the exclusive resource lock
|
||||||
|
try {
|
||||||
|
if (exclusiveResourceLock != null) {
|
||||||
|
exclusiveResourceLock.release();
|
||||||
|
exclusiveResourceLock = null;
|
||||||
|
}
|
||||||
|
} catch (CoordinationService.CoordinationServiceException exx) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error releasing resource lock for case {0}", caseMetadataFilePath), exx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an error occurred while opening the case, release the shared case lock as well
|
||||||
|
if(! caseOpenedSuccessfully){
|
||||||
|
Future<Void> future = getCurrentCaseExecutor().submit(() -> {
|
||||||
|
try {
|
||||||
|
if (currentCaseLock != null) {
|
||||||
|
currentCaseLock.release();
|
||||||
|
currentCaseLock = null;
|
||||||
|
}
|
||||||
|
} catch (CoordinationService.CoordinationServiceException exx) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error releasing shared lock for case {0}", caseMetadataFilePath), exx);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
try{
|
||||||
|
future.get();
|
||||||
|
} catch (InterruptedException | ExecutionException ex){
|
||||||
|
logger.log(Level.SEVERE, String.format("Interrupted while releasing shared lock"), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1320,18 +1483,20 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
MessageNotifyUtil.Notify.error(NbBundle.getMessage(Case.class, "Case.CollaborationSetup.FailNotify.Title"), NbBundle.getMessage(Case.class, "Case.CollaborationSetup.FailNotify.ErrMsg"));
|
MessageNotifyUtil.Notify.error(NbBundle.getMessage(Case.class, "Case.CollaborationSetup.FailNotify.Title"), NbBundle.getMessage(Case.class, "Case.CollaborationSetup.FailNotify.ErrMsg"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AutopsyService.CaseContext context = new AutopsyService.CaseContext(Case.currentCase, new LoggingProgressIndicator());
|
AutopsyService.CaseContext context = new AutopsyService.CaseContext(Case.currentCase, new LoggingProgressIndicator());
|
||||||
String serviceName = "";
|
String serviceName = "";
|
||||||
for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) {
|
for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) {
|
||||||
try {
|
try {
|
||||||
serviceName = service.getServiceName();
|
serviceName = service.getServiceName();
|
||||||
service.openCaseResources(context);
|
if (!serviceName.equals("Solr Keyword Search Service")) {
|
||||||
|
service.openCaseResources(context);
|
||||||
|
}
|
||||||
} catch (AutopsyService.AutopsyServiceException ex) {
|
} catch (AutopsyService.AutopsyServiceException ex) {
|
||||||
Case.logger.log(Level.SEVERE, String.format("%s service failed to open case resources", serviceName), ex);
|
Case.logger.log(Level.SEVERE, String.format("%s service failed to open case resources", serviceName), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
|
eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -1448,12 +1613,23 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
*
|
*
|
||||||
* @param casePath A case directory path.
|
* @param casePath A case directory path.
|
||||||
*
|
*
|
||||||
* @return True if the deleteion succeeded, false otherwise.
|
* @return True if the deletion succeeded, false otherwise.
|
||||||
*/
|
*/
|
||||||
static boolean deleteCaseDirectory(File casePath) {
|
static boolean deleteCaseDirectory(File casePath) {
|
||||||
logger.log(Level.INFO, "Deleting case directory: {0}", casePath.getAbsolutePath()); //NON-NLS
|
logger.log(Level.INFO, "Deleting case directory: {0}", casePath.getAbsolutePath()); //NON-NLS
|
||||||
return FileUtil.deleteDir(casePath);
|
return FileUtil.deleteDir(casePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the single thread executor for the current case, creating it if necessary.
|
||||||
|
* @return The executor
|
||||||
|
*/
|
||||||
|
private static ExecutorService getCurrentCaseExecutor(){
|
||||||
|
if(currentCaseExecutor == null){
|
||||||
|
currentCaseExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
}
|
||||||
|
return currentCaseExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the time zone(s) of the image data source(s) in this case.
|
* Gets the time zone(s) of the image data source(s) in this case.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2015 Basis Technology Corp.
|
* Copyright 2011-2017 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -16,130 +16,91 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.experimental.coordinationservice;
|
package org.sleuthkit.autopsy.coordinationservice;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.apache.zookeeper.CreateMode;
|
|
||||||
import org.apache.zookeeper.ZooDefs;
|
|
||||||
import org.apache.curator.RetryPolicy;
|
import org.apache.curator.RetryPolicy;
|
||||||
import org.apache.curator.retry.ExponentialBackoffRetry;
|
|
||||||
import org.apache.curator.framework.CuratorFramework;
|
import org.apache.curator.framework.CuratorFramework;
|
||||||
import org.apache.curator.framework.CuratorFrameworkFactory;
|
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||||
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
|
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
|
||||||
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
|
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
|
||||||
|
import org.apache.curator.retry.ExponentialBackoffRetry;
|
||||||
|
import org.apache.zookeeper.CreateMode;
|
||||||
import org.apache.zookeeper.KeeperException;
|
import org.apache.zookeeper.KeeperException;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.apache.zookeeper.WatchedEvent;
|
|
||||||
import org.apache.zookeeper.ZooKeeper;
|
|
||||||
import org.apache.zookeeper.KeeperException.NoNodeException;
|
import org.apache.zookeeper.KeeperException.NoNodeException;
|
||||||
|
import org.apache.zookeeper.WatchedEvent;
|
||||||
|
import org.apache.zookeeper.ZooDefs;
|
||||||
|
import org.apache.zookeeper.ZooKeeper;
|
||||||
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A centralized service for maintaining configuration information and providing
|
* A coordination service for maintaining configuration information and
|
||||||
* distributed synchronization using a shared hierarchical namespace of nodes.
|
* providing distributed synchronization using a shared hierarchical namespace
|
||||||
|
* of nodes.
|
||||||
|
*
|
||||||
|
* TODO (JIRA 2205): Simple refactoring for general use.
|
||||||
*/
|
*/
|
||||||
public final class CoordinationService {
|
public final class CoordinationService {
|
||||||
|
|
||||||
/**
|
|
||||||
* Category nodes are the immediate children of the root node of a shared
|
|
||||||
* hierarchical namespace managed by the coordination service.
|
|
||||||
*/
|
|
||||||
public enum CategoryNode { // RJCTODO: Move this to CoordinationServiceNamespace
|
|
||||||
|
|
||||||
CASES("cases"),
|
|
||||||
MANIFESTS("manifests"),
|
|
||||||
CONFIG("config");
|
|
||||||
|
|
||||||
private final String displayName;
|
|
||||||
|
|
||||||
private CategoryNode(String displayName) {
|
|
||||||
this.displayName = displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception type thrown by the coordination service.
|
|
||||||
*/
|
|
||||||
public final static class CoordinationServiceException extends Exception {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private CoordinationServiceException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CoordinationServiceException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An opaque encapsulation of a lock for use in distributed synchronization.
|
|
||||||
* Instances are obtained by calling a get lock method and must be passed to
|
|
||||||
* a release lock method.
|
|
||||||
*/
|
|
||||||
public static class Lock implements AutoCloseable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This implementation uses the Curator read/write lock. see
|
|
||||||
* http://curator.apache.org/curator-recipes/shared-reentrant-read-write-lock.html
|
|
||||||
*/
|
|
||||||
private final InterProcessMutex interProcessLock;
|
|
||||||
private final String nodePath;
|
|
||||||
|
|
||||||
private Lock(String nodePath, InterProcessMutex lock) {
|
|
||||||
this.nodePath = nodePath;
|
|
||||||
this.interProcessLock = lock;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNodePath() {
|
|
||||||
return nodePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void release() throws CoordinationServiceException {
|
|
||||||
try {
|
|
||||||
this.interProcessLock.release();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new CoordinationServiceException(String.format("Failed to release the lock on %s", nodePath), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws CoordinationServiceException {
|
|
||||||
release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static CuratorFramework curator = null;
|
private static CuratorFramework curator = null;
|
||||||
private static final Map<String, CoordinationService> rootNodesToServices = new HashMap<>();
|
private static final Map<String, CoordinationService> rootNodesToServices = new HashMap<>();
|
||||||
private final Map<String, String> categoryNodeToPath = new HashMap<>();
|
|
||||||
private static final int SESSION_TIMEOUT_MILLISECONDS = 300000;
|
private static final int SESSION_TIMEOUT_MILLISECONDS = 300000;
|
||||||
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 300000;
|
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 300000;
|
||||||
private static final int ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 3000;
|
private static final int ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 3000;
|
||||||
private static final int ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS = 15000;
|
private static final int ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS = 15000;
|
||||||
private static final int PORT_OFFSET = 1000;
|
private static final int PORT_OFFSET = 1000; // When run in Solr, ZooKeeper defaults to Solr port + 1000
|
||||||
|
private final Map<String, String> categoryNodeToPath = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets an instance of the centralized coordination service for a specific
|
* Determines if ZooKeeper is accessible with the current settings. Closes
|
||||||
* namespace.
|
* the connection prior to returning.
|
||||||
|
*
|
||||||
|
* @return true if a connection was achieved, false otherwise
|
||||||
|
*
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private static boolean isZooKeeperAccessible() throws InterruptedException, IOException {
|
||||||
|
boolean result = false;
|
||||||
|
Object workerThreadWaitNotifyLock = new Object();
|
||||||
|
int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
|
||||||
|
String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
|
||||||
|
ZooKeeper zooKeeper = new ZooKeeper(connectString, ZOOKEEPER_SESSION_TIMEOUT_MILLIS,
|
||||||
|
(WatchedEvent event) -> {
|
||||||
|
synchronized (workerThreadWaitNotifyLock) {
|
||||||
|
workerThreadWaitNotifyLock.notify();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
synchronized (workerThreadWaitNotifyLock) {
|
||||||
|
workerThreadWaitNotifyLock.wait(ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS);
|
||||||
|
}
|
||||||
|
ZooKeeper.States state = zooKeeper.getState();
|
||||||
|
if (state == ZooKeeper.States.CONNECTED || state == ZooKeeper.States.CONNECTEDREADONLY) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
zooKeeper.close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a coordination service for a specific namespace.
|
||||||
*
|
*
|
||||||
* @param rootNode The name of the root node that defines the namespace.
|
* @param rootNode The name of the root node that defines the namespace.
|
||||||
*
|
*
|
||||||
* @return The service for the namespace defined by the root node name.
|
* @return The coordination service.
|
||||||
*
|
*
|
||||||
* @throws CoordinationServiceException If an instaNce of the coordination
|
* @throws CoordinationServiceException If an instance of the coordination
|
||||||
* service cannot be created.
|
* service cannot be created.
|
||||||
*/
|
*/
|
||||||
public static synchronized CoordinationService getInstance(String rootNode) throws CoordinationServiceException {
|
public static synchronized CoordinationService getServiceForNamespace(String rootNode) throws CoordinationServiceException {
|
||||||
|
/*
|
||||||
|
* Connect to ZooKeeper via Curator.
|
||||||
|
*/
|
||||||
if (null == curator) {
|
if (null == curator) {
|
||||||
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
|
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
|
||||||
// When run in Solr, ZooKeeper defaults to Solr port + 1000
|
|
||||||
int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
|
int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
|
||||||
String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
|
String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
|
||||||
curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy);
|
curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy);
|
||||||
@ -156,7 +117,7 @@ public final class CoordinationService {
|
|||||||
CoordinationService service;
|
CoordinationService service;
|
||||||
try {
|
try {
|
||||||
service = new CoordinationService(rootNode);
|
service = new CoordinationService(rootNode);
|
||||||
} catch (Exception ex) {
|
} catch (IOException | InterruptedException | KeeperException | CoordinationServiceException ex) {
|
||||||
curator = null;
|
curator = null;
|
||||||
throw new CoordinationServiceException("Failed to create coordination service", ex);
|
throw new CoordinationServiceException("Failed to create coordination service", ex);
|
||||||
}
|
}
|
||||||
@ -166,15 +127,18 @@ public final class CoordinationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an instance of the centralized coordination service for a
|
* Constructs an instance of the coordination service for a specific
|
||||||
* specific namespace.
|
* namespace.
|
||||||
*
|
*
|
||||||
* @param rootNodeName The name of the root node that defines the namespace.
|
* @param rootNodeName The name of the root node that defines the namespace.
|
||||||
|
*
|
||||||
|
* @throws Exception (calls Curator methods that throw Exception instead of
|
||||||
|
* more specific exceptions)
|
||||||
*/
|
*/
|
||||||
private CoordinationService(String rootNodeName) throws Exception {
|
private CoordinationService(String rootNodeName) throws InterruptedException, IOException, KeeperException, CoordinationServiceException {
|
||||||
|
|
||||||
if (false == isZooKeeperAccessible()) {
|
if (false == isZooKeeperAccessible()) {
|
||||||
throw new Exception("Unable to access ZooKeeper");
|
throw new CoordinationServiceException("Unable to access ZooKeeper");
|
||||||
}
|
}
|
||||||
|
|
||||||
String rootNode = rootNodeName;
|
String rootNode = rootNodeName;
|
||||||
@ -190,6 +154,8 @@ public final class CoordinationService {
|
|||||||
if (ex.code() != KeeperException.Code.NODEEXISTS) {
|
if (ex.code() != KeeperException.Code.NODEEXISTS) {
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new CoordinationServiceException("Curator experienced an error", ex);
|
||||||
}
|
}
|
||||||
categoryNodeToPath.put(node.getDisplayName(), nodePath);
|
categoryNodeToPath.put(node.getDisplayName(), nodePath);
|
||||||
}
|
}
|
||||||
@ -384,35 +350,77 @@ public final class CoordinationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if ZooKeeper is accessible with the current settings. Closes
|
* Exception type thrown by the coordination service.
|
||||||
* the connection prior to returning.
|
|
||||||
*
|
|
||||||
* @return true if a connection was achieved, false otherwise
|
|
||||||
*/
|
*/
|
||||||
private static boolean isZooKeeperAccessible() {
|
public final static class CoordinationServiceException extends Exception {
|
||||||
boolean result = false;
|
|
||||||
Object workerThreadWaitNotifyLock = new Object();
|
|
||||||
int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
|
|
||||||
String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
|
|
||||||
|
|
||||||
try {
|
private static final long serialVersionUID = 1L;
|
||||||
ZooKeeper zooKeeper = new ZooKeeper(connectString, ZOOKEEPER_SESSION_TIMEOUT_MILLIS,
|
|
||||||
(WatchedEvent event) -> {
|
|
||||||
|
|
||||||
synchronized (workerThreadWaitNotifyLock) {
|
private CoordinationServiceException(String message) {
|
||||||
workerThreadWaitNotifyLock.notify();
|
super(message);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
synchronized (workerThreadWaitNotifyLock) {
|
private CoordinationServiceException(String message, Throwable cause) {
|
||||||
workerThreadWaitNotifyLock.wait(ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
ZooKeeper.States state = zooKeeper.getState();
|
}
|
||||||
if (state == ZooKeeper.States.CONNECTED || state == ZooKeeper.States.CONNECTEDREADONLY) {
|
|
||||||
result = true;
|
/**
|
||||||
}
|
* An opaque encapsulation of a lock for use in distributed synchronization.
|
||||||
zooKeeper.close();
|
* Instances are obtained by calling a get lock method and must be passed to
|
||||||
} catch (InterruptedException | IOException ignored) {
|
* a release lock method.
|
||||||
|
*/
|
||||||
|
public static class Lock implements AutoCloseable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation uses the Curator read/write lock. see
|
||||||
|
* http://curator.apache.org/curator-recipes/shared-reentrant-read-write-lock.html
|
||||||
|
*/
|
||||||
|
private final InterProcessMutex interProcessLock;
|
||||||
|
private final String nodePath;
|
||||||
|
|
||||||
|
private Lock(String nodePath, InterProcessMutex lock) {
|
||||||
|
this.nodePath = nodePath;
|
||||||
|
this.interProcessLock = lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNodePath() {
|
||||||
|
return nodePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void release() throws CoordinationServiceException {
|
||||||
|
try {
|
||||||
|
this.interProcessLock.release();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new CoordinationServiceException(String.format("Failed to release the lock on %s", nodePath), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws CoordinationServiceException {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Category nodes are the immediate children of the root node of a shared
|
||||||
|
* hierarchical namespace managed by a coordination service.
|
||||||
|
*/
|
||||||
|
public enum CategoryNode {
|
||||||
|
|
||||||
|
CASES("cases"),
|
||||||
|
MANIFESTS("manifests"),
|
||||||
|
CONFIG("config"),
|
||||||
|
RESOURCE("resource");
|
||||||
|
|
||||||
|
private final String displayName;
|
||||||
|
|
||||||
|
private CategoryNode(String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2015 Basis Technology Corp.
|
* Copyright 2016-2017 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -16,15 +16,15 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.experimental.autoingest;
|
package org.sleuthkit.autopsy.coordinationservice;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Namespace elements for auto ingest coordination service nodes.
|
* Root node for Autopsy coordination service namespace.
|
||||||
*/
|
*/
|
||||||
final class CoordinationServiceNamespace {
|
public final class CoordinationServiceNamespace {
|
||||||
private static final String ROOT = "autopsy";
|
private static final String ROOT = "autopsy";
|
||||||
|
|
||||||
static String getRoot() {
|
public static String getRoot() {
|
||||||
return ROOT;
|
return ROOT;
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013-2014 Basis Technology Corp.
|
* Copyright 2011-2017 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -32,39 +32,29 @@ import org.sleuthkit.autopsy.core.UserPreferences;
|
|||||||
*/
|
*/
|
||||||
final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
AutopsyOptionsPanel() {
|
AutopsyOptionsPanel() {
|
||||||
initComponents();
|
initComponents();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Profiling has shown that contention for I/O resources and for the
|
||||||
|
* case database limits the number of threads that can do meaningful
|
||||||
|
* work during ingest. If Autopsy was compute-bound, adding more threads
|
||||||
|
* on machines with enough processors might help, but currently, after
|
||||||
|
* four threads, performance actually stays flat and then starts to
|
||||||
|
* degrade.
|
||||||
|
*/
|
||||||
int availableProcessors = Runtime.getRuntime().availableProcessors();
|
int availableProcessors = Runtime.getRuntime().availableProcessors();
|
||||||
Integer fileIngestThreadCountChoices[];
|
Integer fileIngestThreadCountChoices[];
|
||||||
int recommendedFileIngestThreadCount;
|
int recommendedFileIngestThreadCount;
|
||||||
if (availableProcessors >= 16) {
|
if (availableProcessors >= 6) {
|
||||||
fileIngestThreadCountChoices = new Integer[]{1, 2, 4, 6, 8, 12, 16};
|
fileIngestThreadCountChoices = new Integer[]{1, 2, 4};
|
||||||
if (availableProcessors >= 18) {
|
|
||||||
recommendedFileIngestThreadCount = 16;
|
|
||||||
} else {
|
|
||||||
recommendedFileIngestThreadCount = 12;
|
|
||||||
}
|
|
||||||
} else if (availableProcessors >= 12 && availableProcessors <= 15) {
|
|
||||||
fileIngestThreadCountChoices = new Integer[]{1, 2, 4, 6, 8, 12};
|
|
||||||
if (availableProcessors >= 14) {
|
|
||||||
recommendedFileIngestThreadCount = 12;
|
|
||||||
} else {
|
|
||||||
recommendedFileIngestThreadCount = 8;
|
|
||||||
}
|
|
||||||
} else if (availableProcessors >= 8 && availableProcessors <= 11) {
|
|
||||||
fileIngestThreadCountChoices = new Integer[]{1, 2, 4, 6, 8};
|
|
||||||
if (availableProcessors >= 10) {
|
|
||||||
recommendedFileIngestThreadCount = 8;
|
|
||||||
} else {
|
|
||||||
recommendedFileIngestThreadCount = 6;
|
|
||||||
}
|
|
||||||
} else if (availableProcessors >= 6 && availableProcessors <= 7) {
|
|
||||||
fileIngestThreadCountChoices = new Integer[]{1, 2, 4, 6};
|
|
||||||
recommendedFileIngestThreadCount = 4;
|
recommendedFileIngestThreadCount = 4;
|
||||||
} else if (availableProcessors >= 4 && availableProcessors <= 5) {
|
} else if (availableProcessors >= 4 && availableProcessors < 6) {
|
||||||
fileIngestThreadCountChoices = new Integer[]{1, 2, 4};
|
fileIngestThreadCountChoices = new Integer[]{1, 2, 4};
|
||||||
recommendedFileIngestThreadCount = 2;
|
recommendedFileIngestThreadCount = 2;
|
||||||
} else if (availableProcessors >= 2 && availableProcessors <= 3) {
|
} else if (availableProcessors >= 2 && availableProcessors < 4) {
|
||||||
fileIngestThreadCountChoices = new Integer[]{1, 2};
|
fileIngestThreadCountChoices = new Integer[]{1, 2};
|
||||||
recommendedFileIngestThreadCount = 1;
|
recommendedFileIngestThreadCount = 1;
|
||||||
} else {
|
} else {
|
||||||
@ -72,7 +62,9 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
|||||||
recommendedFileIngestThreadCount = 1;
|
recommendedFileIngestThreadCount = 1;
|
||||||
}
|
}
|
||||||
numberOfFileIngestThreadsComboBox.setModel(new DefaultComboBoxModel<>(fileIngestThreadCountChoices));
|
numberOfFileIngestThreadsComboBox.setModel(new DefaultComboBoxModel<>(fileIngestThreadCountChoices));
|
||||||
|
|
||||||
restartRequiredLabel.setText(NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.restartRequiredLabel.text", recommendedFileIngestThreadCount));
|
restartRequiredLabel.setText(NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.restartRequiredLabel.text", recommendedFileIngestThreadCount));
|
||||||
|
|
||||||
// TODO listen to changes in form fields and call controller.changed()
|
// TODO listen to changes in form fields and call controller.changed()
|
||||||
DocumentListener docListener = new DocumentListener() {
|
DocumentListener docListener = new DocumentListener() {
|
||||||
|
|
||||||
@ -92,7 +84,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.jFormattedTextFieldProcTimeOutHrs.getDocument().addDocumentListener(docListener);
|
this.jFormattedTextFieldProcTimeOutHrs.getDocument().addDocumentListener(docListener);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void load() {
|
void load() {
|
||||||
@ -138,7 +130,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
|||||||
UserPreferences.setProcessTimeOutHrs((int) timeOutHrs);
|
UserPreferences.setProcessTimeOutHrs((int) timeOutHrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean valid() {
|
boolean valid() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -77,9 +77,9 @@ public class ImageNode extends AbstractContentNode<Image> {
|
|||||||
String imgName = nameForImage(img);
|
String imgName = nameForImage(img);
|
||||||
this.setDisplayName(imgName);
|
this.setDisplayName(imgName);
|
||||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hard-drive-icon.jpg"); //NON-NLS
|
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hard-drive-icon.jpg"); //NON-NLS
|
||||||
|
|
||||||
// Listen for ingest events so that we can detect new added files (e.g. carved)
|
// Listen for ingest events so that we can detect new added files (e.g. carved)
|
||||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||||
// Listen for case events so that we can detect when case is closed
|
// Listen for case events so that we can detect when case is closed
|
||||||
Case.addPropertyChangeListener(pcl);
|
Case.addPropertyChangeListener(pcl);
|
||||||
}
|
}
|
||||||
@ -101,8 +101,6 @@ public class ImageNode extends AbstractContentNode<Image> {
|
|||||||
"ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes",})
|
"ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes",})
|
||||||
public Action[] getActions(boolean context) {
|
public Action[] getActions(boolean context) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
List<Action> actionsList = new ArrayList<Action>();
|
List<Action> actionsList = new ArrayList<Action>();
|
||||||
for (Action a : super.getActions(true)) {
|
for (Action a : super.getActions(true)) {
|
||||||
actionsList.add(a);
|
actionsList.add(a);
|
||||||
@ -215,7 +213,12 @@ public class ImageNode extends AbstractContentNode<Image> {
|
|||||||
public String getItemType() {
|
public String getItemType() {
|
||||||
return getClass().getName();
|
return getClass().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This property change listener refreshes the tree when a new file is
|
||||||
|
* carved out of this image (i.e, the image is being treated as raw bytes
|
||||||
|
* and was ingested by the RawDSProcessor).
|
||||||
|
*/
|
||||||
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
|
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
|
||||||
String eventType = evt.getPropertyName();
|
String eventType = evt.getPropertyName();
|
||||||
|
|
||||||
@ -255,6 +258,5 @@ public class ImageNode extends AbstractContentNode<Image> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,10 @@ public class VolumeNode extends AbstractContentNode<Volume> {
|
|||||||
Case.removePropertyChangeListener(pcl);
|
Case.removePropertyChangeListener(pcl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This property change listener refreshes the tree when a new file is
|
||||||
|
* carved out of the unallocated space of this volume.
|
||||||
|
*/
|
||||||
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
|
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
|
||||||
String eventType = evt.getPropertyName();
|
String eventType = evt.getPropertyName();
|
||||||
|
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
file.reference.c3p0-0.9.5.jar=release/modules/ext/c3p0-0.9.5.jar
|
file.reference.c3p0-0.9.5.jar=release/modules/ext/c3p0-0.9.5.jar
|
||||||
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
|
|
||||||
file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar
|
|
||||||
file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar
|
|
||||||
file.reference.jackson-core-2.7.0.jar=release/modules/ext/jackson-core-2.7.0.jar
|
file.reference.jackson-core-2.7.0.jar=release/modules/ext/jackson-core-2.7.0.jar
|
||||||
file.reference.LGoodDatePicker-4.3.1.jar=release/modules/ext/LGoodDatePicker-4.3.1.jar
|
file.reference.LGoodDatePicker-4.3.1.jar=release/modules/ext/LGoodDatePicker-4.3.1.jar
|
||||||
file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar
|
file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar
|
||||||
file.reference.solr-solrj-4.9.1.jar=release/modules/ext/solr-solrj-4.9.1.jar
|
file.reference.solr-solrj-4.9.1.jar=release/modules/ext/solr-solrj-4.9.1.jar
|
||||||
file.reference.tika-core-1.5.jar=release/modules/ext/tika-core-1.5.jar
|
file.reference.tika-core-1.5.jar=release/modules/ext/tika-core-1.5.jar
|
||||||
file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar
|
|
||||||
javac.source=1.8
|
javac.source=1.8
|
||||||
javac.compilerargs=-Xlint -Xlint:-serial
|
javac.compilerargs=-Xlint -Xlint:-serial
|
||||||
javadoc.reference.LGoodDatePicker-4.3.1.jar=release/modules/ext/LGoodDatePicker-4.3.1-javadoc.jar
|
javadoc.reference.LGoodDatePicker-4.3.1.jar=release/modules/ext/LGoodDatePicker-4.3.1-javadoc.jar
|
||||||
javadoc.reference.solr-solrj-4.9.1.jar=release/modules/ext/solr-solrj-4.9.1-javadoc.jar
|
javadoc.reference.solr-solrj-4.9.1.jar=release/modules/ext/solr-solrj-4.9.1-javadoc.jar
|
||||||
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
|
|
||||||
source.reference.LGoodDatePicker-4.3.1.jar=release/modules/ext/LGoodDatePicker-4.3.1-sources.jar
|
source.reference.LGoodDatePicker-4.3.1.jar=release/modules/ext/LGoodDatePicker-4.3.1-sources.jar
|
||||||
source.reference.solr-solrj-4.9.1.jar=release/modules/ext/solr-solrj-4.9.1-sources.jar
|
source.reference.solr-solrj-4.9.1.jar=release/modules/ext/solr-solrj-4.9.1-sources.jar
|
||||||
|
@ -119,14 +119,6 @@
|
|||||||
<package>org.sleuthkit.autopsy.experimental.autoingest</package>
|
<package>org.sleuthkit.autopsy.experimental.autoingest</package>
|
||||||
<package>org.sleuthkit.autopsy.experimental.configuration</package>
|
<package>org.sleuthkit.autopsy.experimental.configuration</package>
|
||||||
</public-packages>
|
</public-packages>
|
||||||
<class-path-extension>
|
|
||||||
<runtime-relative-path>ext/zookeeper-3.4.6.jar</runtime-relative-path>
|
|
||||||
<binary-origin>release/modules/ext/zookeeper-3.4.6.jar</binary-origin>
|
|
||||||
</class-path-extension>
|
|
||||||
<class-path-extension>
|
|
||||||
<runtime-relative-path>ext/curator-client-2.8.0.jar</runtime-relative-path>
|
|
||||||
<binary-origin>release/modules/ext/curator-client-2.8.0.jar</binary-origin>
|
|
||||||
</class-path-extension>
|
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/mchange-commons-java-0.2.9.jar</runtime-relative-path>
|
<runtime-relative-path>ext/mchange-commons-java-0.2.9.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/mchange-commons-java-0.2.9.jar</binary-origin>
|
<binary-origin>release/modules/ext/mchange-commons-java-0.2.9.jar</binary-origin>
|
||||||
@ -139,10 +131,6 @@
|
|||||||
<runtime-relative-path>ext/tika-core-1.5.jar</runtime-relative-path>
|
<runtime-relative-path>ext/tika-core-1.5.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/tika-core-1.5.jar</binary-origin>
|
<binary-origin>release/modules/ext/tika-core-1.5.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
|
||||||
<runtime-relative-path>ext/curator-recipes-2.8.0.jar</runtime-relative-path>
|
|
||||||
<binary-origin>release/modules/ext/curator-recipes-2.8.0.jar</binary-origin>
|
|
||||||
</class-path-extension>
|
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/jackson-core-2.7.0.jar</runtime-relative-path>
|
<runtime-relative-path>ext/jackson-core-2.7.0.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/jackson-core-2.7.0.jar</binary-origin>
|
<binary-origin>release/modules/ext/jackson-core-2.7.0.jar</binary-origin>
|
||||||
@ -151,10 +139,6 @@
|
|||||||
<runtime-relative-path>ext/c3p0-0.9.5.jar</runtime-relative-path>
|
<runtime-relative-path>ext/c3p0-0.9.5.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/c3p0-0.9.5.jar</binary-origin>
|
<binary-origin>release/modules/ext/c3p0-0.9.5.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
|
||||||
<runtime-relative-path>ext/curator-framework-2.8.0.jar</runtime-relative-path>
|
|
||||||
<binary-origin>release/modules/ext/curator-framework-2.8.0.jar</binary-origin>
|
|
||||||
</class-path-extension>
|
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/solr-solrj-4.9.1.jar</runtime-relative-path>
|
<runtime-relative-path>ext/solr-solrj-4.9.1.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/solr-solrj-4.9.1.jar</binary-origin>
|
<binary-origin>release/modules/ext/solr-solrj-4.9.1.jar</binary-origin>
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.experimental.autoingest;
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationServiceNamespace;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
@ -29,9 +30,9 @@ import java.text.SimpleDateFormat;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.Lock;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.CoordinationServiceException;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
@ -430,7 +431,7 @@ final class AutoIngestJobLogger {
|
|||||||
* log file.
|
* log file.
|
||||||
*/
|
*/
|
||||||
private void log(MessageCategory category, String message) throws AutoIngestJobLoggerException, InterruptedException {
|
private void log(MessageCategory category, String message) throws AutoIngestJobLoggerException, InterruptedException {
|
||||||
try (Lock lock = CoordinationService.getInstance(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, getLogPath(caseDirectoryPath).toString(), LOCK_TIME_OUT, LOCK_TIME_OUT_UNIT)) {
|
try (Lock lock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, getLogPath(caseDirectoryPath).toString(), LOCK_TIME_OUT, LOCK_TIME_OUT_UNIT)) {
|
||||||
if (null != lock) {
|
if (null != lock) {
|
||||||
File logFile = getLogPath(caseDirectoryPath).toFile();
|
File logFile = getLogPath(caseDirectoryPath).toFile();
|
||||||
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(logFile, logFile.exists())), true)) {
|
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(logFile, logFile.exists())), true)) {
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.experimental.autoingest;
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationServiceNamespace;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
@ -98,9 +99,9 @@ import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
|
|||||||
import org.sleuthkit.autopsy.ingest.IngestJob;
|
import org.sleuthkit.autopsy.ingest.IngestJob;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.CoordinationServiceException;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.Lock;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock;
|
||||||
import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration;
|
import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration;
|
||||||
import org.apache.solr.client.solrj.impl.HttpSolrServer;
|
import org.apache.solr.client.solrj.impl.HttpSolrServer;
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
@ -231,7 +232,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
void startUp() throws AutoIngestManagerStartupException {
|
void startUp() throws AutoIngestManagerStartupException {
|
||||||
SYS_LOGGER.log(Level.INFO, "Auto ingest starting");
|
SYS_LOGGER.log(Level.INFO, "Auto ingest starting");
|
||||||
try {
|
try {
|
||||||
coordinationService = CoordinationService.getInstance(CoordinationServiceNamespace.getRoot());
|
coordinationService = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot());
|
||||||
} catch (CoordinationServiceException ex) {
|
} catch (CoordinationServiceException ex) {
|
||||||
throw new AutoIngestManagerStartupException("Failed to get coordination service", ex);
|
throw new AutoIngestManagerStartupException("Failed to get coordination service", ex);
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.experimental.autoingest;
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
import java.beans.PropertyChangeEvent;
|
|
||||||
import java.beans.PropertyChangeListener;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -37,8 +34,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
|||||||
import org.sleuthkit.autopsy.casemodule.CaseActionException;
|
import org.sleuthkit.autopsy.casemodule.CaseActionException;
|
||||||
import org.sleuthkit.autopsy.casemodule.CaseNewAction;
|
import org.sleuthkit.autopsy.casemodule.CaseNewAction;
|
||||||
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences;
|
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.CoordinationServiceException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles opening, locking, and unlocking cases in review mode. Instances of
|
* Handles opening, locking, and unlocking cases in review mode. Instances of
|
||||||
@ -47,7 +43,7 @@ import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationServic
|
|||||||
* dispatch thread (EDT). Because of the tight coupling to the UI, exception
|
* dispatch thread (EDT). Because of the tight coupling to the UI, exception
|
||||||
* messages are deliberately user-friendly.
|
* messages are deliberately user-friendly.
|
||||||
*/
|
*/
|
||||||
final class ReviewModeCaseManager implements PropertyChangeListener {
|
final class ReviewModeCaseManager {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Provides uniform exceptions with user-friendly error messages.
|
* Provides uniform exceptions with user-friendly error messages.
|
||||||
@ -77,14 +73,7 @@ final class ReviewModeCaseManager implements PropertyChangeListener {
|
|||||||
*/
|
*/
|
||||||
synchronized static ReviewModeCaseManager getInstance() {
|
synchronized static ReviewModeCaseManager getInstance() {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
/*
|
|
||||||
* Two stage construction is used here to avoid allowing "this"
|
|
||||||
* reference to escape from the constructor via registering as an
|
|
||||||
* PropertyChangeListener. This is to ensure that a partially
|
|
||||||
* constructed manager is not published to other threads.
|
|
||||||
*/
|
|
||||||
instance = new ReviewModeCaseManager();
|
instance = new ReviewModeCaseManager();
|
||||||
Case.addPropertyChangeListener(instance);
|
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
@ -149,17 +138,7 @@ final class ReviewModeCaseManager implements PropertyChangeListener {
|
|||||||
* requirement
|
* requirement
|
||||||
*/
|
*/
|
||||||
synchronized void openCaseInEDT(Path caseMetadataFilePath) throws ReviewModeCaseManagerException {
|
synchronized void openCaseInEDT(Path caseMetadataFilePath) throws ReviewModeCaseManagerException {
|
||||||
Path caseFolderPath = caseMetadataFilePath.getParent();
|
|
||||||
try {
|
try {
|
||||||
/*
|
|
||||||
* Acquire a lock on the case folder. If the lock cannot be
|
|
||||||
* acquired, the case cannot be opened.
|
|
||||||
*/
|
|
||||||
currentCaseLock = CoordinationService.getInstance(CoordinationServiceNamespace.getRoot()).tryGetSharedLock(CoordinationService.CategoryNode.CASES, caseFolderPath.toString());
|
|
||||||
if (null == currentCaseLock) {
|
|
||||||
throw new ReviewModeCaseManagerException("Could not get shared access to multi-user case folder");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the case.
|
* Open the case.
|
||||||
*/
|
*/
|
||||||
@ -177,64 +156,8 @@ final class ReviewModeCaseManager implements PropertyChangeListener {
|
|||||||
CallableSystemAction.get(AddImageAction.class).setEnabled(false);
|
CallableSystemAction.get(AddImageAction.class).setEnabled(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (CoordinationServiceException | ReviewModeCaseManagerException | CaseActionException ex) {
|
} catch (CaseActionException ex) {
|
||||||
/*
|
throw new ReviewModeCaseManagerException(String.format("Could not open the case (%s), contract administrator", ex.getMessage()), ex);
|
||||||
* Release the coordination service lock on the case folder.
|
|
||||||
*/
|
|
||||||
try {
|
|
||||||
if (currentCaseLock != null) {
|
|
||||||
currentCaseLock.release();
|
|
||||||
currentCaseLock = null;
|
|
||||||
}
|
|
||||||
} catch (CoordinationService.CoordinationServiceException exx) {
|
|
||||||
logger.log(Level.SEVERE, String.format("Error deleting legacy LOCKED state file for case at %s", caseFolderPath), exx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ex instanceof CoordinationServiceException) {
|
|
||||||
throw new ReviewModeCaseManagerException("Could not get access to the case folder from the coordination service, contact administrator", ex);
|
|
||||||
} else if (ex instanceof IOException) {
|
|
||||||
throw new ReviewModeCaseManagerException("Could not write to the case folder, contact adminstrator", ex);
|
|
||||||
} else if (ex instanceof CaseActionException) {
|
|
||||||
/*
|
|
||||||
* CaseActionExceptions have user friendly error messages.
|
|
||||||
*/
|
|
||||||
throw new ReviewModeCaseManagerException(String.format("Could not open the case (%s), contract administrator", ex.getMessage()), ex);
|
|
||||||
} else if (ex instanceof ReviewModeCaseManagerException) {
|
|
||||||
throw (ReviewModeCaseManagerException) ex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
|
||||||
if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())
|
|
||||||
&& null != evt.getOldValue()
|
|
||||||
&& null == evt.getNewValue()) {
|
|
||||||
/*
|
|
||||||
* When a case is closed, release the coordination service lock on
|
|
||||||
* the case folder. This must be done in the EDT because it was
|
|
||||||
* acquired in the EDT via openCase().
|
|
||||||
*/
|
|
||||||
if (null != currentCaseLock) {
|
|
||||||
try {
|
|
||||||
SwingUtilities.invokeAndWait(() -> {
|
|
||||||
try {
|
|
||||||
currentCaseLock.release();
|
|
||||||
currentCaseLock = null;
|
|
||||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
|
||||||
logger.log(Level.SEVERE, String.format("Failed to release the coordination service lock with path %s", currentCaseLock.getNodePath()), ex);
|
|
||||||
currentCaseLock = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (InterruptedException | InvocationTargetException ex) {
|
|
||||||
logger.log(Level.SEVERE, String.format("Failed to release the coordination service lock with path %s", currentCaseLock.getNodePath()), ex);
|
|
||||||
currentCaseLock = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -49,9 +49,9 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
import org.sleuthkit.autopsy.core.ServicesMonitor;
|
import org.sleuthkit.autopsy.core.ServicesMonitor;
|
||||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
|
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
|
||||||
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestSettingsPanel.UpdateConfigSwingWorker;
|
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestSettingsPanel.UpdateConfigSwingWorker;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.Lock;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock;
|
||||||
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.CoordinationServiceException;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A utility class for loading and saving shared configuration data
|
* A utility class for loading and saving shared configuration data
|
||||||
@ -160,7 +160,7 @@ public class SharedConfiguration {
|
|||||||
|
|
||||||
File remoteFolder = getSharedFolder();
|
File remoteFolder = getSharedFolder();
|
||||||
|
|
||||||
try (Lock writeLock = CoordinationService.getInstance(LOCK_ROOT).tryGetExclusiveLock(CoordinationService.CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) {
|
try (Lock writeLock = CoordinationService.getServiceForNamespace(LOCK_ROOT).tryGetExclusiveLock(CoordinationService.CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) {
|
||||||
if (writeLock == null) {
|
if (writeLock == null) {
|
||||||
logger.log(Level.INFO, String.format("Failed to lock %s - another node is currently uploading or downloading configuration", remoteFolder.getAbsolutePath()));
|
logger.log(Level.INFO, String.format("Failed to lock %s - another node is currently uploading or downloading configuration", remoteFolder.getAbsolutePath()));
|
||||||
return SharedConfigResult.LOCKED;
|
return SharedConfigResult.LOCKED;
|
||||||
@ -230,7 +230,7 @@ public class SharedConfiguration {
|
|||||||
|
|
||||||
File remoteFolder = getSharedFolder();
|
File remoteFolder = getSharedFolder();
|
||||||
|
|
||||||
try (Lock readLock = CoordinationService.getInstance(LOCK_ROOT).tryGetSharedLock(CoordinationService.CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) {
|
try (Lock readLock = CoordinationService.getServiceForNamespace(LOCK_ROOT).tryGetSharedLock(CoordinationService.CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) {
|
||||||
if (readLock == null) {
|
if (readLock == null) {
|
||||||
return SharedConfigResult.LOCKED;
|
return SharedConfigResult.LOCKED;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ class TikaTextExtractor extends FileTextExtractor {
|
|||||||
|
|
||||||
private static final List<String> TIKA_SUPPORTED_TYPES
|
private static final List<String> TIKA_SUPPORTED_TYPES
|
||||||
= new Tika().getParser().getSupportedTypes(new ParseContext())
|
= new Tika().getParser().getSupportedTypes(new ParseContext())
|
||||||
.parallelStream()
|
.stream()
|
||||||
.map(mt -> mt.getType() + "/" + mt.getSubtype())
|
.map(mt -> mt.getType() + "/" + mt.getSubtype())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user