From 86fbba96590ff0bb8ea2be01574c885397318074 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 23 Apr 2024 11:29:56 -0400 Subject: [PATCH] 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 + } +}