Skip to content
Snippets Groups Projects
Unverified Commit 6bee0d5c authored by Richard Cordovano's avatar Richard Cordovano Committed by GitHub
Browse files

Merge pull request #2395 from APriestman/rootDirCache

Adding caching to isRootDirectory()
parents 0ae69b3d 53395006
No related branches found
No related tags found
No related merge requests found
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
*/ */
package org.sleuthkit.datamodel; package org.sleuthkit.datamodel;
   
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.EventBus;
import com.mchange.v2.c3p0.ComboPooledDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource;
...@@ -57,11 +59,13 @@ ...@@ -57,11 +59,13 @@
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.MissingResourceException; import java.util.MissingResourceException;
import java.util.Objects;
import java.util.Properties; import java.util.Properties;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -194,6 +198,12 @@ public class SleuthkitCase { ...@@ -194,6 +198,12 @@ public class SleuthkitCase {
private Map<String, BlackboardAttribute.Type> typeNameToAttributeTypeMap; private Map<String, BlackboardAttribute.Type> typeNameToAttributeTypeMap;
private CaseDbSchemaVersionNumber caseDBSchemaCreationVersion; private CaseDbSchemaVersionNumber caseDBSchemaCreationVersion;
   
// Objects for caching the result of isRootDirectory(). Lock is for visibility only.
private final Object rootDirectoryMapLock = new Object();
private final Map<RootDirectoryKey, Long> rootDirectoryMap = new HashMap<>();
private final Cache<Long, Boolean> isRootDirectoryCache =
CacheBuilder.newBuilder().maximumSize(200000).expireAfterAccess(5, TimeUnit.MINUTES).build();
/* /*
* First parameter is used to specify the SparseBitSet to use, as object IDs * First parameter is used to specify the SparseBitSet to use, as object IDs
* can be larger than the max size of a SparseBitSet * can be larger than the max size of a SparseBitSet
...@@ -7923,6 +7933,52 @@ public LocalFile addLocalFile(String fileName, String localPath, ...@@ -7923,6 +7933,52 @@ public LocalFile addLocalFile(String fileName, String localPath,
closeStatement(queryStatement); closeStatement(queryStatement);
} }
} }
/**
* Utility class to create keys for the cache used in isRootDirectory().
* The dataSourceId must be set but the fileSystemId can be null
* (for local directories, for example).
*/
private class RootDirectoryKey {
private long dataSourceId;
private Long fileSystemId;
RootDirectoryKey(long dataSourceId, Long fileSystemId) {
this.dataSourceId = dataSourceId;
this.fileSystemId = fileSystemId;
}
@Override
public int hashCode() {
int hash = 7;
hash = 41 * hash + Objects.hashCode(dataSourceId);
hash = 41 * hash + Objects.hashCode(fileSystemId);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
RootDirectoryKey otherKey = (RootDirectoryKey)obj;
if (dataSourceId != otherKey.dataSourceId) {
return false;
}
if (fileSystemId != null) {
return fileSystemId.equals(otherKey.fileSystemId);
}
return (otherKey.fileSystemId == null);
}
}
   
/** /**
* Check whether a given AbstractFile is the "root" directory. True if the * Check whether a given AbstractFile is the "root" directory. True if the
...@@ -7937,6 +7993,29 @@ public LocalFile addLocalFile(String fileName, String localPath, ...@@ -7937,6 +7993,29 @@ public LocalFile addLocalFile(String fileName, String localPath,
* @throws TskCoreException * @throws TskCoreException
*/ */
private boolean isRootDirectory(AbstractFile file, CaseDbTransaction transaction) throws TskCoreException { private boolean isRootDirectory(AbstractFile file, CaseDbTransaction transaction) throws TskCoreException {
// First check if we know the root directory for this data source and optionally
// file system. There is only one root, so if we know it we can simply compare
// this file ID to the known root directory.
Long fsObjId = null;
if (file instanceof FsContent) {
fsObjId = ((FsContent)file).getFileSystemId();
}
RootDirectoryKey key = new RootDirectoryKey(file.getDataSourceObjectId(), fsObjId);
synchronized(rootDirectoryMapLock) {
if (rootDirectoryMap.containsKey(key)) {
return rootDirectoryMap.get(key).equals(file.getId());
}
}
// Fallback cache. We store the result of each database lookup
// so it won't be done multiple times in a row. In practice, this will
// only be used if this method was never called on the root directory.
Boolean isRoot = isRootDirectoryCache.getIfPresent(file.getId());
if (isRoot != null) {
return isRoot;
}
CaseDbConnection connection = transaction.getConnection(); CaseDbConnection connection = transaction.getConnection();
Statement statement = null; Statement statement = null;
ResultSet resultSet = null; ResultSet resultSet = null;
...@@ -7954,12 +8033,26 @@ private boolean isRootDirectory(AbstractFile file, CaseDbTransaction transaction ...@@ -7954,12 +8033,26 @@ private boolean isRootDirectory(AbstractFile file, CaseDbTransaction transaction
return true; return true;
} }
int type = resultSet.getInt("parent_type"); int type = resultSet.getInt("parent_type");
return (type == TskData.ObjectType.IMG.getObjectType() boolean result = type == TskData.ObjectType.IMG.getObjectType()
|| type == TskData.ObjectType.VS.getObjectType() || type == TskData.ObjectType.VS.getObjectType()
|| type == TskData.ObjectType.VOL.getObjectType() || type == TskData.ObjectType.VOL.getObjectType()
|| type == TskData.ObjectType.FS.getObjectType()); || type == TskData.ObjectType.FS.getObjectType();
if (result == true) {
synchronized(rootDirectoryMapLock) {
// This is a root directory so save it
rootDirectoryMap.put(key, file.getId());
}
}
isRootDirectoryCache.put(file.getId(), result);
return result;
   
} else { } else {
// This is a root directory so save it
synchronized(rootDirectoryMapLock) {
rootDirectoryMap.put(key, file.getId());
}
isRootDirectoryCache.put(file.getId(), true);
return true; // The file has no parent return true; // The file has no parent
} }
} catch (SQLException ex) { } catch (SQLException ex) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment