Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 55 additions & 5 deletions bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -3080,9 +3080,33 @@ public String getBackupDatabasePath() {
* @throws TskCoreException
*/
public CaseDbTransaction beginTransaction() throws TskCoreException {
return new CaseDbTransaction(this);
return new CaseDbTransaction(this, false);
}

/**
* <p>Create a new transaction on the case database. The transaction object
* 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.</p>
*
* <p>Note that this beginning the transaction also acquires the single user
* case read lock, which will be automatically released when the
* transaction is closed.</p>
*
* <p><strong>WARNING:</strong> This API should only be used if the transaction is
* guaranteed to only ever perform reads and no updates to the database.
* Undefined behavior can occur if this API is used with database updates.</p>
*
* @return A CaseDbTransaction object.
*
* @throws TskCoreException
*/
@Beta
public CaseDbTransaction beginReadOnlyTransaction() throws TskCoreException {
return new CaseDbTransaction(this, true);
}


/**
* Gets the case database name.
*
Expand Down Expand Up @@ -14458,6 +14482,7 @@ void executeCommand(DbCommand command) throws SQLException {
public static final class CaseDbTransaction {

private final CaseDbConnection connection;
private final boolean readOnlyTransaction;
private SleuthkitCase sleuthkitCase;

/* This class can store information about what was
Expand All @@ -14474,23 +14499,44 @@ public static final class CaseDbTransaction {

private List<Long> deletedOsAccountObjectIds = new ArrayList<>();
private List<Long> deletedResultObjectIds = new ArrayList<>();


// Keep track of which threads have connections to debug deadlocks
private static Set<Long> threadsWithOpenTransaction = new HashSet<>();
private static final Object threadsWithOpenTransactionLock = new Object();

private CaseDbTransaction(SleuthkitCase sleuthkitCase) throws TskCoreException {
/**
* Constructor for a case database transaction.
*
* @param sleuthkitCase The TSK case.
* @param readOnlyTransaction True if the transaction will not make any
* writes to the database and therefore does
* not need the write lock.
*
* @throws TskCoreException
*/
private CaseDbTransaction(SleuthkitCase sleuthkitCase, boolean readOnlyTransaction) throws TskCoreException {
this.sleuthkitCase = sleuthkitCase;
this.readOnlyTransaction = readOnlyTransaction;

sleuthkitCase.acquireSingleUserCaseWriteLock();
if (readOnlyTransaction) {
sleuthkitCase.acquireSingleUserCaseReadLock();
} else {
sleuthkitCase.acquireSingleUserCaseWriteLock();
}

this.connection = sleuthkitCase.getConnection();
try {
synchronized (threadsWithOpenTransactionLock) {
this.connection.beginTransaction();
threadsWithOpenTransaction.add(Thread.currentThread().getId());
}
} catch (SQLException ex) {
sleuthkitCase.releaseSingleUserCaseWriteLock();
if (readOnlyTransaction) {
sleuthkitCase.releaseSingleUserCaseReadLock();
} else {
sleuthkitCase.releaseSingleUserCaseWriteLock();
}
throw new TskCoreException("Failed to create transaction on case database", ex);
}

Expand Down Expand Up @@ -14671,7 +14717,11 @@ public void rollback() throws TskCoreException {
*/
void close() {
this.connection.close();
sleuthkitCase.releaseSingleUserCaseWriteLock();
if (readOnlyTransaction) {
sleuthkitCase.releaseSingleUserCaseReadLock();
} else {
sleuthkitCase.releaseSingleUserCaseWriteLock();
}
synchronized (threadsWithOpenTransactionLock) {
threadsWithOpenTransaction.remove(Thread.currentThread().getId());
}
Expand Down