From b2c674e6b302123e48065eabd7fb61defbc79fea Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 16 Apr 2024 17:58:55 -0400 Subject: [PATCH 01/19] set tsk temp directory --- .../org/sleuthkit/autopsy/core/Installer.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/core/Installer.java b/Core/src/org/sleuthkit/autopsy/core/Installer.java index 0cbafe987e..7ef296e0c2 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/core/Installer.java @@ -66,11 +66,17 @@ public class Installer extends ModuleInstall { private static final long serialVersionUID = 1L; + private static final String JAVA_TEMP = "java.io.tmpdir"; + private static final String AUTOPSY_TEMP_DIR = "autopsy_temp"; + private static final String TSK_TEMP = "tsk.tmpdir"; + private final List packageInstallers; private static final Logger logger = Logger.getLogger(Installer.class.getName()); private static volatile boolean javaFxInit = false; static { + setTskTemp(); + loadDynLibraries(); // This call was moved from MediaViewImagePanel so that it is @@ -80,6 +86,24 @@ public class Installer extends ModuleInstall { // This will cause OpenCvLoader to load its library instead of OpenCvLoader.openCvIsLoaded(); } + + /** + * Set TSK temp directory to de-conflict with other programs using TSK libs. + */ + private static void setTskTemp() { + try { + String curTemp = System.getProperty(JAVA_TEMP, ""); + String tskTemp = curTemp + (curTemp.endsWith(File.separator) ? "" : File.separator) + AUTOPSY_TEMP_DIR; + System.setProperty(TSK_TEMP, tskTemp); + File tskTempDir = new File(tskTemp); + tskTempDir.mkdirs(); + if (!tskTempDir.isDirectory()) { + throw new IOException("Unable to create directory at " + tskTemp); + } + } catch (Exception ex) { + logger.log(Level.WARNING, "There was an error setting up tsk temp directory", ex); + } + } private static void loadDynLibraries() { /* From e188f4644e8a807a4af3b8dd1a6269957e922706 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 18 Apr 2024 21:15:43 -0400 Subject: [PATCH 02/19] first draft of locking resources in autopsy --- .../sleuthkit/autopsy/casemodule/Case.java | 118 +++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 3f9c440122..4681083400 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -31,6 +31,8 @@ import java.awt.event.ActionListener; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; import java.lang.reflect.InvocationTargetException; import java.nio.file.InvalidPathException; import java.nio.file.Path; @@ -181,6 +183,7 @@ public class Case { private static final String CASE_RESOURCES_THREAD_NAME = "%s-manage-case-resources"; private static final String NO_NODE_ERROR_MSG_FRAGMENT = "KeeperErrorCode = NoNode"; private static final String CT_PROVIDER_PREFIX = "CTStandardContentProvider_"; + private static final String LOCK_FILE_NAME = "lock"; private static final Logger logger = Logger.getLogger(Case.class.getName()); private static final AutopsyEventPublisher eventPublisher = new AutopsyEventPublisher(); private static final Object caseActionSerializationLock = new Object(); @@ -197,6 +200,10 @@ public class Case { private CollaborationMonitor collaborationMonitor; private Services caseServices; + private RansomAccessFile lockFileRaf = null; + private FileChannel lockFileChannel = null; + private FileLock lockFileLock = null; + private volatile boolean hasDataSource = false; private volatile boolean hasData = false; @@ -769,6 +776,57 @@ public class Case { || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"") || caseName.contains("<") || caseName.contains(">") || caseName.contains("|")); } + + + /** + * Try to acquire a lock to the lock file in the case directory. + * @param caseDir The case directory that the autopsy.db is in. + * @throws IllegalAccessException + * @throws IOException + */ + private void tryAcquireFileLock(String caseDir) throws ConcurrentDbAccessException, IOException { + File lockFile = new File(caseDir, LOCK_FILE_NAME); + lockFile.getParentFile().mkdirs(); + lockFileRaf = new RandomAccessFile(lockFile, "rw"); + lockFileChannel = lockFileRaf.getChannel(); + lockFileLock = lockFileChannel.tryLock(); + if (lockFileLock == null) { + String conflictingApplication = null; + try { + StringBuffer buffer = new StringBuffer(); + while (lockFileRaf.getFilePointer() < lockFileRaf.length()) { + buffer.append(lockFileRaf.readLine() + System.lineSeparator()); + } + conflictingApplication = buffer.toString(); + } finally { + throw new ConcurrentDbAccessException("Unable to acquire lock on " + lockFile, conflictingApplication); + } + } + } + + /** + * An exception thrown if the database is currently in use. + */ + private static class ConcurrentDbAccessException extends Exception { + private final String conflictingApplicationName; + + /** + * Constructor. + * @param message The exception message. + * @param conflictingApplicationName The conflicting application name (or null if unknown). + */ + public ConcurrentDbAccessException(String message, String conflictingApplicationName) { + super(message); + this.conflictingApplicationName = conflictingApplicationName; + } + + /** + * @return The conflicting application name (or null if unknown). + */ + public String getConflictingApplicationName() { + return conflictingApplicationName; + } + } /** * Creates a new case and makes it the current case. @@ -2725,7 +2783,11 @@ public class Case { "Case.progressMessage.creatingCaseDatabase=Creating case database...", "# {0} - exception message", "Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.", "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.", - "# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}." + "# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}.", + "Case_createCaseDatabase_fileLock_ioException=An error occurred while trying to get an exclusive lock on the case.", + "# {0} - appplicationName", + "Case_createCaseDatabase_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy.", + "Case_createCaseDatabase_fileLock_concurrentAccessException_defaultApp=another application" }) private void createCaseDatabase(ProgressIndicator progressIndicator) throws CaseActionException { progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase()); @@ -2736,6 +2798,16 @@ public class Case { * with a standard name, physically located in the case * directory. */ + try { + tryAcquireFileLock(metadata.getCaseDirectory()); + } catch (IOException ex) { + throw new CaseActionException(Bundle.Case_createCaseDatabase_fileLock_ioException(), ex); + } catch (ConcurrentDbAccessException ex) { + throw new CaseActionException(Bundle.Case_createCaseDatabase_fileLock_concurrentAccessException( + StringUtils.defaultIfBlank(ex.getConflictingApplicationName(), + Bundle.Case_createCaseDatabase_fileLock_concurrentAccessException_defaultApp()) + ), ex); + } caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString()); metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME); } else { @@ -2772,7 +2844,11 @@ public class Case { "# {0} - exception message", "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.", "# {0} - exception message", "Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.", "Case.exceptionMessage.contentProviderCouldNotBeFound=Content provider was specified for the case but could not be loaded.", - "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User." + "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User.", + "Case_openCaseDataBase_fileLock_ioException=An error occurred while trying to get an exclusive lock on the case.", + "# {0} - appplicationName", + "Case_openCaseDataBase_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy.", + "Case_openCaseDataBase_fileLock_concurrentAccessException_defaultApp=another application" }) private void openCaseDataBase(ProgressIndicator progressIndicator) throws CaseActionException { progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase()); @@ -2788,6 +2864,16 @@ public class Case { } if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { + try { + tryAcquireFileLock(metadata.getCaseDirectory()); + } catch (IOException ex) { + throw new CaseActionException(Bundle.Case_openCaseDataBase_fileLock_ioException(), ex); + } catch (ConcurrentDbAccessException ex) { + throw new CaseActionException(Bundle.Case_openCaseDataBase_fileLock_concurrentAccessException( + StringUtils.defaultIfBlank(ex.getConflictingApplicationName(), + Bundle.Case_openCaseDataBase_fileLock_concurrentAccessException_defaultApp()) + ), ex); + } caseDb = SleuthkitCase.openCase(metadata.getCaseDatabasePath(), contentProvider); } else if (UserPreferences.getIsMultiUserModeEnabled()) { caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory(), contentProvider); @@ -3098,6 +3184,34 @@ public class Case { collaborationMonitor.shutdown(); } eventPublisher.closeRemoteEventChannel(); + } + + if (this.lockFileLock != null) { + try { + this.lockFileLock.close(); + this.lockFileLock = null; + } catch (Exception ex) { + logger.log(Level.WARNING, "There was an error closing the lock file lock", ex); + } + } + + if (this.lockFileChannel != null) { + try { + this.lockFileChannel.close(); + this.lockFileChannel = null; + } catch (Exception ex) { + logger.log(Level.WARNING, "There was an error closing the lock file channel", ex); + } + } + + + if (this.lockFileRaf != null) { + try { + this.lockFileRaf.close(); + this.lockFileRaf = null; + } catch (Exception ex) { + logger.log(Level.WARNING, "There was an error closing the lock file random access file", ex); + } } /* From 7b66af30a5d15e6c6cd1a0adb2d69e8b69241523 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 18 Apr 2024 21:19:18 -0400 Subject: [PATCH 03/19] fix --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 4681083400..cccd1dc856 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -801,6 +801,9 @@ public class Case { } finally { throw new ConcurrentDbAccessException("Unable to acquire lock on " + lockFile, conflictingApplication); } + } else { + lockFileRaf.setLength(0); + lockFileRaf.writeChars(APP_NAME); } } From 8ae984831bd76d14834124dc9df9d42b1301e6cf Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 19 Apr 2024 15:05:34 -0400 Subject: [PATCH 04/19] updates and fixes --- .../casemodule/Bundle.properties-MERGED | 8 + .../sleuthkit/autopsy/casemodule/Case.java | 106 ++---------- .../autopsy/casemodule/TskLockResources.java | 162 ++++++++++++++++++ 3 files changed, 186 insertions(+), 90 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/TskLockResources.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 1f9719688f..5dfaba3f43 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -110,6 +110,14 @@ Case_caseType_singleUser=Single-user case Case_checkImagePaths_exceptionOccurred=An exception occurred while checking if image paths are present # {0} - paths Case_checkImagePaths_noPaths=The following images had no associated paths: {0} +# {0} - appplicationName +Case_createCaseDatabase_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy. +Case_createCaseDatabase_fileLock_concurrentAccessException_defaultApp=another application +Case_createCaseDatabase_fileLock_ioException=An error occurred while trying to get an exclusive lock on the case. +# {0} - appplicationName +Case_openCaseDataBase_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy. +Case_openCaseDataBase_fileLock_concurrentAccessException_defaultApp=another application +Case_openCaseDataBase_fileLock_ioException=An error occurred while trying to get an exclusive lock on the case. CaseDetailsPanel.casePanel.border.title=Case CaseDetailsPanel.examinerLabel.text=Name: CaseDetailsPanel.examinerPanel.border.title=Examiner diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index cccd1dc856..3e0a84e93f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -32,8 +32,8 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.IOException; -import java.io.RandomAccessFile; import java.lang.reflect.InvocationTargetException; +import java.nio.channels.OverlappingFileLockException; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; @@ -78,6 +78,7 @@ import org.sleuthkit.autopsy.actions.OpenOutputFolderAction; import org.sleuthkit.autopsy.appservices.AutopsyService; import org.sleuthkit.autopsy.appservices.AutopsyService.CaseContext; import org.sleuthkit.autopsy.casemodule.CaseMetadata.CaseMetadataException; +import org.sleuthkit.autopsy.casemodule.TskLockResources.ConcurrentDbAccessException; import org.sleuthkit.autopsy.datasourcesummary.ui.DataSourceSummaryAction; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceEvent; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceFailedEvent; @@ -183,7 +184,6 @@ public class Case { private static final String CASE_RESOURCES_THREAD_NAME = "%s-manage-case-resources"; private static final String NO_NODE_ERROR_MSG_FRAGMENT = "KeeperErrorCode = NoNode"; private static final String CT_PROVIDER_PREFIX = "CTStandardContentProvider_"; - private static final String LOCK_FILE_NAME = "lock"; private static final Logger logger = Logger.getLogger(Case.class.getName()); private static final AutopsyEventPublisher eventPublisher = new AutopsyEventPublisher(); private static final Object caseActionSerializationLock = new Object(); @@ -199,11 +199,8 @@ public class Case { private final SleuthkitEventListener sleuthkitEventListener; private CollaborationMonitor collaborationMonitor; private Services caseServices; - - private RansomAccessFile lockFileRaf = null; - private FileChannel lockFileChannel = null; - private FileLock lockFileLock = null; - + + private TskLockResources tskLockResources = null; private volatile boolean hasDataSource = false; private volatile boolean hasData = false; @@ -217,6 +214,7 @@ public class Case { mainFrame = WindowManager.getDefault().getMainWindow(); }); } + /** * An enumeration of case types. @@ -777,59 +775,6 @@ public class Case { || caseName.contains("<") || caseName.contains(">") || caseName.contains("|")); } - - /** - * Try to acquire a lock to the lock file in the case directory. - * @param caseDir The case directory that the autopsy.db is in. - * @throws IllegalAccessException - * @throws IOException - */ - private void tryAcquireFileLock(String caseDir) throws ConcurrentDbAccessException, IOException { - File lockFile = new File(caseDir, LOCK_FILE_NAME); - lockFile.getParentFile().mkdirs(); - lockFileRaf = new RandomAccessFile(lockFile, "rw"); - lockFileChannel = lockFileRaf.getChannel(); - lockFileLock = lockFileChannel.tryLock(); - if (lockFileLock == null) { - String conflictingApplication = null; - try { - StringBuffer buffer = new StringBuffer(); - while (lockFileRaf.getFilePointer() < lockFileRaf.length()) { - buffer.append(lockFileRaf.readLine() + System.lineSeparator()); - } - conflictingApplication = buffer.toString(); - } finally { - throw new ConcurrentDbAccessException("Unable to acquire lock on " + lockFile, conflictingApplication); - } - } else { - lockFileRaf.setLength(0); - lockFileRaf.writeChars(APP_NAME); - } - } - - /** - * An exception thrown if the database is currently in use. - */ - private static class ConcurrentDbAccessException extends Exception { - private final String conflictingApplicationName; - - /** - * Constructor. - * @param message The exception message. - * @param conflictingApplicationName The conflicting application name (or null if unknown). - */ - public ConcurrentDbAccessException(String message, String conflictingApplicationName) { - super(message); - this.conflictingApplicationName = conflictingApplicationName; - } - - /** - * @return The conflicting application name (or null if unknown). - */ - public String getConflictingApplicationName() { - return conflictingApplicationName; - } - } /** * Creates a new case and makes it the current case. @@ -2802,13 +2747,13 @@ public class Case { * directory. */ try { - tryAcquireFileLock(metadata.getCaseDirectory()); - } catch (IOException ex) { - throw new CaseActionException(Bundle.Case_createCaseDatabase_fileLock_ioException(), ex); + this.tskLockResources = TskLockResources.tryAcquireFileLock(metadata.getCaseDirectory(), UserPreferences.getAppName()); + } catch (IOException | OverlappingFileLockException ex) { + throw new CaseActionException(Bundle.Case_openCaseDataBase_fileLock_ioException(), ex); } catch (ConcurrentDbAccessException ex) { - throw new CaseActionException(Bundle.Case_createCaseDatabase_fileLock_concurrentAccessException( + throw new CaseActionException(Bundle.Case_openCaseDataBase_fileLock_concurrentAccessException( StringUtils.defaultIfBlank(ex.getConflictingApplicationName(), - Bundle.Case_createCaseDatabase_fileLock_concurrentAccessException_defaultApp()) + Bundle.Case_openCaseDataBase_fileLock_concurrentAccessException_defaultApp()) ), ex); } caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString()); @@ -2868,8 +2813,8 @@ public class Case { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { try { - tryAcquireFileLock(metadata.getCaseDirectory()); - } catch (IOException ex) { + this.tskLockResources = TskLockResources.tryAcquireFileLock(metadata.getCaseDirectory(), UserPreferences.getAppName()); + } catch (IOException | OverlappingFileLockException ex) { throw new CaseActionException(Bundle.Case_openCaseDataBase_fileLock_ioException(), ex); } catch (ConcurrentDbAccessException ex) { throw new CaseActionException(Bundle.Case_openCaseDataBase_fileLock_concurrentAccessException( @@ -3189,34 +3134,15 @@ public class Case { eventPublisher.closeRemoteEventChannel(); } - if (this.lockFileLock != null) { + if (this.tskLockResources != null) { try { - this.lockFileLock.close(); - this.lockFileLock = null; + this.tskLockResources.close(); + this.tskLockResources = null; } catch (Exception ex) { - logger.log(Level.WARNING, "There was an error closing the lock file lock", ex); + logger.log(Level.WARNING, "There was an error closing the TSK case lock resources", ex); } } - if (this.lockFileChannel != null) { - try { - this.lockFileChannel.close(); - this.lockFileChannel = null; - } catch (Exception ex) { - logger.log(Level.WARNING, "There was an error closing the lock file channel", ex); - } - } - - - if (this.lockFileRaf != null) { - try { - this.lockFileRaf.close(); - this.lockFileRaf = null; - } catch (Exception ex) { - logger.log(Level.WARNING, "There was an error closing the lock file random access file", ex); - } - } - /* * Allow all registered application services providers to close * resources related to the case. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/TskLockResources.java b/Core/src/org/sleuthkit/autopsy/casemodule/TskLockResources.java new file mode 100644 index 0000000000..78c36cd984 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/TskLockResources.java @@ -0,0 +1,162 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2024 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import org.apache.commons.lang3.StringUtils; + +/** + * The resources associated with the file lock for the TSK database. + */ +class TskLockResources implements AutoCloseable { + + private static final String LOCK_FILE_NAME = "lock"; + + private File lockFile = null; + private RandomAccessFile lockFileRaf = null; + private FileChannel lockFileChannel = null; + private FileLock lockFileLock = null; + + /** + * Constructor. + * + * @param lockFile The lock file File reference. + * @param lockFileRef The lock file random access file reference. + * @param lockFileChannel The lock file file channel. + * @param lockFileLock The lock file file lock. + */ + TskLockResources(File lockFile, RandomAccessFile lockFileRaf, FileChannel lockFileChannel, FileLock lockFileLock) { + this.lockFile = lockFile; + this.lockFileRaf = lockFileRaf; + this.lockFileChannel = lockFileChannel; + this.lockFileLock = lockFileLock; + } + + /** + * Try to acquire a lock to the lock file in the case directory. + * + * @param caseDir The case directory that the autopsy.db is in. + * @return The lock file resources to be closed. + * @throws IllegalAccessException + * @throws IOException + */ + static TskLockResources tryAcquireFileLock(String caseDir, String applicationName) throws ConcurrentDbAccessException, IOException, OverlappingFileLockException { + // get the lock file path + File lockFile = new File(caseDir, LOCK_FILE_NAME); + // make directories leading up to that + lockFile.getParentFile().mkdirs(); + + // if the lock file exists + if (lockFile.isFile() && !lockFile.canWrite()) { + // get the random access file as read only + RandomAccessFile lockFileRaf = new RandomAccessFile(lockFile, "r"); + throw ConcurrentDbAccessException.createForFile(lockFile.getAbsolutePath(), lockFileRaf); + } else { + RandomAccessFile lockFileRaf = new RandomAccessFile(lockFile, "rw"); + FileChannel lockFileChannel = lockFileRaf.getChannel(); + FileLock lockFileLock = lockFileChannel == null + ? null + : lockFileChannel.tryLock(1024L, 1L, false); + + if (lockFileLock != null) { + lockFileRaf.setLength(0); + lockFileRaf.writeChars(applicationName); + return new TskLockResources(lockFile, lockFileRaf, lockFileChannel, lockFileLock); + } else { + throw ConcurrentDbAccessException.createForFile(lockFile.getAbsolutePath(), lockFileRaf); + } + } + } + + @Override + public void close() throws Exception { + // close lock file resources in reverse acquisition order + if (this.lockFileLock != null) { + this.lockFileLock.close(); + this.lockFileLock = null; + } + + if (this.lockFileChannel != null) { + this.lockFileChannel.close(); + this.lockFileChannel = null; + } + + if (this.lockFileRaf != null) { + this.lockFileRaf.close(); + this.lockFileRaf = null; + } + + if (this.lockFile != null) { + this.lockFile.delete(); + this.lockFile = null; + } + } + + /** + * An exception thrown if the database is currently in use. + */ + static class ConcurrentDbAccessException extends Exception { + + private final String conflictingApplicationName; + + /** + * Creates a ConcurrentDbAccessException from the lock file path and the + * random access file of that path whose contents are the application + * name. + * + * @param lockFilePath The lock file path. + * @param lockFileRaf The lock file random access file. + * @return The exception + * @throws IOException + */ + static ConcurrentDbAccessException createForFile(String lockFilePath, RandomAccessFile lockFileRaf) throws IOException { + StringBuffer buffer = new StringBuffer(); + while (lockFileRaf.getFilePointer() < lockFileRaf.length()) { + buffer.append(lockFileRaf.readLine() + System.lineSeparator()); + } + String conflictingApplication = buffer.toString().trim(); + String message = "Unable to acquire lock on " + lockFilePath + "." + (StringUtils.isNotBlank(conflictingApplication) ? (" Database is already open in " + conflictingApplication + ".") : ""); + return new ConcurrentDbAccessException(message, conflictingApplication); + } + + /** + * Constructor. + * + * @param message The exception message. + * @param conflictingApplicationName The conflicting application name + * (or null if unknown). + */ + ConcurrentDbAccessException(String message, String conflictingApplicationName) { + super(message); + this.conflictingApplicationName = conflictingApplicationName; + } + + /** + * @return The conflicting application name (or null if unknown). + */ + public String getConflictingApplicationName() { + return conflictingApplicationName; + } + } +} From bed99ecbf61a47a1a4b1bdbba751b0d6ecdbeb60 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 22 Apr 2024 13:48:51 -0400 Subject: [PATCH 05/19] fixes --- .../casemodule/Bundle.properties-MERGED | 9 +- .../sleuthkit/autopsy/casemodule/Case.java | 95 +++++----- .../autopsy/casemodule/TskLockResources.java | 162 ------------------ 3 files changed, 45 insertions(+), 221 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/TskLockResources.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 5dfaba3f43..14465bba03 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -111,13 +111,8 @@ Case_checkImagePaths_exceptionOccurred=An exception occurred while checking if i # {0} - paths Case_checkImagePaths_noPaths=The following images had no associated paths: {0} # {0} - appplicationName -Case_createCaseDatabase_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy. -Case_createCaseDatabase_fileLock_concurrentAccessException_defaultApp=another application -Case_createCaseDatabase_fileLock_ioException=An error occurred while trying to get an exclusive lock on the case. -# {0} - appplicationName -Case_openCaseDataBase_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy. -Case_openCaseDataBase_fileLock_concurrentAccessException_defaultApp=another application -Case_openCaseDataBase_fileLock_ioException=An error occurred while trying to get an exclusive lock on the case. +Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy. +Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException_defaultApp=another application CaseDetailsPanel.casePanel.border.title=Case CaseDetailsPanel.examinerLabel.text=Name: CaseDetailsPanel.examinerPanel.border.title=Examiner diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 3e0a84e93f..50dcca3f6f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -31,9 +31,7 @@ import java.awt.event.ActionListener; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.nio.channels.OverlappingFileLockException; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; @@ -78,7 +76,6 @@ import org.sleuthkit.autopsy.actions.OpenOutputFolderAction; import org.sleuthkit.autopsy.appservices.AutopsyService; import org.sleuthkit.autopsy.appservices.AutopsyService.CaseContext; import org.sleuthkit.autopsy.casemodule.CaseMetadata.CaseMetadataException; -import org.sleuthkit.autopsy.casemodule.TskLockResources.ConcurrentDbAccessException; import org.sleuthkit.autopsy.datasourcesummary.ui.DataSourceSummaryAction; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceEvent; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceFailedEvent; @@ -148,6 +145,7 @@ import org.sleuthkit.autopsy.timeline.OpenTimelineAction; import org.sleuthkit.autopsy.timeline.events.TimelineEventAddedEvent; import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.CaseDbConnectionInfo; +import org.sleuthkit.datamodel.ConcurrentDbAccessException; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentStreamProvider; import org.sleuthkit.datamodel.ContentTag; @@ -199,8 +197,7 @@ public class Case { private final SleuthkitEventListener sleuthkitEventListener; private CollaborationMonitor collaborationMonitor; private Services caseServices; - - private TskLockResources tskLockResources = null; + private volatile boolean hasDataSource = false; private volatile boolean hasData = false; @@ -214,7 +211,6 @@ public class Case { mainFrame = WindowManager.getDefault().getMainWindow(); }); } - /** * An enumeration of case types. @@ -774,7 +770,6 @@ public class Case { || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"") || caseName.contains("<") || caseName.contains(">") || caseName.contains("|")); } - /** * Creates a new case and makes it the current case. @@ -2731,11 +2726,7 @@ public class Case { "Case.progressMessage.creatingCaseDatabase=Creating case database...", "# {0} - exception message", "Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.", "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.", - "# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}.", - "Case_createCaseDatabase_fileLock_ioException=An error occurred while trying to get an exclusive lock on the case.", - "# {0} - appplicationName", - "Case_createCaseDatabase_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy.", - "Case_createCaseDatabase_fileLock_concurrentAccessException_defaultApp=another application" + "# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}." }) private void createCaseDatabase(ProgressIndicator progressIndicator) throws CaseActionException { progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase()); @@ -2746,17 +2737,7 @@ public class Case { * with a standard name, physically located in the case * directory. */ - try { - this.tskLockResources = TskLockResources.tryAcquireFileLock(metadata.getCaseDirectory(), UserPreferences.getAppName()); - } catch (IOException | OverlappingFileLockException ex) { - throw new CaseActionException(Bundle.Case_openCaseDataBase_fileLock_ioException(), ex); - } catch (ConcurrentDbAccessException ex) { - throw new CaseActionException(Bundle.Case_openCaseDataBase_fileLock_concurrentAccessException( - StringUtils.defaultIfBlank(ex.getConflictingApplicationName(), - Bundle.Case_openCaseDataBase_fileLock_concurrentAccessException_defaultApp()) - ), ex); - } - caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString()); + caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME, APP_NAME).toString()); metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME); } else { /* @@ -2768,6 +2749,7 @@ public class Case { metadata.setCaseDatabaseName(caseDb.getDatabaseName()); } } catch (TskCoreException ex) { + throwIfConcurrentDbAccessException(ex); throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex); } catch (UserPreferencesException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex); @@ -2792,17 +2774,13 @@ public class Case { "# {0} - exception message", "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.", "# {0} - exception message", "Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.", "Case.exceptionMessage.contentProviderCouldNotBeFound=Content provider was specified for the case but could not be loaded.", - "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User.", - "Case_openCaseDataBase_fileLock_ioException=An error occurred while trying to get an exclusive lock on the case.", - "# {0} - appplicationName", - "Case_openCaseDataBase_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy.", - "Case_openCaseDataBase_fileLock_concurrentAccessException_defaultApp=another application" + "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User." }) private void openCaseDataBase(ProgressIndicator progressIndicator) throws CaseActionException { progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase()); try { String databaseName = metadata.getCaseDatabaseName(); - + ContentStreamProvider contentProvider = loadContentProvider(metadata.getContentProviderName()); if (StringUtils.isNotBlank(metadata.getContentProviderName()) && contentProvider == null) { if (metadata.getContentProviderName().trim().toUpperCase().startsWith(CT_PROVIDER_PREFIX.toUpperCase())) { @@ -2810,19 +2788,9 @@ public class Case { } throw new CaseActionException(Bundle.Case_exceptionMessage_contentProviderCouldNotBeFound()); } - + if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { - try { - this.tskLockResources = TskLockResources.tryAcquireFileLock(metadata.getCaseDirectory(), UserPreferences.getAppName()); - } catch (IOException | OverlappingFileLockException ex) { - throw new CaseActionException(Bundle.Case_openCaseDataBase_fileLock_ioException(), ex); - } catch (ConcurrentDbAccessException ex) { - throw new CaseActionException(Bundle.Case_openCaseDataBase_fileLock_concurrentAccessException( - StringUtils.defaultIfBlank(ex.getConflictingApplicationName(), - Bundle.Case_openCaseDataBase_fileLock_concurrentAccessException_defaultApp()) - ), ex); - } - caseDb = SleuthkitCase.openCase(metadata.getCaseDatabasePath(), contentProvider); + caseDb = SleuthkitCase.openCase(metadata.getCaseDatabasePath(), contentProvider, APP_NAME); } else if (UserPreferences.getIsMultiUserModeEnabled()) { caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory(), contentProvider); } else { @@ -2834,7 +2802,39 @@ public class Case { } catch (UserPreferencesException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex); } catch (TskCoreException ex) { - throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex); + throwIfConcurrentDbAccessException(ex); + throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex); + } + } + + + /** + * Throws a CaseActionException if the exception or any nested exception is a ConcurrentDbAccessException (max depth of 10) + * @param ex The exception. + * @throws CaseActionException Thrown if there is a concurrent db access exception. + */ + @Messages({ + "# {0} - appplicationName", + "Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException=The case is open in {0}. Please close it before attempting to open it in Autopsy.", + "Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException_defaultApp=another application" + }) + private void throwIfConcurrentDbAccessException(Exception ex) throws CaseActionException { + ConcurrentDbAccessException concurrentEx = null; + Throwable curEx = ex; + // max depth search for a concurrent db access exception will be 10 + for (int i = 0; i < 10; i++) { + if (curEx instanceof ConcurrentDbAccessException foundEx) { + concurrentEx = foundEx; + break; + } + curEx = curEx.getCause(); + } + + if (concurrentEx != null) { + throw new CaseActionException(Bundle.Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException( + StringUtils.defaultIfBlank(concurrentEx.getConflictingApplicationName(), + Bundle.Case_throwIfConcurrentDbAccessException_fileLock_concurrentAccessException_defaultApp()) + ), concurrentEx); } } @@ -3132,17 +3132,8 @@ public class Case { collaborationMonitor.shutdown(); } eventPublisher.closeRemoteEventChannel(); - } - - if (this.tskLockResources != null) { - try { - this.tskLockResources.close(); - this.tskLockResources = null; - } catch (Exception ex) { - logger.log(Level.WARNING, "There was an error closing the TSK case lock resources", ex); - } } - + /* * Allow all registered application services providers to close * resources related to the case. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/TskLockResources.java b/Core/src/org/sleuthkit/autopsy/casemodule/TskLockResources.java deleted file mode 100644 index 78c36cd984..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/TskLockResources.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2024 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.casemodule; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.channels.OverlappingFileLockException; -import org.apache.commons.lang3.StringUtils; - -/** - * The resources associated with the file lock for the TSK database. - */ -class TskLockResources implements AutoCloseable { - - private static final String LOCK_FILE_NAME = "lock"; - - private File lockFile = null; - private RandomAccessFile lockFileRaf = null; - private FileChannel lockFileChannel = null; - private FileLock lockFileLock = null; - - /** - * Constructor. - * - * @param lockFile The lock file File reference. - * @param lockFileRef The lock file random access file reference. - * @param lockFileChannel The lock file file channel. - * @param lockFileLock The lock file file lock. - */ - TskLockResources(File lockFile, RandomAccessFile lockFileRaf, FileChannel lockFileChannel, FileLock lockFileLock) { - this.lockFile = lockFile; - this.lockFileRaf = lockFileRaf; - this.lockFileChannel = lockFileChannel; - this.lockFileLock = lockFileLock; - } - - /** - * Try to acquire a lock to the lock file in the case directory. - * - * @param caseDir The case directory that the autopsy.db is in. - * @return The lock file resources to be closed. - * @throws IllegalAccessException - * @throws IOException - */ - static TskLockResources tryAcquireFileLock(String caseDir, String applicationName) throws ConcurrentDbAccessException, IOException, OverlappingFileLockException { - // get the lock file path - File lockFile = new File(caseDir, LOCK_FILE_NAME); - // make directories leading up to that - lockFile.getParentFile().mkdirs(); - - // if the lock file exists - if (lockFile.isFile() && !lockFile.canWrite()) { - // get the random access file as read only - RandomAccessFile lockFileRaf = new RandomAccessFile(lockFile, "r"); - throw ConcurrentDbAccessException.createForFile(lockFile.getAbsolutePath(), lockFileRaf); - } else { - RandomAccessFile lockFileRaf = new RandomAccessFile(lockFile, "rw"); - FileChannel lockFileChannel = lockFileRaf.getChannel(); - FileLock lockFileLock = lockFileChannel == null - ? null - : lockFileChannel.tryLock(1024L, 1L, false); - - if (lockFileLock != null) { - lockFileRaf.setLength(0); - lockFileRaf.writeChars(applicationName); - return new TskLockResources(lockFile, lockFileRaf, lockFileChannel, lockFileLock); - } else { - throw ConcurrentDbAccessException.createForFile(lockFile.getAbsolutePath(), lockFileRaf); - } - } - } - - @Override - public void close() throws Exception { - // close lock file resources in reverse acquisition order - if (this.lockFileLock != null) { - this.lockFileLock.close(); - this.lockFileLock = null; - } - - if (this.lockFileChannel != null) { - this.lockFileChannel.close(); - this.lockFileChannel = null; - } - - if (this.lockFileRaf != null) { - this.lockFileRaf.close(); - this.lockFileRaf = null; - } - - if (this.lockFile != null) { - this.lockFile.delete(); - this.lockFile = null; - } - } - - /** - * An exception thrown if the database is currently in use. - */ - static class ConcurrentDbAccessException extends Exception { - - private final String conflictingApplicationName; - - /** - * Creates a ConcurrentDbAccessException from the lock file path and the - * random access file of that path whose contents are the application - * name. - * - * @param lockFilePath The lock file path. - * @param lockFileRaf The lock file random access file. - * @return The exception - * @throws IOException - */ - static ConcurrentDbAccessException createForFile(String lockFilePath, RandomAccessFile lockFileRaf) throws IOException { - StringBuffer buffer = new StringBuffer(); - while (lockFileRaf.getFilePointer() < lockFileRaf.length()) { - buffer.append(lockFileRaf.readLine() + System.lineSeparator()); - } - String conflictingApplication = buffer.toString().trim(); - String message = "Unable to acquire lock on " + lockFilePath + "." + (StringUtils.isNotBlank(conflictingApplication) ? (" Database is already open in " + conflictingApplication + ".") : ""); - return new ConcurrentDbAccessException(message, conflictingApplication); - } - - /** - * Constructor. - * - * @param message The exception message. - * @param conflictingApplicationName The conflicting application name - * (or null if unknown). - */ - ConcurrentDbAccessException(String message, String conflictingApplicationName) { - super(message); - this.conflictingApplicationName = conflictingApplicationName; - } - - /** - * @return The conflicting application name (or null if unknown). - */ - public String getConflictingApplicationName() { - return conflictingApplicationName; - } - } -} From 8fa375f172928c85958d78c936848471366a790c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 23 Apr 2024 11:28:19 -0400 Subject: [PATCH 06/19] updates for tsk lib lock --- .../autopsy/datamodel/Installer.java | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java b/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java index 323424efdf..297c93ece3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.datamodel; import java.awt.Component; +import java.io.IOException; import java.util.logging.Level; import org.openide.util.NbBundle; @@ -26,6 +27,8 @@ import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JOptionPane; import org.openide.LifecycleManager; import org.openide.modules.ModuleInstall; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datamodel.TskLibLock.LockState; import org.sleuthkit.datamodel.SleuthkitJNI; /** @@ -46,8 +49,14 @@ public class Installer extends ModuleInstall { super(); } + @Messages({ + "Installer_validate_tskLibLock_title=Error calling Sleuth Kit library", + "Installer_validate_tskLibLock_description=It appears that an older version of an application that opens The Sleuth Kit databases is currently running on your system. Close this application before opening Autopsy, and consider upgrading in order to have a better user experience." + }) @Override public void validate() throws IllegalStateException { + + /* * The NetBeans API specifies that a module should throw an * IllegalStateException if it can't be initalized, but NetBeans doesn't @@ -59,7 +68,17 @@ public class Installer extends ModuleInstall { // Check that the the Sleuth Kit JNI is working by getting the Sleuth Kit version number Logger logger = Logger.getLogger(Installer.class.getName()); + try { + try { + TskLibLock libLock = TskLibLock.acquireLibLock(); + if (libLock != null && libLock.getLockState() == LockState.HELD_BY_OLD) { + throw new OldAppLockException("A lock on the libtsk_jni lib is already held by an old application. " + (libLock.getLibTskJniFile() != null ? libLock.getLibTskJniFile().getAbsolutePath() : "")); + } + } catch (IOException ex) { + logger.log(Level.SEVERE, "An error occurred while acquiring the TSK lib lock", ex); + } + String skVersion = SleuthkitJNI.getVersion(); if (skVersion == null) { @@ -71,15 +90,25 @@ public class Installer extends ModuleInstall { } } catch (Exception | UnsatisfiedLinkError e) { - logger.log(Level.SEVERE, "Error calling Sleuth Kit library (test call failed)", e); //NON-NLS - logger.log(Level.SEVERE, "Is Autopsy or Cyber Triage already running?)", e); //NON-NLS - + // Normal error box log handler won't be loaded yet, so show error here. final Component parentComponent = null; // Use default window frame. - final String message = NbBundle.getMessage(this.getClass(), "Installer.tskLibErr.msg", e.toString()); - final String title = NbBundle.getMessage(this.getClass(), "Installer.tskLibErr.err"); final int messageType = JOptionPane.ERROR_MESSAGE; + final String message; + final String title; + + if (e instanceof OldAppLockException ex) { + logger.log(Level.SEVERE, "An older application already holds a lock on the libtsk_jni lib", ex); + message = Bundle.Installer_validate_tskLibLock_description(); + title = Bundle.Installer_validate_tskLibLock_title(); + } else { + logger.log(Level.SEVERE, "Error calling Sleuth Kit library (test call failed)", e); //NON-NLS + logger.log(Level.SEVERE, "Is Autopsy or Cyber Triage already running?)", e); //NON-NLS + message = NbBundle.getMessage(this.getClass(), "Installer.tskLibErr.msg", e.toString()); + title = NbBundle.getMessage(this.getClass(), "Installer.tskLibErr.err"); + } + JOptionPane.showMessageDialog( parentComponent, message, @@ -91,4 +120,15 @@ public class Installer extends ModuleInstall { } } + + /** + * An exception when an older application (Autopsy + */ + static class OldAppLockException extends Exception { + + public OldAppLockException(String message) { + super(message); + } + + } } From 86fbba96590ff0bb8ea2be01574c885397318074 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 23 Apr 2024 11:29:56 -0400 Subject: [PATCH 07/19] updates for tsk lib lock --- .../autopsy/datamodel/TskLibLock.java | 283 ++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java b/Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java new file mode 100644 index 0000000000..0ca02cc40a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java @@ -0,0 +1,283 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2024 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.datamodel; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.Arrays; + +/** + * Creates a file lock at the old location (Autopsy LTE 4.21.0) of TSK + * libraries, and does so in such a way that newer versions can determine that + * it is a new application holding the lock. + */ +class TskLibLock implements AutoCloseable { + + private static final String DLL_EXT = "dll"; + private static final String DYLIB_EXT = "dylib"; + private static final String SO_EXT = "so"; + + private static final String LIBTSK_JNI_LIB = "libtsk_jni"; + + private static final String LIB_FILE_LOCK_TEXT = "lib_lock"; + + private static final String TMP_DIR_KEY = "java.io.tmpdir"; + private static final String USER_NAME_KEY = "user.name"; + + private static final byte[] UTF8_BOM = {(byte) 0XEF, (byte) 0xBB, (byte) 0XBF}; + + private static TskLibLock libLock = null; + + /** + * Attempts to acquire a file lock on the libtsk_jni library at the old + * location if there currently is no lock. + * + * @return The result of attempting to obtain the lock including the result + * type (lock held by new application, lock held by old application, lock + * acquired) and any resources if the lock is acquired. + * @throws IOException + */ + static TskLibLock acquireLibLock() throws IOException { + if (libLock == null) { + libLock = getLibLock(); + } + return libLock; + } + + /** + * Removes any library file lock present. + * + * @throws Exception If there is an error closing the lock resources. + */ + static void removeLibLock() throws Exception { + if (libLock != null) { + libLock.close(); + } + } + + /** + * Gets the lib lock for the libtsk_jni library at the old location. + * + * @return The result of attempting to obtain the lock including the result + * type (lock held by new application, lock held by old application, lock + * acquired) and any resources if the lock is acquired. + * @throws IOException + */ + private static TskLibLock getLibLock() throws IOException { + // TODO error handling cleanup + + String libExt = getLibExtension(); + File libTskJniFile = Paths.get( + System.getProperty(TMP_DIR_KEY, ""), + MessageFormat.format("{0}_{1}.{2}", + LIBTSK_JNI_LIB, + System.getProperty(USER_NAME_KEY, ""), + libExt)).toFile(); + + // if the lock file exists + if (libTskJniFile.isFile() && !libTskJniFile.canWrite()) { + // get the random access file as read only + try (RandomAccessFile lockFileRaf = new RandomAccessFile(libTskJniFile, "r")) { + LockState lockState = isNewLock(lockFileRaf) + ? LockState.HELD_BY_NEW + : LockState.HELD_BY_OLD; + + return new TskLibLock(lockState, libTskJniFile, lockFileRaf, null, null); + } + } else { + // make directories leading up to that + libTskJniFile.getParentFile().mkdirs(); + + // get file access to the file + RandomAccessFile lockFileRaf = new RandomAccessFile(libTskJniFile, "rw"); + FileChannel lockFileChannel = lockFileRaf.getChannel(); + FileLock lockFileLock = lockFileChannel == null + ? null + : lockFileChannel.tryLock(1024L, 1L, false); + + if (lockFileLock != null) { + lockFileRaf.setLength(0); + lockFileRaf.write(UTF8_BOM); + lockFileRaf.writeChars(LIB_FILE_LOCK_TEXT); + + return new TskLibLock(LockState.ACQUIRED, libTskJniFile, lockFileRaf, lockFileChannel, lockFileLock); + } else { + if (lockFileChannel != null) { + lockFileChannel.close(); + } + + if (lockFileRaf != null) { + lockFileRaf.close(); + } + + LockState lockState = isNewLock(lockFileRaf) + ? LockState.HELD_BY_NEW + : LockState.HELD_BY_OLD; + + return new TskLibLock(lockState, libTskJniFile, lockFileRaf, lockFileChannel, null); + } + } + } + + /** + * Returns true if the file is locked by a newer application (Autopsy GT + * 4.21.0). + * + * @param libRaf The random access file. + * @return True if lock held by a newer application. + * @throws IOException + */ + private static boolean isNewLock(RandomAccessFile libRaf) throws IOException { + libRaf.seek(0); + byte[] startFileArr = new byte[UTF8_BOM.length]; + int read = libRaf.read(startFileArr); + return read == startFileArr.length && Arrays.equals(UTF8_BOM, startFileArr); + + } + + private File libTskJniFile; + private RandomAccessFile lockFileRaf; + private FileChannel lockFileChannel; + private FileLock lockFileLock; + private LockState lockState; + + /** + * Constructor + * + * @param lockState The lock state. + * @param lockFile The lock file or null. + * @param lockFileRaf The lock file random access file or null. + * @param lockFileChannel The lock file channel or null. + * @param lockFileLock The lock file lock or null. + */ + private TskLibLock( + LockState lockState, + File lockFile, + RandomAccessFile lockFileRaf, + FileChannel lockFileChannel, + FileLock lockFileLock) { + + this.libTskJniFile = lockFile; + this.lockFileRaf = lockFileRaf; + this.lockFileChannel = lockFileChannel; + this.lockFileLock = lockFileLock; + this.lockState = lockState; + } + + /** + * @return The lock state result of attempting to lock the file. + */ + LockState getLockState() { + return lockState; + } + + /** + * @return The file path for the lib tsk jni file. + */ + File getLibTskJniFile() { + return libTskJniFile; + } + + @Override + public void close() throws Exception { + // close lock file resources in reverse acquisition order + if (this.lockFileLock != null) { + this.lockFileLock.close(); + this.lockFileLock = null; + } + + if (this.lockFileChannel != null) { + this.lockFileChannel.close(); + this.lockFileChannel = null; + } + + if (this.lockFileRaf != null) { + this.lockFileRaf.close(); + this.lockFileRaf = null; + } + + if (this.libTskJniFile != null) { + this.libTskJniFile.delete(); + this.libTskJniFile = null; + } + } + + /** + * @return The library extension based on the operating system. + */ + private static String getLibExtension() { + if (isWindows()) { + return DLL_EXT; + } else if (isMac()) { + return DYLIB_EXT; + } else { + return SO_EXT; + } + } + + /** + * Is the platform Windows? + * + * @return + */ + private static boolean isWindows() { + return System.getProperty("os.name", "").toLowerCase().contains("windows"); + } + + /** + * Is the platform Mac? + * + * @return + */ + private static boolean isMac() { + return System.getProperty("os.name", "").toLowerCase().contains("mac"); + } + + /** + * Is the platform Linux? + * + * @return + */ + private static boolean isLinux() { + return System.getProperty("os.name", "").toLowerCase().contains("linux"); + } + + /** + * The result of attempting to lock the libtsk_jni lib file. + */ + enum LockState { + /** + * If a lock on the library is held by a version of Autopsy LTE 4.21.0 + */ + HELD_BY_OLD, + /** + * If a lock on the library is held by a version of Autopsy GT 4.21.0 + */ + HELD_BY_NEW, + /** + * If the lock has been acquired. + */ + ACQUIRED + } +} From f5082ead119d04c552041ced79381242ce5d5784 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 23 Apr 2024 15:22:50 -0400 Subject: [PATCH 08/19] updates and fixes --- .../org/sleuthkit/autopsy/core/Installer.java | 10 ++-- .../datamodel/Bundle.properties-MERGED | 2 + .../autopsy/datamodel/Installer.java | 36 +++++++++--- .../autopsy/datamodel/TskLibLock.java | 56 ++++++++++--------- 4 files changed, 65 insertions(+), 39 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/core/Installer.java b/Core/src/org/sleuthkit/autopsy/core/Installer.java index 7ef296e0c2..e8b1c79196 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/core/Installer.java @@ -575,11 +575,6 @@ public class Installer extends ModuleInstall { logger.log(Level.INFO, "close()"); //NON-NLS - //exit JavaFx plat - if (javaFxInit) { - Platform.exit(); - } - for (ModuleInstall mi : packageInstallers) { logger.log(Level.INFO, "{0} close()", mi.getClass().getName()); //NON-NLS try { @@ -591,5 +586,10 @@ public class Installer extends ModuleInstall { for (Handler h : logger.getHandlers()) { h.close(); //must call h.close or a .LCK file will remain. } + + //exit JavaFx plat + if (javaFxInit) { + Platform.exit(); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index 4d958cf39b..26df51a9ba 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -169,6 +169,8 @@ ImageNode.createSheet.type.displayName=Type ImageNode.createSheet.type.name=Type ImageNode.createSheet.type.text=Image ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes +Installer_validate_tskLibLock_description=It appears that an older version of an application that opens The Sleuth Kit databases is currently running on your system.
Close this application before opening Autopsy, and consider upgrading in order to have a better user experience. +Installer_validate_tskLibLock_title=Error calling Sleuth Kit library KeyValueNode.menuItemText.viewFileInDir=View Source File in Directory KeywordHits.createNodeForKey.accessTime.desc=Access Time KeywordHits.createNodeForKey.accessTime.displayName=Access Time diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java b/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java index 297c93ece3..74485e09ef 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.datamodel; import java.awt.Component; -import java.io.IOException; import java.util.logging.Level; import org.openide.util.NbBundle; @@ -51,7 +50,7 @@ public class Installer extends ModuleInstall { @Messages({ "Installer_validate_tskLibLock_title=Error calling Sleuth Kit library", - "Installer_validate_tskLibLock_description=It appears that an older version of an application that opens The Sleuth Kit databases is currently running on your system. Close this application before opening Autopsy, and consider upgrading in order to have a better user experience." + "Installer_validate_tskLibLock_description=It appears that an older version of an application that opens The Sleuth Kit databases is currently running on your system.
Close this application before opening Autopsy, and consider upgrading in order to have a better user experience." }) @Override public void validate() throws IllegalStateException { @@ -70,13 +69,9 @@ public class Installer extends ModuleInstall { Logger logger = Logger.getLogger(Installer.class.getName()); try { - try { - TskLibLock libLock = TskLibLock.acquireLibLock(); - if (libLock != null && libLock.getLockState() == LockState.HELD_BY_OLD) { - throw new OldAppLockException("A lock on the libtsk_jni lib is already held by an old application. " + (libLock.getLibTskJniFile() != null ? libLock.getLibTskJniFile().getAbsolutePath() : "")); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, "An error occurred while acquiring the TSK lib lock", ex); + TskLibLock libLock = TskLibLock.acquireLibLock(); + if (libLock != null && libLock.getLockState() == LockState.HELD_BY_OLD) { + throw new OldAppLockException("A lock on the libtsk_jni lib is already held by an old application. " + (libLock.getLibTskJniFile() != null ? libLock.getLibTskJniFile().getAbsolutePath() : "")); } String skVersion = SleuthkitJNI.getVersion(); @@ -120,7 +115,30 @@ public class Installer extends ModuleInstall { } } + + @Override + public void close() { + try { + TskLibLock.removeLibLock(); + } catch (Exception ex) { + Logger logger = Logger.getLogger(Installer.class.getName()); + logger.log(Level.WARNING, "There was an error removing the TSK lib lock.", ex); + } + } + + @Override + public void uninstalled() { + try { + TskLibLock.removeLibLock(); + } catch (Exception ex) { + Logger logger = Logger.getLogger(Installer.class.getName()); + logger.log(Level.WARNING, "There was an error removing the TSK lib lock.", ex); + } + } + + + /** * An exception when an older application (Autopsy */ diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java b/Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java index 0ca02cc40a..484e2a81de 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java @@ -26,6 +26,7 @@ import java.nio.channels.FileLock; import java.nio.file.Paths; import java.text.MessageFormat; import java.util.Arrays; +import java.util.logging.Level; /** * Creates a file lock at the old location (Autopsy LTE 4.21.0) of TSK @@ -58,7 +59,7 @@ class TskLibLock implements AutoCloseable { * acquired) and any resources if the lock is acquired. * @throws IOException */ - static TskLibLock acquireLibLock() throws IOException { + static TskLibLock acquireLibLock() { if (libLock == null) { libLock = getLibLock(); } @@ -84,7 +85,7 @@ class TskLibLock implements AutoCloseable { * acquired) and any resources if the lock is acquired. * @throws IOException */ - private static TskLibLock getLibLock() throws IOException { + private static TskLibLock getLibLock() { // TODO error handling cleanup String libExt = getLibExtension(); @@ -104,38 +105,43 @@ class TskLibLock implements AutoCloseable { : LockState.HELD_BY_OLD; return new TskLibLock(lockState, libTskJniFile, lockFileRaf, null, null); + } catch (IOException ex) { + // if there is an error getting read only access, then it is the old application dll + java.util.logging.Logger.getLogger(TskLibLock.class.getCanonicalName()).log(Level.WARNING, "An error occurred while acquiring the TSK lib lock", ex); + return new TskLibLock(LockState.HELD_BY_OLD, libTskJniFile, null, null, null); } } else { // make directories leading up to that libTskJniFile.getParentFile().mkdirs(); // get file access to the file - RandomAccessFile lockFileRaf = new RandomAccessFile(libTskJniFile, "rw"); - FileChannel lockFileChannel = lockFileRaf.getChannel(); - FileLock lockFileLock = lockFileChannel == null - ? null - : lockFileChannel.tryLock(1024L, 1L, false); + RandomAccessFile lockFileRaf = null; + FileChannel lockFileChannel = null; + FileLock lockFileLock = null; + try { + lockFileRaf = new RandomAccessFile(libTskJniFile, "rw"); + lockFileChannel = lockFileRaf.getChannel(); + lockFileLock = lockFileChannel == null + ? null + : lockFileChannel.tryLock(1024L, 1L, false); - if (lockFileLock != null) { - lockFileRaf.setLength(0); - lockFileRaf.write(UTF8_BOM); - lockFileRaf.writeChars(LIB_FILE_LOCK_TEXT); + if (lockFileLock != null) { + lockFileRaf.setLength(0); + lockFileRaf.write(UTF8_BOM); + lockFileRaf.writeChars(LIB_FILE_LOCK_TEXT); - return new TskLibLock(LockState.ACQUIRED, libTskJniFile, lockFileRaf, lockFileChannel, lockFileLock); - } else { - if (lockFileChannel != null) { - lockFileChannel.close(); + return new TskLibLock(LockState.ACQUIRED, libTskJniFile, lockFileRaf, lockFileChannel, lockFileLock); + } else { + LockState lockState = isNewLock(lockFileRaf) + ? LockState.HELD_BY_NEW + : LockState.HELD_BY_OLD; + + return new TskLibLock(lockState, libTskJniFile, lockFileRaf, lockFileChannel, null); } - - if (lockFileRaf != null) { - lockFileRaf.close(); - } - - LockState lockState = isNewLock(lockFileRaf) - ? LockState.HELD_BY_NEW - : LockState.HELD_BY_OLD; - - return new TskLibLock(lockState, libTskJniFile, lockFileRaf, lockFileChannel, null); + } catch (IOException ex) { + // if there is an error getting read only access, then it is the old application dll + java.util.logging.Logger.getLogger(TskLibLock.class.getCanonicalName()).log(Level.WARNING, "An error occurred while acquiring the TSK lib lock", ex); + return new TskLibLock(LockState.HELD_BY_OLD, libTskJniFile, lockFileRaf, lockFileChannel, lockFileLock); } } } From 67242e28e76784177a06fc96f9596d325de5faec Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 24 Apr 2024 12:11:58 -0400 Subject: [PATCH 09/19] message update --- .../org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED | 2 +- Core/src/org/sleuthkit/autopsy/datamodel/Installer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index 26df51a9ba..fb2079cccd 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -169,7 +169,7 @@ ImageNode.createSheet.type.displayName=Type ImageNode.createSheet.type.name=Type ImageNode.createSheet.type.text=Image ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes -Installer_validate_tskLibLock_description=It appears that an older version of an application that opens The Sleuth Kit databases is currently running on your system.
Close this application before opening Autopsy, and consider upgrading in order to have a better user experience. +Installer_validate_tskLibLock_description=Another forensics application is running that uses The Sleuth Kit.
You must close that application before launching Autopsy.
If that application is Cyber Triage, then you should upgrade it so that it can run at the same time as Autopsy. Installer_validate_tskLibLock_title=Error calling Sleuth Kit library KeyValueNode.menuItemText.viewFileInDir=View Source File in Directory KeywordHits.createNodeForKey.accessTime.desc=Access Time diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java b/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java index 74485e09ef..6906951493 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java @@ -50,7 +50,7 @@ public class Installer extends ModuleInstall { @Messages({ "Installer_validate_tskLibLock_title=Error calling Sleuth Kit library", - "Installer_validate_tskLibLock_description=It appears that an older version of an application that opens The Sleuth Kit databases is currently running on your system.
Close this application before opening Autopsy, and consider upgrading in order to have a better user experience." + "Installer_validate_tskLibLock_description=Another forensics application is running that uses The Sleuth Kit.
You must close that application before launching Autopsy.
If that application is Cyber Triage, then you should upgrade it so that it can run at the same time as Autopsy." }) @Override public void validate() throws IllegalStateException { From 25236a90b073fcb8ae4ec4702bddbd86c8694236 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 24 Apr 2024 20:32:22 -0400 Subject: [PATCH 10/19] library lock update --- .../autopsy/datamodel/Installer.java | 9 +- .../autopsy/datamodel/TskLibLock.java | 289 ------------------ 2 files changed, 5 insertions(+), 293 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java b/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java index 6906951493..311250eb88 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Installer.java @@ -27,7 +27,8 @@ import javax.swing.JOptionPane; import org.openide.LifecycleManager; import org.openide.modules.ModuleInstall; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.datamodel.TskLibLock.LockState; +import org.sleuthkit.datamodel.LibraryLock; +import org.sleuthkit.datamodel.LibraryLock.LockState; import org.sleuthkit.datamodel.SleuthkitJNI; /** @@ -69,7 +70,7 @@ public class Installer extends ModuleInstall { Logger logger = Logger.getLogger(Installer.class.getName()); try { - TskLibLock libLock = TskLibLock.acquireLibLock(); + LibraryLock libLock = LibraryLock.acquireLibLock(); if (libLock != null && libLock.getLockState() == LockState.HELD_BY_OLD) { throw new OldAppLockException("A lock on the libtsk_jni lib is already held by an old application. " + (libLock.getLibTskJniFile() != null ? libLock.getLibTskJniFile().getAbsolutePath() : "")); } @@ -119,7 +120,7 @@ public class Installer extends ModuleInstall { @Override public void close() { try { - TskLibLock.removeLibLock(); + LibraryLock.removeLibLock(); } catch (Exception ex) { Logger logger = Logger.getLogger(Installer.class.getName()); logger.log(Level.WARNING, "There was an error removing the TSK lib lock.", ex); @@ -129,7 +130,7 @@ public class Installer extends ModuleInstall { @Override public void uninstalled() { try { - TskLibLock.removeLibLock(); + LibraryLock.removeLibLock(); } catch (Exception ex) { Logger logger = Logger.getLogger(Installer.class.getName()); logger.log(Level.WARNING, "There was an error removing the TSK lib lock.", ex); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java b/Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java deleted file mode 100644 index 484e2a81de..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/TskLibLock.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2024 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datamodel; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.file.Paths; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.logging.Level; - -/** - * Creates a file lock at the old location (Autopsy LTE 4.21.0) of TSK - * libraries, and does so in such a way that newer versions can determine that - * it is a new application holding the lock. - */ -class TskLibLock implements AutoCloseable { - - private static final String DLL_EXT = "dll"; - private static final String DYLIB_EXT = "dylib"; - private static final String SO_EXT = "so"; - - private static final String LIBTSK_JNI_LIB = "libtsk_jni"; - - private static final String LIB_FILE_LOCK_TEXT = "lib_lock"; - - private static final String TMP_DIR_KEY = "java.io.tmpdir"; - private static final String USER_NAME_KEY = "user.name"; - - private static final byte[] UTF8_BOM = {(byte) 0XEF, (byte) 0xBB, (byte) 0XBF}; - - private static TskLibLock libLock = null; - - /** - * Attempts to acquire a file lock on the libtsk_jni library at the old - * location if there currently is no lock. - * - * @return The result of attempting to obtain the lock including the result - * type (lock held by new application, lock held by old application, lock - * acquired) and any resources if the lock is acquired. - * @throws IOException - */ - static TskLibLock acquireLibLock() { - if (libLock == null) { - libLock = getLibLock(); - } - return libLock; - } - - /** - * Removes any library file lock present. - * - * @throws Exception If there is an error closing the lock resources. - */ - static void removeLibLock() throws Exception { - if (libLock != null) { - libLock.close(); - } - } - - /** - * Gets the lib lock for the libtsk_jni library at the old location. - * - * @return The result of attempting to obtain the lock including the result - * type (lock held by new application, lock held by old application, lock - * acquired) and any resources if the lock is acquired. - * @throws IOException - */ - private static TskLibLock getLibLock() { - // TODO error handling cleanup - - String libExt = getLibExtension(); - File libTskJniFile = Paths.get( - System.getProperty(TMP_DIR_KEY, ""), - MessageFormat.format("{0}_{1}.{2}", - LIBTSK_JNI_LIB, - System.getProperty(USER_NAME_KEY, ""), - libExt)).toFile(); - - // if the lock file exists - if (libTskJniFile.isFile() && !libTskJniFile.canWrite()) { - // get the random access file as read only - try (RandomAccessFile lockFileRaf = new RandomAccessFile(libTskJniFile, "r")) { - LockState lockState = isNewLock(lockFileRaf) - ? LockState.HELD_BY_NEW - : LockState.HELD_BY_OLD; - - return new TskLibLock(lockState, libTskJniFile, lockFileRaf, null, null); - } catch (IOException ex) { - // if there is an error getting read only access, then it is the old application dll - java.util.logging.Logger.getLogger(TskLibLock.class.getCanonicalName()).log(Level.WARNING, "An error occurred while acquiring the TSK lib lock", ex); - return new TskLibLock(LockState.HELD_BY_OLD, libTskJniFile, null, null, null); - } - } else { - // make directories leading up to that - libTskJniFile.getParentFile().mkdirs(); - - // get file access to the file - RandomAccessFile lockFileRaf = null; - FileChannel lockFileChannel = null; - FileLock lockFileLock = null; - try { - lockFileRaf = new RandomAccessFile(libTskJniFile, "rw"); - lockFileChannel = lockFileRaf.getChannel(); - lockFileLock = lockFileChannel == null - ? null - : lockFileChannel.tryLock(1024L, 1L, false); - - if (lockFileLock != null) { - lockFileRaf.setLength(0); - lockFileRaf.write(UTF8_BOM); - lockFileRaf.writeChars(LIB_FILE_LOCK_TEXT); - - return new TskLibLock(LockState.ACQUIRED, libTskJniFile, lockFileRaf, lockFileChannel, lockFileLock); - } else { - LockState lockState = isNewLock(lockFileRaf) - ? LockState.HELD_BY_NEW - : LockState.HELD_BY_OLD; - - return new TskLibLock(lockState, libTskJniFile, lockFileRaf, lockFileChannel, null); - } - } catch (IOException ex) { - // if there is an error getting read only access, then it is the old application dll - java.util.logging.Logger.getLogger(TskLibLock.class.getCanonicalName()).log(Level.WARNING, "An error occurred while acquiring the TSK lib lock", ex); - return new TskLibLock(LockState.HELD_BY_OLD, libTskJniFile, lockFileRaf, lockFileChannel, lockFileLock); - } - } - } - - /** - * Returns true if the file is locked by a newer application (Autopsy GT - * 4.21.0). - * - * @param libRaf The random access file. - * @return True if lock held by a newer application. - * @throws IOException - */ - private static boolean isNewLock(RandomAccessFile libRaf) throws IOException { - libRaf.seek(0); - byte[] startFileArr = new byte[UTF8_BOM.length]; - int read = libRaf.read(startFileArr); - return read == startFileArr.length && Arrays.equals(UTF8_BOM, startFileArr); - - } - - private File libTskJniFile; - private RandomAccessFile lockFileRaf; - private FileChannel lockFileChannel; - private FileLock lockFileLock; - private LockState lockState; - - /** - * Constructor - * - * @param lockState The lock state. - * @param lockFile The lock file or null. - * @param lockFileRaf The lock file random access file or null. - * @param lockFileChannel The lock file channel or null. - * @param lockFileLock The lock file lock or null. - */ - private TskLibLock( - LockState lockState, - File lockFile, - RandomAccessFile lockFileRaf, - FileChannel lockFileChannel, - FileLock lockFileLock) { - - this.libTskJniFile = lockFile; - this.lockFileRaf = lockFileRaf; - this.lockFileChannel = lockFileChannel; - this.lockFileLock = lockFileLock; - this.lockState = lockState; - } - - /** - * @return The lock state result of attempting to lock the file. - */ - LockState getLockState() { - return lockState; - } - - /** - * @return The file path for the lib tsk jni file. - */ - File getLibTskJniFile() { - return libTskJniFile; - } - - @Override - public void close() throws Exception { - // close lock file resources in reverse acquisition order - if (this.lockFileLock != null) { - this.lockFileLock.close(); - this.lockFileLock = null; - } - - if (this.lockFileChannel != null) { - this.lockFileChannel.close(); - this.lockFileChannel = null; - } - - if (this.lockFileRaf != null) { - this.lockFileRaf.close(); - this.lockFileRaf = null; - } - - if (this.libTskJniFile != null) { - this.libTskJniFile.delete(); - this.libTskJniFile = null; - } - } - - /** - * @return The library extension based on the operating system. - */ - private static String getLibExtension() { - if (isWindows()) { - return DLL_EXT; - } else if (isMac()) { - return DYLIB_EXT; - } else { - return SO_EXT; - } - } - - /** - * Is the platform Windows? - * - * @return - */ - private static boolean isWindows() { - return System.getProperty("os.name", "").toLowerCase().contains("windows"); - } - - /** - * Is the platform Mac? - * - * @return - */ - private static boolean isMac() { - return System.getProperty("os.name", "").toLowerCase().contains("mac"); - } - - /** - * Is the platform Linux? - * - * @return - */ - private static boolean isLinux() { - return System.getProperty("os.name", "").toLowerCase().contains("linux"); - } - - /** - * The result of attempting to lock the libtsk_jni lib file. - */ - enum LockState { - /** - * If a lock on the library is held by a version of Autopsy LTE 4.21.0 - */ - HELD_BY_OLD, - /** - * If a lock on the library is held by a version of Autopsy GT 4.21.0 - */ - HELD_BY_NEW, - /** - * If the lock has been acquired. - */ - ACQUIRED - } -} From e36d6f83e9e5f4f1bd794ae6defe34e5aa223afe Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 8 May 2024 11:33:03 -0400 Subject: [PATCH 11/19] fix for NPE --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 50dcca3f6f..221acc3e68 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -2823,11 +2823,14 @@ public class Case { Throwable curEx = ex; // max depth search for a concurrent db access exception will be 10 for (int i = 0; i < 10; i++) { - if (curEx instanceof ConcurrentDbAccessException foundEx) { + if (curEx == null) { + break; + } else if (curEx instanceof ConcurrentDbAccessException foundEx) { concurrentEx = foundEx; break; + } else { + curEx = curEx.getCause(); } - curEx = curEx.getCause(); } if (concurrentEx != null) { From 1317b5e3a541e777b91f817103fe6223725d7217 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 8 May 2024 15:20:20 -0400 Subject: [PATCH 12/19] wrong call for newCase --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 221acc3e68..0923666a87 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -2737,7 +2737,7 @@ public class Case { * with a standard name, physically located in the case * directory. */ - caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME, APP_NAME).toString()); + caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString(), (ContentStreamProvider) null, APP_NAME); metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME); } else { /* From 41ed4f0a121e6fb6fe6b1fb533710249bb4d16bd Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 28 May 2024 15:19:08 -0400 Subject: [PATCH 13/19] app name used in tempdir --- Core/src/org/sleuthkit/autopsy/core/Installer.java | 5 +++-- thirdparty/pasco2/.project | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/core/Installer.java b/Core/src/org/sleuthkit/autopsy/core/Installer.java index e8b1c79196..374d5b8ccb 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/core/Installer.java @@ -67,7 +67,7 @@ public class Installer extends ModuleInstall { private static final long serialVersionUID = 1L; private static final String JAVA_TEMP = "java.io.tmpdir"; - private static final String AUTOPSY_TEMP_DIR = "autopsy_temp"; + private static final String AUTOPSY_TEMP_DIR_SUFFIX = "_temp"; private static final String TSK_TEMP = "tsk.tmpdir"; private final List packageInstallers; @@ -93,7 +93,8 @@ public class Installer extends ModuleInstall { private static void setTskTemp() { try { String curTemp = System.getProperty(JAVA_TEMP, ""); - String tskTemp = curTemp + (curTemp.endsWith(File.separator) ? "" : File.separator) + AUTOPSY_TEMP_DIR; + String autopsyTempDir = StringUtils.defaultIfBlank(UserPreferences.getAppName(), "autopsy").replaceAll("[^a-zA-Z0-9_\\-]", "_") + AUTOPSY_TEMP_DIR_SUFFIX; + String tskTemp = Paths.get(StringUtils.defaultString(curTemp), autopsyTempDir).toString(); System.setProperty(TSK_TEMP, tskTemp); File tskTempDir = new File(tskTemp); tskTempDir.mkdirs(); diff --git a/thirdparty/pasco2/.project b/thirdparty/pasco2/.project index 454c782dbe..17a0007916 100644 --- a/thirdparty/pasco2/.project +++ b/thirdparty/pasco2/.project @@ -21,6 +21,17 @@ C:/Development/42six/trunk/thirdparty/pasco2/lib + + + 1715884364848 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + BIN_DIR From 2e2bd209bdb905ebbff68b99629dc661b4cf2547 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 28 May 2024 15:21:07 -0400 Subject: [PATCH 14/19] revert unnecessary change --- thirdparty/pasco2/.project | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/thirdparty/pasco2/.project b/thirdparty/pasco2/.project index 17a0007916..454c782dbe 100644 --- a/thirdparty/pasco2/.project +++ b/thirdparty/pasco2/.project @@ -21,17 +21,6 @@ C:/Development/42six/trunk/thirdparty/pasco2/lib - - - 1715884364848 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - BIN_DIR From 9d46c310b046563cbabe70c1cb75948aa810c5d0 Mon Sep 17 00:00:00 2001 From: "eugene.livis" Date: Tue, 2 Jul 2024 12:51:27 -0400 Subject: [PATCH 15/19] Upgraded SQLite version --- Core/build.xml | 4 ++-- Core/nbproject/project.xml | 4 ++-- ImageGallery/nbproject/project.xml | 4 ++-- RecentActivity/nbproject/project.xml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Core/build.xml b/Core/build.xml index df8bf56464..7429307d43 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -134,8 +134,8 @@ - + release/modules/ext/spotbugs-annotations-4.6.0.jar - ext/sqlite-jdbc-3.42.0.1.jar - release/modules/ext/sqlite-jdbc-3.42.0.1.jar + ext/sqlite-jdbc-3.46.0.0.jar + release/modules/ext/sqlite-jdbc-3.46.0.0.jar ext/txw2-2.3.3.jar diff --git a/ImageGallery/nbproject/project.xml b/ImageGallery/nbproject/project.xml index 49221290cd..6941e4b187 100644 --- a/ImageGallery/nbproject/project.xml +++ b/ImageGallery/nbproject/project.xml @@ -142,8 +142,8 @@ - ext/sqlite-jdbc-3.42.0.1.jar - release/modules/ext/sqlite-jdbc-3.42.0.1.jar + ext/sqlite-jdbc-3.46.0.0.jar + release/modules/ext/sqlite-jdbc-3.46.0.0.jar diff --git a/RecentActivity/nbproject/project.xml b/RecentActivity/nbproject/project.xml index 05745bb79a..bc1dd53aa8 100644 --- a/RecentActivity/nbproject/project.xml +++ b/RecentActivity/nbproject/project.xml @@ -88,8 +88,8 @@ release/modules/ext/Rejistry-1.1-SNAPSHOT.jar - ext/sqlite-jdbc-3.42.0.1.jar - release/modules/ext/sqlite-jdbc-3.42.0.1.jar + ext/sqlite-jdbc-3.46.0.0.jar + release/modules/ext/sqlite-jdbc-3.46.0.0.jar From 841ab81cb1422ecf79328f713ab67f84c667a454 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 5 Jul 2024 06:56:22 -0400 Subject: [PATCH 16/19] upgrade for sqlite --- Core/nbproject/project.properties | 2 +- ImageGallery/nbproject/project.properties | 2 +- RecentActivity/nbproject/project.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 1109aa35f7..984105254c 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -91,7 +91,7 @@ file.reference.slf4j-api-1.7.36.jar=release/modules/ext/slf4j-api-1.7.36.jar file.reference.snakeyaml-2.0.jar=release/modules/ext/snakeyaml-2.0.jar file.reference.SparseBitSet-1.1.jar=release/modules/ext/SparseBitSet-1.1.jar file.reference.spotbugs-annotations-4.6.0.jar=release/modules/ext/spotbugs-annotations-4.6.0.jar -file.reference.sqlite-jdbc-3.42.0.1.jar=release/modules/ext/sqlite-jdbc-3.42.0.1.jar +file.reference.sqlite-jdbc-3.46.0.0.jar=release/modules/ext/sqlite-jdbc-3.46.0.0.jar file.reference.txw2-2.3.3.jar=release/modules/ext/txw2-2.3.3.jar file.reference.xalan-2.7.2.jar=release/modules/ext/xalan-2.7.2.jar file.reference.xml-apis-1.4.01.jar=release/modules/ext/xml-apis-1.4.01.jar diff --git a/ImageGallery/nbproject/project.properties b/ImageGallery/nbproject/project.properties index d69f7d273a..2ea0f24c23 100644 --- a/ImageGallery/nbproject/project.properties +++ b/ImageGallery/nbproject/project.properties @@ -1,4 +1,4 @@ -file.reference.sqlite-jdbc-3.42.0.1.jar=release/modules/ext/sqlite-jdbc-3.42.0.1.jar +file.reference.sqlite-jdbc-3.46.0.0.jar=release/modules/ext/sqlite-jdbc-3.46.0.0.jar javac.source=17 javac.compilerargs=-Xlint -Xlint:-serial license.file=LICENSE-2.0.txt diff --git a/RecentActivity/nbproject/project.properties b/RecentActivity/nbproject/project.properties index 430436bea9..8c106492c6 100644 --- a/RecentActivity/nbproject/project.properties +++ b/RecentActivity/nbproject/project.properties @@ -1,6 +1,6 @@ javac.source=17 file.reference.Rejistry-1.1-SNAPSHOT.jar=release/modules/ext/Rejistry-1.1-SNAPSHOT.jar -file.reference.sqlite-jdbc-3.42.0.1.jar=release/modules/ext/sqlite-jdbc-3.42.0.1.jar +file.reference.sqlite-jdbc-3.46.0.0.jar=release/modules/ext/sqlite-jdbc-3.46.0.0.jar javac.compilerargs=-Xlint -Xlint:-serial license.file=../LICENSE-2.0.txt nbm.homepage=http://www.sleuthkit.org/autopsy/ From 0a62fbbaca48e3bae177bf57745451f331eebffd Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Mon, 8 Jul 2024 17:12:36 -0400 Subject: [PATCH 17/19] Update footer.html --- docs/doxygen-user/footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/doxygen-user/footer.html b/docs/doxygen-user/footer.html index cfedffe82c..fe4f9e42d1 100644 --- a/docs/doxygen-user/footer.html +++ b/docs/doxygen-user/footer.html @@ -1,5 +1,5 @@
-

Copyright © 2012-2023 BasisTech. Generated on $date
+

Copyright © 2012-2024 Sleuth Kit Labs. Generated on $date
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.

From 4eeeb5c1af82889cfcb6bf0be67265a679a236ee Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Mon, 8 Jul 2024 17:13:07 -0400 Subject: [PATCH 18/19] Update footer.html --- docs/doxygen-dev/footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/doxygen-dev/footer.html b/docs/doxygen-dev/footer.html index d648961fdc..fe4f9e42d1 100755 --- a/docs/doxygen-dev/footer.html +++ b/docs/doxygen-dev/footer.html @@ -1,5 +1,5 @@
-

Copyright © 2012-2022 Basis Technology. Generated on $date
+

Copyright © 2012-2024 Sleuth Kit Labs. Generated on $date
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.

From f996320a1c54d5d444b91dfcb1657009756130d7 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Mon, 8 Jul 2024 17:13:34 -0400 Subject: [PATCH 19/19] Update footer.html --- docs/doxygen/footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/doxygen/footer.html b/docs/doxygen/footer.html index f9eb00590e..6d97889cc8 100644 --- a/docs/doxygen/footer.html +++ b/docs/doxygen/footer.html @@ -1,5 +1,5 @@
-

Copyright © 2012-2022 Basis Technology. Generated on: $date
+

Copyright © 2012-2024 Sleuth Kit Labs. Generated on: $date
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.