diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java index eb4372c6417cf407a3037f4af0a6427cbd658d84..d7782fef832654f1879281e63b29581c43c451ab 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java @@ -266,7 +266,7 @@ private void createTagTables(Statement stmt) throws SQLException { stmt.execute("CREATE TABLE tsk_tag_sets (tag_set_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, name TEXT UNIQUE)"); stmt.execute("CREATE TABLE tag_names (tag_name_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, display_name TEXT UNIQUE, " + "description TEXT NOT NULL, color TEXT NOT NULL, knownStatus INTEGER NOT NULL," - + " tag_set_id INTEGER, FOREIGN KEY(tag_set_id) REFERENCES tsk_tag_sets(tag_set_id) ON DELETE SET NULL)"); + + " tag_set_id " + dbQueryHelper.getBigIntType() + ", rank INTEGER, FOREIGN KEY(tag_set_id) REFERENCES tsk_tag_sets(tag_set_id) ON DELETE SET NULL)"); stmt.execute("CREATE TABLE tsk_examiners (examiner_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, " + "login_name TEXT NOT NULL, display_name TEXT, UNIQUE(login_name))"); diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDbAccessManager.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDbAccessManager.java index e80c280aad430bd7124c8fbb9322189f72427250..1bf72c77351b042721c2fa4cef32807d6d4352d6 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CaseDbAccessManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDbAccessManager.java @@ -307,7 +307,6 @@ public void alterTable(final String tableName, final String alterSQL, final Case validateSQL(alterSQL); CaseDbConnection connection = transaction.getConnection(); - transaction.acquireSingleUserCaseWriteLock(); Statement statement = null; String sql = "ALTER TABLE " + tableName + " " + alterSQL; @@ -328,7 +327,6 @@ public void alterTable(final String tableName, final String alterSQL, final Case throw new TskCoreException(String.format("Error altering table %s with SQL = %s", tableName, sql), ex); } finally { closeStatement(statement); - // NOTE: write lock will be released by transaction } } @@ -422,7 +420,6 @@ public long insert(final String tableName, final String sql, final CaseDbTransac validateSQL(sql); CaseDbConnection connection = transaction.getConnection(); - transaction.acquireSingleUserCaseWriteLock(); PreparedStatement statement = null; ResultSet resultSet; @@ -444,7 +441,6 @@ public long insert(final String tableName, final String sql, final CaseDbTransac throw new TskCoreException("Error inserting row in table " + tableName + " with sql = "+ insertSQL, ex); } finally { closeStatement(statement); - // NOTE: write lock will be released by transaction } return rowId; @@ -507,7 +503,6 @@ public long insertOrUpdate(final String tableName, final String sql, final CaseD validateSQL(sql); CaseDbConnection connection = transaction.getConnection(); - transaction.acquireSingleUserCaseWriteLock(); PreparedStatement statement = null; ResultSet resultSet; @@ -528,7 +523,6 @@ public long insertOrUpdate(final String tableName, final String sql, final CaseD throw new TskCoreException("Error inserting row in table " + tableName + " with sql = "+ insertSQL, ex); } finally { closeStatement(statement); - // NOTE: write lock will be released by transaction } return rowId; @@ -576,7 +570,6 @@ public void update(final String tableName, final String sql, CaseDbTransaction t validateSQL(sql); CaseDbConnection connection = transaction.getConnection(); - transaction.acquireSingleUserCaseWriteLock(); Statement statement = null; String updateSQL = "UPDATE " + tableName + " " + sql; // NON-NLS @@ -588,7 +581,6 @@ public void update(final String tableName, final String sql, CaseDbTransaction t throw new TskCoreException("Error Updating table " + tableName, ex); } finally { closeStatement(statement); - // NOTE: write lock will be released by transaction } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java index 68c5eb6cb85bd2fa6a8653819a3f012de2f99ebb..a16fa8aaf095ae3d09d9d68f54a11342d6010ab6 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java @@ -69,7 +69,6 @@ class JniDbHelper { */ private void beginTransaction() throws TskCoreException { trans = caseDb.beginTransaction(); - trans.acquireSingleUserCaseWriteLock(); } /** diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 7b39ae6a299a709558ac0023c48240ec50fd906e..988e6687a5e61989ef24a718392773f522e73863 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -98,7 +98,6 @@ public class SleuthkitCase { */ static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION = new CaseDbSchemaVersionNumber(8, 5); - private static final long BASE_ARTIFACT_ID = Long.MIN_VALUE; // Artifact ids will start at the lowest negative value private static final Logger logger = Logger.getLogger(SleuthkitCase.class.getName()); @@ -480,11 +479,11 @@ public TimelineManager getTimelineManager() throws TskCoreException { public synchronized CaseDbAccessManager getCaseDbAccessManager() throws TskCoreException { return dbAccessManager; } - + /** * Get the case database TaggingManager object. - * - * @return The per case TaggingManager object. + * + * @return The per case TaggingManager object. */ public synchronized TaggingManager getTaggingManager() { return taggingMgr; @@ -1919,24 +1918,23 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot2toSchema8dot3(CaseDbSchem statement.execute("UPDATE tsk_db_info_extended SET name = 'CREATION_SCHEMA_MAJOR_VERSION' WHERE name = 'CREATED_SCHEMA_MAJOR_VERSION'"); statement.execute("UPDATE tsk_db_info_extended SET name = 'CREATION_SCHEMA_MINOR_VERSION' WHERE name = 'CREATED_SCHEMA_MINOR_VERSION'"); - - return new CaseDbSchemaVersionNumber(8, 3); } finally { closeResultSet(resultSet); releaseSingleUserCaseWriteLock(); } } - + /** * Updates a schema version 8.3 database to a schema version 8.4 database. - * - * This includes a bug fix update for a misnamed column in tsk_event_descriptions in - * the previous update code. - * - * Note that 8.4 also introduced cascading deletes on many of the database tables. We do not need to - * add these in the upgrade code because data sources in cases that were originally created with 8.3 - * or earlier can not be deleted. + * + * This includes a bug fix update for a misnamed column in + * tsk_event_descriptions in the previous update code. + * + * Note that 8.4 also introduced cascading deletes on many of the database + * tables. We do not need to add these in the upgrade code because data + * sources in cases that were originally created with 8.3 or earlier can not + * be deleted. * * @param schemaVersion The current schema version of the database. * @param connection A connection to the case database. @@ -1960,24 +1958,24 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot3toSchema8dot4(CaseDbSchem Statement statement = connection.createStatement(); ResultSet results = null; - acquireSingleUserCaseWriteLock(); + acquireSingleUserCaseWriteLock(); try { // This is a bug fix update for a misnamed column in tsk_event_descriptions in // the previous update code. if (null == getDatabaseType()) { throw new TskCoreException("Unsupported data base type: " + getDatabaseType().toString()); - } - + } + switch (getDatabaseType()) { case POSTGRESQL: // Check if the misnamed column is present results = statement.executeQuery("SELECT column_name FROM information_schema.columns " - + "WHERE table_name='tsk_event_descriptions' and column_name='file_obj_id'"); + + "WHERE table_name='tsk_event_descriptions' and column_name='file_obj_id'"); if (results.next()) { // In PostgreSQL we can rename the column if it exists statement.execute("ALTER TABLE tsk_event_descriptions " - + "RENAME COLUMN file_obj_id TO content_obj_id"); - + + "RENAME COLUMN file_obj_id TO content_obj_id"); + // In 8.2 to 8.3 upgrade, the event_id & time column in tsk_events table was erroneously created as type INTEGER, instead of BIGINT // Fix the schema, preserving any data if exists. statement.execute("CREATE TABLE temp_tsk_events ( " @@ -1986,18 +1984,18 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot3toSchema8dot4(CaseDbSchem + " event_description_id BIGINT NOT NULL REFERENCES tsk_event_descriptions(event_description_id)," + " time BIGINT NOT NULL, " + " UNIQUE (event_type_id, event_description_id, time))" - ); - + ); + // Copy the data statement.execute("INSERT INTO temp_tsk_events(event_id, event_type_id, " + "event_description_id, time) SELECT * FROM tsk_events"); - + // Drop the old table statement.execute("DROP TABLE tsk_events"); - + // Rename the new table statement.execute("ALTER TABLE temp_tsk_events RENAME TO tsk_events"); - + //create tsk_events indices that were skipped in the 8.2 to 8.3 update code statement.execute("CREATE INDEX events_data_source_obj_id ON tsk_event_descriptions(data_source_obj_id) "); statement.execute("CREATE INDEX events_content_obj_id ON tsk_event_descriptions(content_obj_id) "); @@ -2013,9 +2011,9 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot3toSchema8dot4(CaseDbSchem if (results.getString("name") != null && results.getString("name").equals("file_obj_id")) { hasMisnamedColumn = true; break; - } + } } - + if (hasMisnamedColumn) { // Since we can't rename the column we'll need to make new tables and copy the data statement.execute("CREATE TABLE temp_tsk_event_descriptions (" @@ -2032,16 +2030,16 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot3toSchema8dot4(CaseDbSchem + " FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id), " + " FOREIGN KEY(content_obj_id) REFERENCES tsk_files(obj_id), " + " FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id))" - ); - + ); + statement.execute("CREATE TABLE temp_tsk_events ( " + " event_id INTEGER PRIMARY KEY, " + " event_type_id BIGINT NOT NULL REFERENCES tsk_event_types(event_type_id) ," + " event_description_id BIGINT NOT NULL REFERENCES temp_tsk_event_descriptions(event_description_id)," + " time INTEGER NOT NULL, " + " UNIQUE (event_type_id, event_description_id, time))" - ); - + ); + // Copy the data statement.execute("INSERT INTO temp_tsk_event_descriptions(event_description_id, full_description, " + "med_description, short_description, data_source_obj_id, content_obj_id, artifact_id, " @@ -2049,7 +2047,7 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot3toSchema8dot4(CaseDbSchem statement.execute("INSERT INTO temp_tsk_events(event_id, event_type_id, " + "event_description_id, time) SELECT * FROM tsk_events"); - + // Drop the old tables statement.execute("DROP TABLE tsk_events"); statement.execute("DROP TABLE tsk_event_descriptions"); @@ -2057,7 +2055,7 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot3toSchema8dot4(CaseDbSchem // Rename the new tables statement.execute("ALTER TABLE temp_tsk_event_descriptions RENAME TO tsk_event_descriptions"); statement.execute("ALTER TABLE temp_tsk_events RENAME TO tsk_events"); - + //create tsk_events indices statement.execute("CREATE INDEX events_data_source_obj_id ON tsk_event_descriptions(data_source_obj_id) "); statement.execute("CREATE INDEX events_content_obj_id ON tsk_event_descriptions(content_obj_id) "); @@ -2069,14 +2067,14 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot3toSchema8dot4(CaseDbSchem default: throw new TskCoreException("Unsupported data base type: " + getDatabaseType().toString()); } - + // create pool info table if (this.dbType.equals(DbType.SQLITE)) { statement.execute("CREATE TABLE tsk_pool_info (obj_id INTEGER PRIMARY KEY, pool_type INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)"); } else { statement.execute("CREATE TABLE tsk_pool_info (obj_id BIGSERIAL PRIMARY KEY, pool_type INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)"); } - + // Add new account types for newly supported messaging applications, if they dont exists already. insertAccountTypeIfNotExists(statement, "IMO", "IMO"); insertAccountTypeIfNotExists(statement, "LINE", "LINE"); @@ -2088,15 +2086,15 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot3toSchema8dot4(CaseDbSchem insertAccountTypeIfNotExists(statement, "XENDER", "Xender"); insertAccountTypeIfNotExists(statement, "ZAPYA", "Zapya"); insertAccountTypeIfNotExists(statement, "SHAREIT", "ShareIt"); - + return new CaseDbSchemaVersionNumber(8, 4); } finally { closeResultSet(results); closeStatement(statement); releaseSingleUserCaseWriteLock(); - } + } } - + private CaseDbSchemaVersionNumber updateFromSchema8dot4toSchema8dot5(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { if (schemaVersion.getMajor() != 8) { return schemaVersion; @@ -2112,15 +2110,17 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot4toSchema8dot5(CaseDbSchem switch (getDatabaseType()) { case POSTGRESQL: statement.execute("CREATE TABLE tsk_tag_sets (tag_set_id BIGSERIAL PRIMARY KEY, name TEXT UNIQUE)"); + statement.execute("ALTER TABLE tag_names ADD COLUMN tag_set_id BIGINT REFERENCES tsk_tag_sets(tag_set_id)"); break; case SQLITE: statement.execute("CREATE TABLE tsk_tag_sets (tag_set_id INTEGER PRIMARY KEY, name TEXT UNIQUE)"); + statement.execute("ALTER TABLE tag_names ADD COLUMN tag_set_id INTEGER REFERENCES tsk_tag_sets(tag_set_id)"); break; } - statement.execute("ALTER TABLE tag_names ADD COLUMN tag_set_id INTEGER REFERENCES tsk_tag_sets(tag_set_id)"); + statement.execute("ALTER TABLE tag_names ADD COLUMN rank INTEGER"); - String insertStmt = "INSERT INTO tsk_tag_sets (name) VALUES ('Project VIC (United States)')"; + String insertStmt = "INSERT INTO tsk_tag_sets (name) VALUES ('Project VIC')"; if (getDatabaseType() == DbType.POSTGRESQL) { statement.execute(insertStmt, Statement.RETURN_GENERATED_KEYS); } else { @@ -2130,13 +2130,20 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot4toSchema8dot5(CaseDbSchem if (resultSet != null && resultSet.next()) { int tagSetId = resultSet.getInt(1); - String updateQuery = "UPDATE tag_names SET tag_set_id = %d, color = '%s' WHERE display_name = '%s'"; - statement.executeUpdate(String.format(updateQuery, tagSetId, "Red", "CAT-1: Child Exploitation (Illegal)")); - statement.executeUpdate(String.format(updateQuery, tagSetId, "Lime", "CAT-2: Child Exploitation (Non-Illegal/Age Difficult)")); - statement.executeUpdate(String.format(updateQuery, tagSetId, "Yellow", "CAT-3: CGI/Animation (Child Exploitive)")); - statement.executeUpdate(String.format(updateQuery, tagSetId, "Purple", "CAT-4: Exemplar/Comparison (Internal Use Only)")); - statement.executeUpdate(String.format(updateQuery, tagSetId, "Green", "CAT-5: Non-pertinent")); - statement.executeUpdate(String.format(updateQuery, tagSetId, "Silver", "CAT-0: Uncategorized")); + String updateQuery = "UPDATE tag_names SET tag_set_id = %d, color = '%s', rank = %d, display_name = '%s' WHERE display_name = '%s'"; + statement.executeUpdate(String.format(updateQuery, tagSetId, "Red", 1, "Child Exploitation (Illegal)", "CAT-1: Child Exploitation (Illegal)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Lime", 2, "Child Exploitation (Non-Illegal/Age Difficult)", "CAT-2: Child Exploitation (Non-Illegal/Age Difficult)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Yellow", 3, "CGI/Animation (Child Exploitive)", "CAT-3: CGI/Animation (Child Exploitive)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Purple", 4, "Exemplar/Comparison (Internal Use Only)", "CAT-4: Exemplar/Comparison (Internal Use Only)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Fuchsia", 5, "Non-pertinent", "CAT-5: Non-pertinent")); + + String deleteContentTag = "DELETE FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id from tag_names WHERE display_name LIKE 'CAT-0: Uncategorized')"; + String deleteArtifactTag = "DELETE FROM blackboard_artifact_tags WHERE tag_name_id IN (SELECT tag_name_id from tag_names WHERE display_name LIKE 'CAT-0: Uncategorized')"; + String deleteCat0 = "DELETE FROM tag_names WHERE display_name = 'CAT-0: Uncategorized'"; + statement.executeUpdate(deleteContentTag); + statement.executeUpdate(deleteArtifactTag); + statement.executeUpdate(deleteCat0); + } else { throw new TskCoreException("Failed to retrieve the default tag_set_id from DB"); } @@ -2174,18 +2181,18 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot4toSchema8dot5(CaseDbSchem } /** - * Inserts a row for the given account type in account_types table, - * if one doesn't exist. - * - * @param statement Statement to use to execute SQL. - * @param type_name Account type name. + * Inserts a row for the given account type in account_types table, if one + * doesn't exist. + * + * @param statement Statement to use to execute SQL. + * @param type_name Account type name. * @param display_name Account type display name. - * + * * @throws TskCoreException - * @throws SQLException + * @throws SQLException */ private void insertAccountTypeIfNotExists(Statement statement, String type_name, String display_name) throws TskCoreException, SQLException { - + String insertSQL = String.format("INTO account_types(type_name, display_name) VALUES ('%s', '%s')", type_name, display_name); switch (getDatabaseType()) { case POSTGRESQL: @@ -2199,7 +2206,8 @@ private void insertAccountTypeIfNotExists(Statement statement, String type_name, } statement.execute(insertSQL); //NON-NLS } - /** + + /** * Extract the extension from a file name. * * @param fileName the file name to extract the extension from. @@ -2283,6 +2291,10 @@ public String getBackupDatabasePath() { * that is returned can be passed to methods that take a CaseDbTransaction. * The caller is responsible for calling either commit() or rollback() on * the transaction object. + * + * Note that this beginning the transaction also acquires the single user + * case write lock, which will be automatically released when the transaction + * is closed. * * @return A CaseDbTransaction object. * @@ -3028,7 +3040,7 @@ public long getBlackboardArtifactsTypeCount(int artifactTypeID) throws TskCoreEx releaseSingleUserCaseReadLock(); } } - + /** * Get a count of artifacts of a given type for the given data source. Does * not include rejected artifacts. @@ -5450,14 +5462,12 @@ public List<AbstractFile> findFiles(Content dataSource, String fileName, String */ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName) throws TskCoreException { CaseDbTransaction localTrans = beginTransaction(); - localTrans.acquireSingleUserCaseWriteLock(); try { VirtualDirectory newVD = addVirtualDirectory(parentId, directoryName, localTrans); localTrans.commit(); localTrans = null; return newVD; } finally { - // NOTE: write lock will be released by transaction if (null != localTrans) { try { localTrans.rollback(); @@ -5532,7 +5542,6 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName, throw new TskCoreException("Passed null CaseDbTransaction"); } - transaction.acquireSingleUserCaseWriteLock(); ResultSet resultSet = null; try { // Get the parent path. @@ -5630,7 +5639,6 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName, throw new TskCoreException("Error creating virtual directory '" + directoryName + "'", e); } finally { closeResultSet(resultSet); - // NOTE: write lock will be released by transaction } } @@ -5647,7 +5655,6 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName, * @throws TskCoreException */ public LocalDirectory addLocalDirectory(long parentId, String directoryName) throws TskCoreException { - acquireSingleUserCaseWriteLock(); CaseDbTransaction localTrans = beginTransaction(); try { LocalDirectory newLD = addLocalDirectory(parentId, directoryName, localTrans); @@ -5660,8 +5667,6 @@ public LocalDirectory addLocalDirectory(long parentId, String directoryName) thr logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2); } throw ex; - } finally { - releaseSingleUserCaseWriteLock(); } } @@ -5687,7 +5692,6 @@ public LocalDirectory addLocalDirectory(long parentId, String directoryName, Cas throw new TskCoreException("Passed null CaseDbTransaction"); } - transaction.acquireSingleUserCaseWriteLock(); ResultSet resultSet = null; try { // Get the parent path. @@ -5766,7 +5770,6 @@ public LocalDirectory addLocalDirectory(long parentId, String directoryName, Cas throw new TskCoreException("Error creating local directory '" + directoryName + "'", e); } finally { closeResultSet(resultSet); - // NOTE: write lock will be released by transaction } } @@ -6022,17 +6025,17 @@ public Volume addVolume(long parentObjId, long addr, long start, long length, St releaseSingleUserCaseWriteLock(); } } - + /** * Add a pool to the database. - * + * * @param parentObjId Object ID of the pool's parent * @param type Type of pool * @param transaction Case DB transaction - * + * * @return the newly created Pool - * - * @throws TskCoreException + * + * @throws TskCoreException */ public Pool addPool(long parentObjId, TskData.TSK_POOL_TYPE_ENUM type, CaseDbTransaction transaction) throws TskCoreException { acquireSingleUserCaseWriteLock(); @@ -6058,7 +6061,7 @@ public Pool addPool(long parentObjId, TskData.TSK_POOL_TYPE_ENUM type, CaseDbTra closeStatement(statement); releaseSingleUserCaseWriteLock(); } - } + } /** * Add a FileSystem to the database. @@ -6158,7 +6161,6 @@ public FsContent addFileSystemFile(long dataSourceObjId, long fsObjId, Statement queryStatement = null; try { CaseDbConnection connection = transaction.getConnection(); - transaction.acquireSingleUserCaseWriteLock(); // Insert a row for the local/logical file into the tsk_objects table. // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) @@ -6206,10 +6208,10 @@ public FsContent addFileSystemFile(long dataSourceObjId, long fsObjId, connection.executeUpdate(statement); - DerivedFile derivedFile = new DerivedFile(this, objectId, dataSourceObjId, fileName, dirType, metaType, dirFlag, metaFlags, + DerivedFile derivedFile = new DerivedFile(this, objectId, dataSourceObjId, fileName, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, null, null, parentPath, null, parent.getId(), null, null, extension); - timelineManager.addEventsForNewFile(derivedFile, connection); + timelineManager.addEventsForNewFile(derivedFile, connection); transaction.commit(); transaction = null; @@ -6299,7 +6301,6 @@ public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange> try { transaction = beginTransaction(); - transaction.acquireSingleUserCaseWriteLock(); CaseDbConnection connection = transaction.getConnection(); List<LayoutFile> fileRangeLayoutFiles = new ArrayList<LayoutFile>(); @@ -6388,7 +6389,6 @@ public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange> closeResultSet(resultSet); closeStatement(statement); - // NOTE: write lock will be released by transaction if (null != transaction) { try { transaction.rollback(); @@ -6429,7 +6429,6 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws long newCacheKey = 0; // Used to roll back cache if transaction is rolled back. try { transaction = beginTransaction(); - transaction.acquireSingleUserCaseWriteLock(); CaseDbConnection connection = transaction.getConnection(); /* @@ -6574,7 +6573,6 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws closeResultSet(resultSet); closeStatement(statement); - // NOTE: write lock will be released by transaction if (null != transaction) { try { transaction.rollback(); @@ -6626,7 +6624,6 @@ public DerivedFile addDerivedFile(String fileName, String localPath, // Strip off any leading slashes from the local path (leading slashes indicate absolute paths) localPath = localPath.replaceAll("^[/\\\\]+", ""); - acquireSingleUserCaseWriteLock(); TimelineManager timelineManager = getTimelineManager(); CaseDbTransaction transaction = beginTransaction(); @@ -6722,7 +6719,6 @@ public DerivedFile addDerivedFile(String fileName, String localPath, throw new TskCoreException("Failed to add derived file to case database", ex); } finally { connection.close(); - releaseSingleUserCaseWriteLock(); } } @@ -6943,7 +6939,6 @@ public LocalFile addLocalFile(String fileName, String localPath, boolean isFile, TskData.EncodingType encodingType, Content parent, CaseDbTransaction transaction) throws TskCoreException { CaseDbConnection connection = transaction.getConnection(); - transaction.acquireSingleUserCaseWriteLock(); Statement queryStatement = null; try { @@ -7029,7 +7024,6 @@ public LocalFile addLocalFile(String fileName, String localPath, throw new TskCoreException(String.format("Failed to INSERT local file %s (%s) with parent id %d in tsk_files table", fileName, localPath, parent.getId()), ex); } finally { closeStatement(queryStatement); - // NOTE: write lock will be released by transaction } } @@ -7047,7 +7041,6 @@ public LocalFile addLocalFile(String fileName, String localPath, */ private boolean isRootDirectory(AbstractFile file, CaseDbTransaction transaction) throws TskCoreException { CaseDbConnection connection = transaction.getConnection(); - transaction.acquireSingleUserCaseWriteLock(); Statement statement = null; ResultSet resultSet = null; @@ -7077,7 +7070,6 @@ private boolean isRootDirectory(AbstractFile file, CaseDbTransaction transaction } finally { closeResultSet(resultSet); closeStatement(statement); - // NOTE: write lock will be released by transaction } } @@ -7123,7 +7115,6 @@ public LayoutFile addLayoutFile(String fileName, ResultSet resultSet = null; try { transaction = beginTransaction(); - transaction.acquireSingleUserCaseWriteLock(); CaseDbConnection connection = transaction.getConnection(); /* @@ -7222,7 +7213,6 @@ public LayoutFile addLayoutFile(String fileName, closeResultSet(resultSet); closeStatement(statement); - // NOTE: write lock will be released by transaction if (null != transaction) { try { transaction.rollback(); @@ -7666,7 +7656,7 @@ FileSystem getFileSystemById(long id, long parentId) throws TskCoreException { FileSystem getFileSystemById(long id, Volume parent) throws TskCoreException { return getFileSystemByIdHelper(id, parent); } - + /** * Get a pool by the object id * @@ -7680,8 +7670,8 @@ FileSystem getFileSystemById(long id, Volume parent) throws TskCoreException { */ Pool getPoolById(long id, Content parent) throws TskCoreException { return getPoolByIdHelper(id, parent); - } - + } + /** * @param id ID of the desired Volume * @param parentId ID of the Volume's parent @@ -7695,7 +7685,7 @@ Pool getPoolById(long id, long parentId) throws TskCoreException { pool.setParentId(parentId); return pool; } - + /** * Get pool by id and Content parent * @@ -7711,13 +7701,13 @@ private Pool getPoolByIdHelper(long id, Content parent) throws TskCoreException acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = connections.getConnection(); - Statement s = connection.createStatement(); - ResultSet rs = connection.executeQuery(s, "SELECT * FROM tsk_pool_info " //NON-NLS - + "where obj_id = " + id);) { //NON-NLS + Statement s = connection.createStatement(); + ResultSet rs = connection.executeQuery(s, "SELECT * FROM tsk_pool_info " //NON-NLS + + "where obj_id = " + id);) { //NON-NLS if (rs.next()) { Pool pool = new Pool(this, rs.getLong("obj_id"), TskData.TSK_POOL_TYPE_ENUM.valueOf(rs.getLong("pool_type")).getName(), rs.getLong("pool_type")); pool.setParent(parent); - + return pool; } else { throw new TskCoreException("No pool found for ID:" + id); @@ -8007,7 +7997,7 @@ List<Long> getImageChildrenIds(Image img) throws TskCoreException { } return children; } - + /** * Returns the list of direct children for a given Pool * @@ -8068,7 +8058,7 @@ List<Long> getPoolChildrenIds(Pool pool) throws TskCoreException { } } return children; - } + } /** * Returns the list of direct children for a given VolumeSystem @@ -9605,7 +9595,7 @@ public List<TagName> getAllTagNames() throws TskCoreException { while (resultSet.next()) { tagNames.add(new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), - TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getInt("tag_set_id"))); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"), resultSet.getInt("rank"))); //NON-NLS } return tagNames; } catch (SQLException ex) { @@ -9639,7 +9629,7 @@ public List<TagName> getTagNamesInUse() throws TskCoreException { while (resultSet.next()) { tagNames.add(new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), - TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"))); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"), resultSet.getInt("rank"))); //NON-NLS } return tagNames; } catch (SQLException ex) { @@ -9683,7 +9673,7 @@ public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException { while (resultSet.next()) { tagNames.add(new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), - TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"))); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"), resultSet.getInt("rank"))); //NON-NLS } return tagNames; } catch (SQLException ex) { @@ -9746,8 +9736,18 @@ public TagName addOrUpdateTagName(String displayName, String description, TagNam connection.executeUpdate(statement); resultSet = statement.getGeneratedKeys(); resultSet.next(); - return new TagName(resultSet.getLong(1), //last_insert_rowid() - displayName, description, color, knownStatus, 0); + + long tagId = resultSet.getLong(1); + + resultSet.close(); + statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAME_BY_ID); + statement.clearParameters(); + statement.setLong(1, tagId); + resultSet = connection.executeQuery(statement); + resultSet.next(); + + return new TagName(tagId, + displayName, description, color, knownStatus, resultSet.getLong("tag_set_id"), resultSet.getInt("rank")); } catch (SQLException ex) { throw new TskCoreException("Error adding row for " + displayName + " tag name to tag_names table", ex); } finally { @@ -9769,7 +9769,7 @@ public TagName addOrUpdateTagName(String displayName, String description, TagNam * @return A ContentTag data transfer object (DTO) for the new row. * * @throws TskCoreException - * @deprecated Use TaggingManager.addContentTag + * @deprecated Use TaggingManager.addContentTag */ @Deprecated public ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws TskCoreException { @@ -9821,7 +9821,7 @@ public List<ContentTag> getAllContentTags() throws TskCoreException { while (resultSet.next()) { TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), - TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"), resultSet.getInt("rank")); //NON-NLS Content content = getContentById(resultSet.getLong("obj_id")); //NON-NLS tags.add(new ContentTag(resultSet.getLong("tag_id"), content, tagName, resultSet.getString("comment"), resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name"))); //NON-NLS @@ -9951,7 +9951,7 @@ public ContentTag getContentTagByID(long contentTagID) throws TskCoreException { while (resultSet.next()) { TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), - TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"), resultSet.getInt("rank")); tag = new ContentTag(resultSet.getLong("tag_id"), getContentById(resultSet.getLong("obj_id")), tagName, resultSet.getString("comment"), resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name")); } @@ -10087,7 +10087,7 @@ public List<ContentTag> getContentTagsByContent(Content content) throws TskCoreE while (resultSet.next()) { TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), - TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"), resultSet.getInt("rank")); //NON-NLS ContentTag tag = new ContentTag(resultSet.getLong("tag_id"), content, tagName, resultSet.getString("comment"), resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name")); //NON-NLS tags.add(tag); @@ -10167,7 +10167,7 @@ public List<BlackboardArtifactTag> getAllBlackboardArtifactTags() throws TskCore while (resultSet.next()) { TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), - TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"), resultSet.getInt("rank")); //NON-NLS BlackboardArtifact artifact = getBlackboardArtifact(resultSet.getLong("artifact_id")); //NON-NLS Content content = getContentById(artifact.getObjectID()); BlackboardArtifactTag tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), @@ -10397,7 +10397,7 @@ public BlackboardArtifactTag getBlackboardArtifactTagByID(long artifactTagID) th while (resultSet.next()) { TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), - TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"), resultSet.getInt("rank")); BlackboardArtifact artifact = getBlackboardArtifact(resultSet.getLong("artifact_id")); //NON-NLS Content content = getContentById(artifact.getObjectID()); tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), @@ -10445,7 +10445,7 @@ public List<BlackboardArtifactTag> getBlackboardArtifactTagsByArtifact(Blackboar while (resultSet.next()) { TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), - TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"), resultSet.getInt("rank")); //NON-NLS Content content = getContentById(artifact.getObjectID()); BlackboardArtifactTag tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), artifact, content, tagName, resultSet.getString("comment"), resultSet.getString("login_name")); //NON-NLS @@ -10973,21 +10973,21 @@ private List<IngestModuleInfo> getIngestModules(int ingestJobId, CaseDbConnectio } } - + /** - * Add an image to the database. - * For use with the JNI callbacks associated with the add image process. - * - * @param type Type of image. - * @param sectorSize Sector size. - * @param size Image size. - * @param timezone Time zone. - * @param md5 MD5 hash. - * @param sha1 SHA1 hash. - * @param sha256 SHA256 hash. - * @param deviceId Device ID. + * Add an image to the database. For use with the JNI callbacks associated + * with the add image process. + * + * @param type Type of image. + * @param sectorSize Sector size. + * @param size Image size. + * @param timezone Time zone. + * @param md5 MD5 hash. + * @param sha1 SHA1 hash. + * @param sha256 SHA256 hash. + * @param deviceId Device ID. * @param collectionDetails Collection details. - * @param transaction Case DB transaction. + * @param transaction Case DB transaction. * * @return The newly added Image object ID. * @@ -11035,17 +11035,17 @@ long addImageJNI(TskData.TSK_IMG_TYPE_ENUM type, long sectorSize, long size, releaseSingleUserCaseWriteLock(); } } - + /** - * Add an image name to the database. - * For use with the JNI callbacks associated with the add image process. - * - * @param objId The object id of the image. - * @param name The file name for the image - * @param sequence The sequence number of this file. + * Add an image name to the database. For use with the JNI callbacks + * associated with the add image process. + * + * @param objId The object id of the image. + * @param name The file name for the image + * @param sequence The sequence number of this file. * @param transaction The open transaction. - * - * @throws TskCoreException + * + * @throws TskCoreException */ void addImageNameJNI(long objId, String name, long sequence, CaseDbTransaction transaction) throws TskCoreException { @@ -11064,21 +11064,21 @@ void addImageNameJNI(long objId, String name, long sequence, releaseSingleUserCaseWriteLock(); } } - + /** - * Looks up a parent file object ID. - * The calling thread is expected to have a case read lock. - * For use with the JNI callbacks associated with the add image process. - * - * @param metaAddr The metadata address. - * @param fsObjId The file system object ID. - * @param path The file path. - * @param name The file name. + * Looks up a parent file object ID. The calling thread is expected to have + * a case read lock. For use with the JNI callbacks associated with the add + * image process. + * + * @param metaAddr The metadata address. + * @param fsObjId The file system object ID. + * @param path The file path. + * @param name The file name. * @param transaction The open transaction. - * + * * @return The object ID if found, -1 otherwise. - * - * @throws TskCoreException + * + * @throws TskCoreException */ long findParentObjIdJNI(long metaAddr, long fsObjId, String path, String name, CaseDbTransaction transaction) throws TskCoreException { ResultSet resultSet = null; @@ -11102,48 +11102,50 @@ long findParentObjIdJNI(long metaAddr, long fsObjId, String path, String name, C closeResultSet(resultSet); } } - + /** - * Add a file system file to the database. - * For use with the JNI callbacks associated with the add image process. - * + * Add a file system file to the database. For use with the JNI callbacks + * associated with the add image process. + * * @param parentObjId The parent of the file. * @param fsObjId The object ID of the file system. * @param dataSourceObjId The data source object ID. - * @param fsType The type. - * @param attrType The type attribute given to the file by the file system. - * @param attrId The type id given to the file by the file system. - * @param name The name of the file. - * @param metaAddr The meta address of the file. - * @param metaSeq The meta sequence number of the file. - * @param dirType The type of the file, usually as reported in - * the name structure of the file system. - * @param metaType The type of the file, usually as reported in - * the metadata structure of the file system. - * @param dirFlags The allocated status of the file, usually as - * reported in the name structure of the file system. - * @param metaFlags The allocated status of the file, usually as - * reported in the metadata structure of the file system. - * @param size The file size. - * @param crtime The created time. - * @param ctime The last changed time - * @param atime The last accessed time. - * @param mtime The last modified time. - * @param meta_mode The modes for the file. - * @param gid The group identifier. - * @param uid The user identifier. - * @param md5 The MD5 hash. - * @param known The file known status. - * @param escaped_path The escaped path to the file. - * @param extension The file extension. - * @param hasLayout True if this is a layout file, false otherwise. - * @param transaction The open transaction. - * + * @param fsType The type. + * @param attrType The type attribute given to the file by the file + * system. + * @param attrId The type id given to the file by the file system. + * @param name The name of the file. + * @param metaAddr The meta address of the file. + * @param metaSeq The meta sequence number of the file. + * @param dirType The type of the file, usually as reported in the + * name structure of the file system. + * @param metaType The type of the file, usually as reported in the + * metadata structure of the file system. + * @param dirFlags The allocated status of the file, usually as + * reported in the name structure of the file system. + * @param metaFlags The allocated status of the file, usually as + * reported in the metadata structure of the file + * system. + * @param size The file size. + * @param crtime The created time. + * @param ctime The last changed time + * @param atime The last accessed time. + * @param mtime The last modified time. + * @param meta_mode The modes for the file. + * @param gid The group identifier. + * @param uid The user identifier. + * @param md5 The MD5 hash. + * @param known The file known status. + * @param escaped_path The escaped path to the file. + * @param extension The file extension. + * @param hasLayout True if this is a layout file, false otherwise. + * @param transaction The open transaction. + * * @return The object ID of the new file system - * - * @throws TskCoreException + * + * @throws TskCoreException */ - long addFileJNI(long parentObjId, + long addFileJNI(long parentObjId, Long fsObjId, long dataSourceObjId, int fsType, Integer attrType, Integer attrId, String name, @@ -11153,7 +11155,7 @@ long addFileJNI(long parentObjId, Long crtime, Long ctime, Long atime, Long mtime, Integer meta_mode, Integer gid, Integer uid, String md5, TskData.FileKnown known, - String escaped_path, String extension, + String escaped_path, String extension, boolean hasLayout, CaseDbTransaction transaction) throws TskCoreException { Statement queryStatement = null; @@ -11177,7 +11179,7 @@ long addFileJNI(long parentObjId, } statement.setLong(2, objectId); // obj_id statement.setLong(3, dataSourceObjId); // data_source_obj_id - statement.setShort(4, (short)fsType); // type + statement.setShort(4, (short) fsType); // type if (attrType != null) { statement.setShort(5, attrType.shortValue()); // attr_type } else { @@ -11199,10 +11201,10 @@ long addFileJNI(long parentObjId, } else { statement.setNull(9, java.sql.Types.INTEGER); } - statement.setShort(10, (short)dirType); // dir_type - statement.setShort(11, (short)metaType); // meta_type - statement.setShort(12, (short)dirFlags); // dir_flags - statement.setShort(13, (short)metaFlags); // meta_flags + statement.setShort(10, (short) dirType); // dir_type + statement.setShort(11, (short) metaType); // meta_type + statement.setShort(12, (short) dirFlags); // dir_flags + statement.setShort(13, (short) metaFlags); // meta_flags statement.setLong(14, size < 0 ? 0 : size); // size if (crtime != null) { statement.setLong(15, crtime); // crtime @@ -11251,20 +11253,20 @@ long addFileJNI(long parentObjId, connection.executeUpdate(statement); // If this is not a slack file create the timeline events - if (! hasLayout + if (!hasLayout && TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() != fsType && (!name.equals(".")) && (!name.equals(".."))) { TimelineManager timelineManager = getTimelineManager(); - DerivedFile derivedFile = new DerivedFile(this, objectId, dataSourceObjId, name, - TSK_FS_NAME_TYPE_ENUM.valueOf((short)dirType), - TSK_FS_META_TYPE_ENUM.valueOf((short)metaType), - TSK_FS_NAME_FLAG_ENUM.valueOf(dirFlags), - (short)metaFlags, + DerivedFile derivedFile = new DerivedFile(this, objectId, dataSourceObjId, name, + TSK_FS_NAME_TYPE_ENUM.valueOf((short) dirType), + TSK_FS_META_TYPE_ENUM.valueOf((short) metaType), + TSK_FS_NAME_FLAG_ENUM.valueOf(dirFlags), + (short) metaFlags, size, ctime, crtime, atime, mtime, null, null, escaped_path, null, parentObjId, null, null, extension); - timelineManager.addEventsForNewFileQuiet(derivedFile, connection); + timelineManager.addEventsForNewFileQuiet(derivedFile, connection); } - + return objectId; } catch (SQLException ex) { throw new TskCoreException("Failed to add file system file", ex); @@ -11273,28 +11275,28 @@ long addFileJNI(long parentObjId, releaseSingleUserCaseWriteLock(); } } - + /** - * Add a layout file range to the database. - * For use with the JNI callbacks associated with the add image process. - * - * @param objId Object ID of the layout file. - * @param byteStart Start byte. - * @param byteLen Length in bytes. - * @param seq Sequence number of this range. + * Add a layout file range to the database. For use with the JNI callbacks + * associated with the add image process. + * + * @param objId Object ID of the layout file. + * @param byteStart Start byte. + * @param byteLen Length in bytes. + * @param seq Sequence number of this range. * @param transaction The open transaction. - * - * @throws TskCoreException + * + * @throws TskCoreException */ - void addLayoutFileRangeJNI(long objId, long byteStart, long byteLen, + void addLayoutFileRangeJNI(long objId, long byteStart, long byteLen, long seq, CaseDbTransaction transaction) throws TskCoreException { try { acquireSingleUserCaseWriteLock(); CaseDbConnection connection = transaction.getConnection(); - + PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LAYOUT_FILE); prepStmt.clearParameters(); - prepStmt.setLong(1, objId); + prepStmt.setLong(1, objId); prepStmt.setLong(2, byteStart); prepStmt.setLong(3, byteLen); prepStmt.setLong(4, seq); @@ -11305,7 +11307,7 @@ void addLayoutFileRangeJNI(long objId, long byteStart, long byteLen, releaseSingleUserCaseWriteLock(); } } - + /** * Stores a pair of object ID and its type */ @@ -11436,7 +11438,7 @@ private enum PREPARED_STATEMENT { + " AND content_tags.tag_name_id = ? " + " AND tsk_files.data_source_obj_id = ? " ), - SELECT_CONTENT_TAGS("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + SELECT_CONTENT_TAGS("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id, tag_names.rank " + "FROM content_tags " + "INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id"), //NON-NLS @@ -11451,12 +11453,12 @@ private enum PREPARED_STATEMENT { + " AND content_tags.tag_name_id = tag_names.tag_name_id" + " AND content_tags.tag_name_id = ?" + " AND tsk_files.data_source_obj_id = ? "), - SELECT_CONTENT_TAG_BY_ID("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + SELECT_CONTENT_TAG_BY_ID("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id, tag_names.rank " + "FROM content_tags " + "INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id " + "WHERE tag_id = ?"), //NON-NLS - SELECT_CONTENT_TAGS_BY_CONTENT("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + SELECT_CONTENT_TAGS_BY_CONTENT("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id, tag_names.rank " + "FROM content_tags " + "INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id " @@ -11464,7 +11466,7 @@ private enum PREPARED_STATEMENT { INSERT_ARTIFACT_TAG("INSERT INTO blackboard_artifact_tags (artifact_id, tag_name_id, comment, examiner_id) " + "VALUES (?, ?, ?, ?)"), //NON-NLS DELETE_ARTIFACT_TAG("DELETE FROM blackboard_artifact_tags WHERE tag_id = ?"), //NON-NLS - SELECT_ARTIFACT_TAGS("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " + SELECT_ARTIFACT_TAGS("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tag_names.tag_set_id, tsk_examiners.login_name, tag_names.rank " + "FROM blackboard_artifact_tags " + "INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id"), //NON-NLS @@ -11482,12 +11484,12 @@ private enum PREPARED_STATEMENT { + " AND artifact_tags.artifact_id = arts.artifact_id" + " AND artifact_tags.tag_name_id = ? " + " AND arts.data_source_obj_id = ? "), - SELECT_ARTIFACT_TAG_BY_ID("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + SELECT_ARTIFACT_TAG_BY_ID("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id, tag_names.rank " + "FROM blackboard_artifact_tags " + "INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id " + "WHERE blackboard_artifact_tags.tag_id = ?"), //NON-NLS - SELECT_ARTIFACT_TAGS_BY_ARTIFACT("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + SELECT_ARTIFACT_TAGS_BY_ARTIFACT("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id, tag_names.rank " + "FROM blackboard_artifact_tags " + "INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id " @@ -11524,7 +11526,8 @@ private enum PREPARED_STATEMENT { INSERT_POOL_INFO("INSERT INTO tsk_pool_info (obj_id, pool_type) VALUES (?, ?)"), INSERT_FS_INFO("INSERT INTO tsk_fs_info (obj_id, data_source_obj_id, img_offset, fs_type, block_size, block_count, root_inum, first_inum, last_inum, display_name)" + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), - SELECT_OBJ_ID_BY_META_ADDR_AND_PATH("SELECT obj_id FROM tsk_files WHERE meta_addr = ? AND fs_obj_id = ? AND parent_path = ? AND name = ?"); + SELECT_OBJ_ID_BY_META_ADDR_AND_PATH("SELECT obj_id FROM tsk_files WHERE meta_addr = ? AND fs_obj_id = ? AND parent_path = ? AND name = ?"), + SELECT_TAG_NAME_BY_ID("SELECT * FROM tag_names where tag_name_id = ?"); private final String sql; @@ -12083,11 +12086,16 @@ void executeCommand(DbCommand command) throws SQLException { * Transaction interface because that sort of flexibility and its associated * complexity is not needed. Also, TskCoreExceptions are thrown to be * consistent with the outer SleuthkitCase class. + * + * This class will automatically acquire the single user case write lock + * and release it when the transaction is closed. Otherwise we risk deadlock + * because this transaction can lock up SQLite and make it "busy" and + * another thread may get a write lock to the DB, but not + * be able to do anything because the DB is busy. */ public static final class CaseDbTransaction { private final CaseDbConnection connection; - private boolean hasWriteLock = false; private SleuthkitCase sleuthkitCase; private CaseDbTransaction(SleuthkitCase sleuthkitCase, CaseDbConnection connection) throws TskCoreException { @@ -12098,6 +12106,7 @@ private CaseDbTransaction(SleuthkitCase sleuthkitCase, CaseDbConnection connecti } catch (SQLException ex) { throw new TskCoreException("Failed to create transaction on case database", ex); } + sleuthkitCase.acquireSingleUserCaseWriteLock(); } /** @@ -12111,23 +12120,6 @@ CaseDbConnection getConnection() { return this.connection; } - /** - * Obtain a write lock for this transaction. Only one will be obtained - * (no matter how many times it is called) and will be released when - * commit or rollback is called. - * - * If this is not used, you risk deadlock because this transaction can - * lock up SQLite and make it "busy" and another thread may get a write - * lock to the DB, but not be able to do anything because the DB is - * busy. - */ - void acquireSingleUserCaseWriteLock() { - if (!hasWriteLock) { - hasWriteLock = true; - sleuthkitCase.acquireSingleUserCaseWriteLock(); - } - } - /** * Commits the transaction on the case database that was begun when this * object was constructed. @@ -12166,10 +12158,7 @@ public void rollback() throws TskCoreException { */ void close() { this.connection.close(); - if (hasWriteLock) { - sleuthkitCase.releaseSingleUserCaseWriteLock(); - hasWriteLock = false; - } + sleuthkitCase.releaseSingleUserCaseWriteLock(); } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/TagName.java b/bindings/java/src/org/sleuthkit/datamodel/TagName.java index 2bb7b92f3747c4407ef42c18b3dc1cbc864d2b24..769971b5e9be1776d6a8aab10a5fce03c3db4971 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/TagName.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TagName.java @@ -87,15 +87,17 @@ public static HTML_COLOR getColorByName(String colorName) { private final HTML_COLOR color; private final TskData.FileKnown knownStatus; private final long tagSetId; + private final int rank; // Clients of the org.sleuthkit.datamodel package should not directly create these objects. - TagName(long id, String displayName, String description, HTML_COLOR color, TskData.FileKnown knownStatus, long tagSetId) { + TagName(long id, String displayName, String description, HTML_COLOR color, TskData.FileKnown knownStatus, long tagSetId, int rank) { this.id = id; this.displayName = displayName; this.description = description; this.color = color; this.knownStatus = knownStatus; this.tagSetId = tagSetId; + this.rank = rank; } public long getId() { @@ -121,6 +123,10 @@ public TskData.FileKnown getKnownStatus() { long getTagSetId() { return tagSetId; } + + public int getRank() { + return rank; + } /** * Compares two TagName objects by comparing their display names. diff --git a/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java b/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java index 69043f0391a3ceb704d619c0b7704c96103ee90d..29826d973765b201532398a5c6c391262281d7ad 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java @@ -109,20 +109,16 @@ public TagSet addTagSet(String name, List<TagName> tagNames) throws TskCoreExcep if (tagNames != null) { // Get all of the TagName ids they can be updated in one // SQL call. - List<String> idList = new ArrayList<>(); - for (TagName tagName : tagNames) { - idList.add(Long.toString(tagName.getId())); - } - - stmt.executeUpdate(String.format("UPDATE tag_names SET tag_set_id = %d WHERE tag_name_id IN (%s)", setID, String.join(",", idList))); - - for (TagName tagName : tagNames) { + for (int index = 0; index < tagNames.size(); index++) { + TagName tagName = tagNames.get(index); + stmt.executeUpdate(String.format("UPDATE tag_names SET tag_set_id = %d, rank = %d WHERE tag_name_id = %d", setID, index, tagName.getId())); updatedTags.add(new TagName(tagName.getId(), tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus(), - setID)); + setID, + index)); } } tagSet = new TagSet(setID, name, updatedTags); @@ -140,11 +136,12 @@ public TagSet addTagSet(String name, List<TagName> tagNames) throws TskCoreExcep } /** - * Remove a row from the tag set table. The TagNames in the TagSet will not - * be deleted, nor will any tags with the TagNames from the deleted tag set - * be deleted. + * Remove a row from the tag set table. If the given TagSet has a valid list + * of TagNames the TagNames will be removed from the tag_name table if there + * are not references to the TagNames in the content_tag or + * blackboard_artifact_tag table. * - * @param tagSet TagSet to be deleted + * @param tagSet TagSet to be deleted. * * @throws TskCoreException */ @@ -153,19 +150,26 @@ public void deleteTagSet(TagSet tagSet) throws TskCoreException { throw new IllegalArgumentException("Error adding deleting TagSet, TagSet object was null"); } - CaseDbConnection connection = skCase.getConnection(); - skCase.acquireSingleUserCaseWriteLock(); - try (Statement stmt = connection.createStatement()) { - connection.beginTransaction(); - String queryTemplate = "DELETE FROM tsk_tag_sets WHERE tag_set_id = '%d'"; - stmt.execute(String.format(queryTemplate, tagSet.getId())); - connection.commitTransaction(); - } catch (SQLException ex) { - connection.rollbackTransaction(); - throw new TskCoreException(String.format("Error deleting tag set where id = %d.", tagSet.getId()), ex); - } finally { - connection.close(); - skCase.releaseSingleUserCaseWriteLock(); + if (isTagSetInUse(tagSet)) { + throw new TskCoreException("Unable to delete TagSet (%d). TagSet TagName list contains TagNames that are currently in use."); + } + + try (CaseDbConnection connection = skCase.getConnection()) { + skCase.acquireSingleUserCaseWriteLock(); + try (Statement stmt = connection.createStatement()) { + connection.beginTransaction(); + String queryTemplate = "DELETE FROM tag_names WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE tag_set_id = %d)"; + stmt.execute(String.format(queryTemplate, tagSet.getId())); + + queryTemplate = "DELETE FROM tsk_tag_sets WHERE tag_set_id = '%d'"; + stmt.execute(String.format(queryTemplate, tagSet.getId())); + connection.commitTransaction(); + } catch (SQLException ex) { + connection.rollbackTransaction(); + throw new TskCoreException(String.format("Error deleting tag set where id = %d.", tagSet.getId()), ex); + } finally { + skCase.releaseSingleUserCaseWriteLock(); + } } } @@ -205,7 +209,7 @@ public TagSet addTagNameToTagSet(TagSet tagSet, TagName tagName) throws TskCoreE List<TagName> newTagNameList = new ArrayList<>(); newTagNameList.addAll(setTagNameList); - newTagNameList.add(new TagName(tagName.getId(), tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus(), tagSet.getId())); + newTagNameList.add(new TagName(tagName.getId(), tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus(), tagSet.getId(), tagName.getRank())); return new TagSet(tagSet.getId(), tagSet.getName(), newTagNameList); @@ -258,9 +262,10 @@ public BlackboardArtifactTagChange addArtifactTag(BlackboardArtifact artifact, T resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), - tagSetId + tagSetId, + resultSet.getInt("rank") ); - + BlackboardArtifactTag bat = new BlackboardArtifactTag(resultSet.getLong("tag_id"), artifact, @@ -353,9 +358,10 @@ public ContentTagChange addContentTag(Content content, TagName tagName, String c resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), - tagSetId + tagSetId, + resultSet.getInt("rank") ); - + ContentTag bat = new ContentTag(resultSet.getLong("tag_id"), content, @@ -414,6 +420,48 @@ public ContentTagChange addContentTag(Content content, TagName tagName, String c } } + /** + * Determine if the given TagSet contains TagNames that are currently in + * use, ie there is an existing ContentTag or ArtifactTag that uses TagName. + * + * @param tagSet The Tagset to check. + * + * @return Return true if the TagSet is in use. + * + * @throws TskCoreException + */ + private boolean isTagSetInUse(TagSet tagSet) throws TskCoreException { + try (CaseDbConnection connection = skCase.getConnection()) { + List<TagName> tagNameList = tagSet.getTagNames(); + if (tagNameList != null && !tagNameList.isEmpty()) { + skCase.acquireSingleUserCaseReadLock(); + try { + String statement = String.format("SELECT tag_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE tag_set_id = %d)", tagSet.getId()); + try (Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(statement)) { + if (resultSet.next()) { + return true; + } + } catch (SQLException ex) { + throw new TskCoreException(String.format("Failed to determine if TagSet is in use (%s)", tagSet.getId()), ex); + } + + statement = String.format("SELECT tag_id FROM blackboard_artifact_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE tag_set_id = %d)", tagSet.getId()); + try (Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(statement)) { + if (resultSet.next()) { + return true; + } + } catch (SQLException ex) { + throw new TskCoreException(String.format("Failed to determine if TagSet is in use (%s)", tagSet.getId()), ex); + } + } finally { + skCase.releaseSingleUserCaseReadLock(); + } + } + } + + return false; + } + /** * Returns a list of all of the TagNames that are apart of the given TagSet. * @@ -437,13 +485,13 @@ private List<TagName> getTagNamesByTagSetID(int tagSetId) throws TskCoreExceptio String query = String.format("SELECT * FROM tag_names WHERE tag_set_id = %d", tagSetId); try (Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(query)) { while (resultSet.next()) { - long tagId = resultSet.getLong("tag_name_id"); - String tagName = resultSet.getString("display_name"); - String description = resultSet.getString("description"); - String color = resultSet.getString("color"); - byte knownStatus = resultSet.getByte("knownStatus"); - - tagNameList.add(new TagName(tagId, tagName, description, TagName.HTML_COLOR.getColorByName(color), TskData.FileKnown.valueOf(knownStatus), tagSetId)); + tagNameList.add(new TagName(resultSet.getLong("tag_name_id"), + resultSet.getString("display_name"), + resultSet.getString("description"), + TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), + tagSetId, + resultSet.getInt("rank"))); } } catch (SQLException ex) { throw new TskCoreException(String.format("Error getting tag names for tag set (%d)", tagSetId), ex); diff --git a/bindings/java/test/org/sleuthkit/datamodel/CommunicationsManagerTest.java b/bindings/java/test/org/sleuthkit/datamodel/CommunicationsManagerTest.java index d71ddd4e35e66e5ed5da6c5fce5d2ba33730b6b1..d2e025b95d3fd6a6b0cc8baf5afe6cf0874934b2 100644 --- a/bindings/java/test/org/sleuthkit/datamodel/CommunicationsManagerTest.java +++ b/bindings/java/test/org/sleuthkit/datamodel/CommunicationsManagerTest.java @@ -253,7 +253,6 @@ public static void setUpClass() { System.out.println("CommsMgr Test DB created at: " + dbPath); SleuthkitCase.CaseDbTransaction trans = caseDB.beginTransaction(); - trans.acquireSingleUserCaseWriteLock(); LocalFilesDataSource dataSource_1 = caseDB.addLocalFilesDataSource(DS1_DEVICEID, ROOTDIR_1, "", trans); LocalFilesDataSource dataSource_2 = caseDB.addLocalFilesDataSource(DS2_DEVICEID, ROOTDIR_2, "", trans);