diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java index 123551396e0a21cabfd25e12c5b8277a6a151086..e6c08af852c8b49b830ef902e4d9cc853f9a4868 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java @@ -223,7 +223,7 @@ private void createFileTables(Statement stmt) throws SQLException { + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(fs_obj_id) REFERENCES tsk_fs_info(obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE, " - + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id)) " ); + + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE SET NULL) " ); stmt.execute("CREATE TABLE file_encoding_types (encoding_type INTEGER PRIMARY KEY, name TEXT NOT NULL)"); @@ -418,7 +418,7 @@ private void createHostTables(Statement stmt) throws SQLException { + "person_id INTEGER, " + "merged_into " + dbQueryHelper.getBigIntType() + ", " + "FOREIGN KEY(person_id) REFERENCES tsk_persons(id) ON DELETE SET NULL, " - + "FOREIGN KEY(merged_into) REFERENCES tsk_hosts(id), " + + "FOREIGN KEY(merged_into) REFERENCES tsk_hosts(id) ON DELETE CASCADE, " + "UNIQUE(name)) "); stmt.execute("CREATE TABLE tsk_host_addresses (id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, " @@ -491,8 +491,8 @@ private void createAccountTables(Statement stmt) throws SQLException { + "db_status INTEGER DEFAULT 0, " // active/merged/deleted + "merged_into " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, " + "UNIQUE(realm_signature), " - + "FOREIGN KEY(scope_host_id) REFERENCES tsk_hosts(id)," - + "FOREIGN KEY(merged_into) REFERENCES tsk_os_account_realms(id) )"); + + "FOREIGN KEY(scope_host_id) REFERENCES tsk_hosts(id) ON DELETE CASCADE," + + "FOREIGN KEY(merged_into) REFERENCES tsk_os_account_realms(id) ON DELETE CASCADE )"); // References tsk_objects, tsk_os_account_realms, tsk_persons stmt.execute("CREATE TABLE tsk_os_accounts (os_account_obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, " @@ -508,8 +508,8 @@ private void createAccountTables(Statement stmt) throws SQLException { + "merged_into " + dbQueryHelper.getBigIntType() + " DEFAULT NULL, " + "UNIQUE(signature, realm_id), " + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, " - + "FOREIGN KEY(realm_id) REFERENCES tsk_os_account_realms(id)," - + "FOREIGN KEY(merged_into) REFERENCES tsk_os_accounts(os_account_obj_id) )"); + + "FOREIGN KEY(realm_id) REFERENCES tsk_os_account_realms(id) ON DELETE CASCADE," + + "FOREIGN KEY(merged_into) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE )"); } // Must be called after createAccountTables() and blackboard_attribute_types, blackboard_artifacts creation. @@ -526,8 +526,8 @@ private void createAccountInstancesAndArtifacts(Statement stmt) throws SQLExcept + "value_text TEXT, " + "value_int32 INTEGER, value_int64 " + dbQueryHelper.getBigIntType() + ", " + "value_double NUMERIC(20, 10), " - + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id), " - + "FOREIGN KEY(host_id) REFERENCES tsk_hosts(id), " + + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE, " + + "FOREIGN KEY(host_id) REFERENCES tsk_hosts(id) ON DELETE CASCADE, " + "FOREIGN KEY(source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE SET NULL, " + "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))"); @@ -537,7 +537,7 @@ private void createAccountInstancesAndArtifacts(Statement stmt) throws SQLExcept + "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " + "instance_type INTEGER NOT NULL, " // PerformedActionOn/ReferencedOn + "UNIQUE(os_account_obj_id, data_source_obj_id), " - + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id), " + + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE ) "); // References blackboard_artifacts, tsk_os_accounts @@ -545,7 +545,7 @@ private void createAccountInstancesAndArtifacts(Statement stmt) throws SQLExcept + "artifact_obj_id " + dbQueryHelper.getBigIntType() + " PRIMARY KEY, " + "os_account_obj_id " + dbQueryHelper.getBigIntType() + ", " + "FOREIGN KEY(artifact_obj_id) REFERENCES blackboard_artifacts(artifact_obj_id) ON DELETE CASCADE, " - + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id)) "); + + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE SET NULL) "); } private void createEventTables(Statement stmt) throws SQLException { diff --git a/bindings/java/src/org/sleuthkit/datamodel/HostManager.java b/bindings/java/src/org/sleuthkit/datamodel/HostManager.java index 2e620df94b2182b959f97bdf9cd20de71d07fc85..d8255b2e7823a93e715bb3264b3f2794ac95f6ab 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/HostManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/HostManager.java @@ -435,11 +435,23 @@ public List<Host> getAllHosts() throws TskCoreException { * @throws TskCoreException if no host is found or an error occurs. */ public Host getHostByDataSource(DataSource dataSource) throws TskCoreException { - + return getHostByDataSource(dataSource.getId()); + } + + /** + * Get host for the given data source ID. + * + * @param dataSourceId The data source ID to look up the host for. + * + * @return The host for this data source (will not be null). + * + * @throws TskCoreException if no host is found or an error occurs. + */ + Host getHostByDataSource(long dataSourceId) throws TskCoreException { String queryString = "SELECT tsk_hosts.id AS hostId, tsk_hosts.name AS name, tsk_hosts.db_status AS db_status FROM \n" + "tsk_hosts INNER JOIN data_source_info \n" + "ON tsk_hosts.id = data_source_info.host_id \n" - + "WHERE data_source_info.obj_id = " + dataSource.getId(); + + "WHERE data_source_info.obj_id = " + dataSourceId; db.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = this.db.getConnection(); @@ -447,12 +459,12 @@ public Host getHostByDataSource(DataSource dataSource) throws TskCoreException { ResultSet rs = connection.executeQuery(s, queryString)) { if (!rs.next()) { - throw new TskCoreException(String.format("Host not found for data source with ID = %d", dataSource.getId())); + throw new TskCoreException(String.format("Host not found for data source with ID = %d", dataSourceId)); } else { return new Host(rs.getLong("hostId"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status"))); } } catch (SQLException ex) { - throw new TskCoreException(String.format("Error getting host for data source with ID = %d", dataSource.getId()), ex); + throw new TskCoreException(String.format("Error getting host for data source with ID = %d", dataSourceId), ex); } finally { db.releaseSingleUserCaseReadLock(); } diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 6b5d7d7b11133c10c63d619be553adf57192388e..32326ed21baf4cf23831a6bf61519d889e7375bd 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -9782,6 +9782,22 @@ public void setImagePaths(long obj_id, List<String> paths) throws TskCoreExcepti * within tsk core and the update fails */ void deleteDataSource(long dataSourceObjectId) throws TskCoreException { + + // Check if this data source is the only one associated with its host. If so, + // we will delete the host and other associated data. + // Note that the cascading deletes were only added in schema 9.1, so we + // would get an error trying to delete a host from older cases. + Host hostToDelete = null; + VersionNumber version = getDBSchemaCreationVersion(); + int major = version.getMajor(); + int minor = version.getMinor(); + if(major > 9 || (major == 9 && minor >= 1)) { + hostToDelete = getHostManager().getHostByDataSource(dataSourceObjectId); + if (getHostManager().getDataSourcesForHost(hostToDelete).size() != 1) { + hostToDelete = null; + } + } + CaseDbConnection connection = null; Statement statement; acquireSingleUserCaseWriteLock(); @@ -9798,6 +9814,19 @@ void deleteDataSource(long dataSourceObjectId) throws TskCoreException { + "WHERE account_id NOT IN (SELECT account1_id FROM account_relationships) " + "AND account_id NOT IN (SELECT account2_id FROM account_relationships))"; statement.execute(accountSql); + + // Now delete any host that was only associated with this data source. This will cascade to delete + // realms, os accounts, and os account attributes that were associated with the host. + if (hostToDelete != null) { + statement.execute("DELETE FROM tsk_hosts WHERE id = " + hostToDelete.getHostId()); + + // Clean up any stray OS Account objects + String deleteOsAcctObjectsQuery = "DELETE FROM tsk_objects " + + "WHERE type=" + TskData.ObjectType.OS_ACCOUNT.getObjectType() + " " + + "AND obj_id NOT IN (SELECT os_account_obj_id FROM tsk_os_accounts WHERE os_account_obj_id IS NOT NULL)"; + statement.execute(deleteOsAcctObjectsQuery); + } + connection.commitTransaction(); } catch (SQLException ex) { rollbackTransaction(connection);