diff --git a/bindings/java/src/org/sleuthkit/datamodel/FileManager.java b/bindings/java/src/org/sleuthkit/datamodel/FileManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..e285708c5bca99f9c7c643f40bb6b3d18eb0ae72
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/FileManager.java
@@ -0,0 +1,111 @@
+/*
+ * SleuthKit Java Bindings
+ *
+ * Copyright 2021 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> 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.datamodel;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Utility class for file-based database queries.
+ */
+public class FileManager {
+
+	private final SleuthkitCase skCase;
+
+	/**
+	 * Constructs a FileManager.
+	 *
+	 * @param casedb The case database.
+	 */
+	FileManager(SleuthkitCase skCase) {
+		this.skCase = Objects.requireNonNull(skCase, "Cannot create Blackboard for null SleuthkitCase");
+	}
+	
+	/**
+     * Find all files with the exact given name and parentId.
+     * 
+     * @param parentId Id of the parent folder to search.
+     * @param name Exact file name to match.
+     * 
+     * @return A list of matching files.
+     * 
+     * @throws TskCoreException 
+     */
+    public List<AbstractFile> findFilesExactName(long parentId, String name) throws TskCoreException {
+		String query = "SELECT tsk_files.* FROM tsk_files JOIN tsk_objects ON tsk_objects.obj_id = tsk_files.obj_id WHERE par_obj_id = ? AND name = ?";
+		skCase.acquireSingleUserCaseReadLock();
+		try (SleuthkitCase.CaseDbConnection connection = skCase.getConnection()) {
+			PreparedStatement statement = connection.getPreparedStatement(query, Statement.RETURN_GENERATED_KEYS);
+			statement.clearParameters();
+			statement.setLong(1, parentId);
+			statement.setString(2, name);
+			try (ResultSet rs = connection.executeQuery(statement)) {
+				return skCase.resultSetToAbstractFiles(rs, connection);
+			}
+		} catch (SQLException ex) {
+			throw new TskCoreException("SQLException thrown when calling query: " + query + " for parentID = " + parentId + " and name " + name, ex);
+		} finally {
+			skCase.releaseSingleUserCaseReadLock();
+		}
+    }
+	
+	/**
+     * Find all files with the exact given name and exact parent path.
+     * 
+	 * @param dataSource The data source to search within.
+     * @param name Exact file name to match.
+	 * @param path Exact parent path.
+     * 
+     * @return A list of matching files.
+     * 
+     * @throws TskCoreException 
+     */
+	public List<AbstractFile> findFilesExactNameExactPath(Content dataSource, String name, String path) throws TskCoreException {
+		
+		// Database paths will always start and end with a forward slash, so add those if not present
+		String normalizedPath = path;
+		if (!normalizedPath.startsWith("/")) {
+			normalizedPath = "/" + normalizedPath;
+		}
+		if (!normalizedPath.endsWith("/")) {
+			normalizedPath = normalizedPath + "/";
+		}
+		
+		String query = "SELECT tsk_files.* FROM tsk_files JOIN tsk_objects ON tsk_objects.obj_id = tsk_files.obj_id WHERE parent_path = ? AND name = ? AND data_source_obj_id = ?";
+		skCase.acquireSingleUserCaseReadLock();
+		try (SleuthkitCase.CaseDbConnection connection = skCase.getConnection()) {
+			PreparedStatement statement = connection.getPreparedStatement(query, Statement.RETURN_GENERATED_KEYS);
+			statement.clearParameters();
+			statement.setString(1, normalizedPath);
+			statement.setString(2, name);
+			statement.setLong(3, dataSource.getId());
+			try (ResultSet rs = connection.executeQuery(statement)) {
+				return skCase.resultSetToAbstractFiles(rs, connection);
+			}
+		} catch (SQLException ex) {
+			throw new TskCoreException("SQLException thrown when calling query: " + query + " for parent path = " + path + " and name " + name, ex);
+		} finally {
+			skCase.releaseSingleUserCaseReadLock();
+		}
+	}
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
index 350ad81cd1a7896ca7abd2b310861498ca9a0ff2..9d522eb7b2da0906411a3c68e4b04ba3e51827fa 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -222,6 +222,7 @@ public class SleuthkitCase {
 	private TimelineManager timelineMgr;
 	private Blackboard blackboard;
 	private CaseDbAccessManager dbAccessManager;
+	private FileManager fileManager;
 	private TaggingManager taggingMgr;
 	private ScoringManager scoringManager;
 	private OsAccountRealmManager osAccountRealmManager;
@@ -403,6 +404,7 @@ private void init() throws Exception {
 		}
 
 		blackboard = new Blackboard(this);
+		fileManager = new FileManager(this);
 		communicationsMgr = new CommunicationsManager(this);
 		timelineMgr = new TimelineManager(this);
 		dbAccessManager = new CaseDbAccessManager(this);
@@ -493,6 +495,15 @@ public CommunicationsManager getCommunicationsManager() throws TskCoreException
 	public Blackboard getBlackboard() {
 		return blackboard;
 	}
+	
+	/**
+	 * Gets the file manager for this case.
+	 * 
+	 * @return The per case FileManager object.
+	 */
+	public FileManager getFileManager() {
+		return fileManager;
+	}
 
 	/**
 	 * Gets the communications manager for this case.
@@ -9606,7 +9617,7 @@ void deleteDataSource(long dataSourceObjectId) throws TskCoreException {
 	 * @throws SQLException Thrown if there is a problem iterating through the
 	 *                      record set.
 	 */
-	private List<AbstractFile> resultSetToAbstractFiles(ResultSet rs, CaseDbConnection connection) throws SQLException {
+	List<AbstractFile> resultSetToAbstractFiles(ResultSet rs, CaseDbConnection connection) throws SQLException {
 		ArrayList<AbstractFile> results = new ArrayList<AbstractFile>();
 		try {
 			while (rs.next()) {