diff --git a/bindings/java/src/org/sleuthkit/datamodel/CarvedFileContainer.java b/bindings/java/src/org/sleuthkit/datamodel/CarvedFileContainer.java
new file mode 100755
index 0000000000000000000000000000000000000000..915ecd60ae60ef0b65217ec55263fe6d16c4387e
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/CarvedFileContainer.java
@@ -0,0 +1,34 @@
+package org.sleuthkit.datamodel;
+
+import java.util.List;
+
+public final class CarvedFileContainer {
+
+	private String mCarvedFileName;
+	private long mCarvedFileSize;
+	private long mContainerId;
+	private List<TskFileRange> mRangeData;
+
+	public CarvedFileContainer(String carvedFileName, long carvedFileSize, long containerId, List<TskFileRange> rangeData) {
+		mCarvedFileName = carvedFileName;
+		mCarvedFileSize = carvedFileSize;
+		mContainerId = containerId;
+		mRangeData = rangeData;
+	}
+
+	public String getName() {
+		return mCarvedFileName;
+	}
+
+	public long getSize() {
+		return mCarvedFileSize;
+	}
+
+	public long getId() {
+		return mContainerId;
+	}
+
+	public List<TskFileRange> getRanges() {
+		return mRangeData;
+	}
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
index 4e594b42df033e4a6c4116d042b7fc14b80fede8..c1bf2ecd57aada4584699a95345e3e3d95a1027b 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -63,7 +63,7 @@ public class SleuthkitCase {
 	private static final ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle");
 	private final ConnectionPerThreadDispenser connections = new ConnectionPerThreadDispenser();
 	private final ResultSetHelper rsHelper = new ResultSetHelper(this);
-	private final Map<Long, Long> systemIdMap = new HashMap<Long, Long>(); // For use by getCarvedDirectoryId().
+	private final Map<Long, Long> carvedFileContainersCache = new HashMap<Long, Long>(); // Caches the IDs of the root $CarvedFiles for each volume.
 	private final Map<Long, FileSystem> fileSystemIdMap = new HashMap<Long, FileSystem>(); // Cache for file system results.
 	private final ArrayList<ErrorObserver> errorObservers = new ArrayList<ErrorObserver>();
 	private final String dbPath;
@@ -2690,75 +2690,10 @@ public List<VirtualDirectory> getVirtualDirectoryRoots() throws TskCoreException
 		}
 	}
 
-	/**
-	 * @param id an image, volume or file system ID
-	 * @return the ID of the '$CarvedFiles' directory for the given systemId
-	 */
-	private long getCarvedDirectoryId(long id) throws TskCoreException {
-		long ret = 0;
-
-		//use lock to ensure atomic cache check and caseDbConnection/cache update
-		acquireExclusiveLock();
-
-		try {
-			// first, check the cache
-			Long carvedDirId = systemIdMap.get(id);
-			if (carvedDirId != null) {
-				return carvedDirId;
-			}
-
-			// it's not in the cache. Go to the DB
-			// determine if we've got a volume system or file system ID
-			Content parent = getContentById(id);
-			if (parent == null) {
-				throw new TskCoreException("No Content object found with this ID (" + id + ").");
-			}
-
-			List<Content> children = Collections.<Content>emptyList();
-			if (parent instanceof FileSystem) {
-				FileSystem fs = (FileSystem) parent;
-				children = fs.getRootDirectory().getChildren();
-			} else if (parent instanceof Volume
-					|| parent instanceof Image) {
-				children = parent.getChildren();
-			} else {
-				throw new TskCoreException("The given ID (" + id + ") was not an image, volume or file system.");
-			}
-
-			// see if any of the children are a '$CarvedFiles' directory
-			Content carvedFilesDir = null;
-			for (Content child : children) {
-				if (child.getName().equals(VirtualDirectory.NAME_CARVED)) {
-					carvedFilesDir = child;
-					break;
-				}
-			}
-
-			// if we found it, add it to the cache and return its ID
-			if (carvedFilesDir != null) {
-
-				// add it to the cache
-				systemIdMap.put(id, carvedFilesDir.getId());
-
-				return carvedFilesDir.getId();
-			}
-
-			// a carved files directory does not exist; create one
-			VirtualDirectory vd = addVirtualDirectory(id, VirtualDirectory.NAME_CARVED);
-
-			ret = vd.getId();
-			// add it to the cache
-			systemIdMap.put(id, ret);
-		} finally {
-			releaseExclusiveLock();
-		}
-
-		return ret;
-	}
-
 	/**
 	 * Adds a carved file to the VirtualDirectory '$CarvedFiles' in the volume
-	 * or file system given by systemId.
+	 * or image given by systemId. Creates $CarvedFiles virtual directory if it
+	 * does not exist already.
 	 *
 	 * @param carvedFileName the name of the carved file to add
 	 * @param carvedFileSize the size of the carved file to add
@@ -2769,129 +2704,205 @@ private long getCarvedDirectoryId(long id) throws TskCoreException {
 	 * @throws org.sleuthkit.datamodel.TskCoreException
 	 */
 	public LayoutFile addCarvedFile(String carvedFileName, long carvedFileSize, long containerId, List<TskFileRange> data) throws TskCoreException {
-		CaseDbConnection connection = connections.getConnection();
-		acquireExclusiveLock();
-		Statement s = null;
-		ResultSet rs = null;
-		try {
-			connection.beginTransaction();
 
-			// get the ID of the appropriate '$CarvedFiles' directory
-			long carvedDirId = getCarvedDirectoryId(containerId);
+		List<CarvedFileContainer> carvedFileContainer = new ArrayList<CarvedFileContainer>();
+		carvedFileContainer.add(new CarvedFileContainer(carvedFileName, carvedFileSize, containerId, data));
 
-			// get the parent path for the $CarvedFiles directory		
-			String parentPath = getFileParentPath(carvedDirId);
-			if (parentPath == null) {
-				parentPath = ""; //NON-NLS
-			}
-			String parentName = getFileName(carvedDirId);
-			if (parentName != null) {
-				parentPath = parentPath + "/" + parentName; //NON-NLS
-			}
+		List<LayoutFile> layoutCarvedFiles = addCarvedFiles(carvedFileContainer);
+		if (layoutCarvedFiles != null) {
+			return layoutCarvedFiles.get(0);
+		} else {
+			return null;
+		}
+	}
 
-			// we should cache this when we start adding lots of carved files...
-			boolean isContainerAFs = false;
-			s = connection.createStatement();
-			rs = connection.executeQuery(s, "select * from tsk_fs_info " //NON-NLS
-					+ "where obj_id = " + containerId); //NON-NLS
-			if (rs.next()) {
-				isContainerAFs = true;
-			}
-			rs.close();
-			rs = null;
+	/**
+	 * Adds a collection of carved files to the VirtualDirectory '$CarvedFiles'
+	 * in the volume or image given by systemId. Creates $CarvedFiles virtual
+	 * directory if it does not exist already.
+	 *
+	 * @param filesToAdd a list of CarvedFileContainer files to add as carved
+	 * files
+	 * @return List<LayoutFile> This is a list of the files added to the
+	 * database
+	 * @throws org.sleuthkit.datamodel.TskCoreException
+	 */
+	public List<LayoutFile> addCarvedFiles(List<CarvedFileContainer> filesToAdd) throws TskCoreException {
+		if (filesToAdd != null) {
+			CaseDbTransaction localTrans = beginTransaction();
+			CaseDbConnection connection = localTrans.getConnection();
+			acquireExclusiveLock();
+			Statement s = null;
+			ResultSet rs = null;
+			List<LayoutFile> addedFiles = new ArrayList<LayoutFile>();
 
-			// Insert a row for the carved file into the tsk_objects table.
-			// INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
-			PreparedStatement statement = connection.getPreparedStatement(CaseDbConnection.PREPARED_STATEMENT.INSERT_OBJECT);
-			statement.clearParameters();
-			statement.setLong(1, carvedDirId);
-			statement.setLong(2, TskData.ObjectType.ABSTRACTFILE.getObjectType());
-			connection.executeUpdate(statement);
-			rs = statement.getGeneratedKeys();
-			long newObjId = rs.getLong(1);
+			try {
+				// get the ID of the appropriate '$CarvedFiles' directory
+				long firstItemId = filesToAdd.get(0).getId();
+				long id = 0;
+				// first, check the cache
+				Long carvedDirId = carvedFileContainersCache.get(firstItemId);
+				if (carvedDirId != null) {
+					id = carvedDirId;
+				} else {
+					// it's not in the cache. Go to the DB
+					// determine if we've got a volume system or file system ID
+					Content parent = getContentById(firstItemId);
+					if (parent == null) {
+						throw new TskCoreException("No Content object found with this ID (" + firstItemId + ").");
+					}
 
-			// Insert a row for the carved file into the tsk_files table.
-			// INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, 
-			// dir_flags, meta_flags, size, ctime, crtime, atime, mtime, parent_path) 
-			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)			
-			statement = connection.getPreparedStatement(CaseDbConnection.PREPARED_STATEMENT.INSERT_FILE);
-			statement.clearParameters();
-			statement.setLong(1, newObjId);
+					List<Content> children = Collections.<Content>emptyList();
+					if (parent instanceof FileSystem) {
+						FileSystem fs = (FileSystem) parent;
+						children = fs.getRootDirectory().getChildren();
+					} else if (parent instanceof Volume
+							|| parent instanceof Image) {
+						children = parent.getChildren();
+					} else {
+						throw new TskCoreException("The given ID (" + firstItemId + ") was not an image, volume or file system.");
+					}
 
-			// only insert into the fs_obj_id column if container is a FS
-			if (isContainerAFs) {
-				statement.setLong(2, containerId);
-			}
-			statement.setString(3, carvedFileName);
+					// see if any of the children are a '$CarvedFiles' directory
+					Content carvedFilesDir = null;
+					for (Content child : children) {
+						if (child.getName().equals(VirtualDirectory.NAME_CARVED)) {
+							carvedFilesDir = child;
+							break;
+						}
+					}
 
-			// type
-			final TSK_DB_FILES_TYPE_ENUM type = TSK_DB_FILES_TYPE_ENUM.CARVED;
-			statement.setShort(4, type.getFileType());
+					// if we found it, add it to the cache and grab its ID
+					if (carvedFilesDir != null) {
+						// add it to the cache
+						carvedFileContainersCache.put(firstItemId, carvedFilesDir.getId());
+						id = carvedFilesDir.getId();
+					} else {
+						// a carved files directory does not exist; create one
+						VirtualDirectory vd = addVirtualDirectory(firstItemId, VirtualDirectory.NAME_CARVED, localTrans);
+						id = vd.getId();
+						// add it to the cache
+						carvedFileContainersCache.put(firstItemId, id);
+					}
+				}
 
-			// has_path
-			statement.setBoolean(5, true);
+				// get the parent path for the $CarvedFiles directory		
+				String parentPath = getFileParentPath(id);
+				if (parentPath == null) {
+					parentPath = ""; //NON-NLS
+				}
+				String parentName = getFileName(id);
+				if (parentName != null) {
+					parentPath = parentPath + "/" + parentName; //NON-NLS
+				}
 
-			// dirType
-			final TSK_FS_NAME_TYPE_ENUM dirType = TSK_FS_NAME_TYPE_ENUM.REG;
-			statement.setShort(6, dirType.getValue());
+				// we should cache this when we start adding lots of carved files...
+				boolean isContainerAFs = false;
+				s = connection.createStatement();
+				rs = connection.executeQuery(s, "select * from tsk_fs_info " //NON-NLS
+						+ "where obj_id = " + firstItemId); //NON-NLS
+				if (rs.next()) {
+					isContainerAFs = true;
+				}
+				rs.close();
+				rs = null;
 
-			// metaType
-			final TSK_FS_META_TYPE_ENUM metaType = TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG;
-			statement.setShort(7, metaType.getValue());
+				for (CarvedFileContainer itemToAdd : filesToAdd) {
+
+					// Insert a row for the carved file into the tsk_objects table.
+					// INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
+					PreparedStatement statement = connection.getPreparedStatement(CaseDbConnection.PREPARED_STATEMENT.INSERT_OBJECT);
+					statement.clearParameters();
+					statement.setLong(1, id);
+					statement.setLong(2, TskData.ObjectType.ABSTRACTFILE.getObjectType());
+					connection.executeUpdate(statement);
+					rs = statement.getGeneratedKeys();
+					long newObjId = rs.getLong(1);
+
+					// Insert a row for the carved file into the tsk_files table.
+					// INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, 
+					// dir_flags, meta_flags, size, ctime, crtime, atime, mtime, parent_path) 
+					// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)			
+					statement = connection.getPreparedStatement(CaseDbConnection.PREPARED_STATEMENT.INSERT_FILE);
+					statement.clearParameters();
+					statement.setLong(1, newObjId);
+
+					// only insert into the fs_obj_id column if container is a FS
+					if (isContainerAFs) {
+						statement.setLong(2, itemToAdd.getId());
+					}
+					statement.setString(3, itemToAdd.getName());
 
-			// dirFlag
-			final TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.UNALLOC;
-			statement.setShort(8, dirFlag.getValue());
+					// type
+					final TSK_DB_FILES_TYPE_ENUM type = TSK_DB_FILES_TYPE_ENUM.CARVED;
+					statement.setShort(4, type.getFileType());
 
-			// metaFlags
-			final short metaFlags = TSK_FS_META_FLAG_ENUM.UNALLOC.getValue();
-			statement.setShort(9, metaFlags);
+					// has_path
+					statement.setBoolean(5, true);
 
-			// size
-			statement.setLong(10, carvedFileSize);
+					// dirType
+					final TSK_FS_NAME_TYPE_ENUM dirType = TSK_FS_NAME_TYPE_ENUM.REG;
+					statement.setShort(6, dirType.getValue());
 
-			//parent path, nulls for params 11-14
-			statement.setString(15, parentPath);
+					// metaType
+					final TSK_FS_META_TYPE_ENUM metaType = TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG;
+					statement.setShort(7, metaType.getValue());
 
-			connection.executeUpdate(statement);
+					// dirFlag
+					final TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.UNALLOC;
+					statement.setShort(8, dirFlag.getValue());
 
-			// Add a row in the tsk_layout_file table for each TskFileRange.
-			// INSERT INTO tsk_file_layout (obj_id, byte_start, byte_len, sequence) 
-			// VALUES (?, ?, ?, ?)
-			statement = connection.getPreparedStatement(CaseDbConnection.PREPARED_STATEMENT.INSERT_LAYOUT_FILE);
-			for (TskFileRange tskFileRange : data) {
-				statement.clearParameters();
+					// metaFlags
+					final short metaFlags = TSK_FS_META_FLAG_ENUM.UNALLOC.getValue();
+					statement.setShort(9, metaFlags);
 
-				// set the object ID
-				statement.setLong(1, newObjId);
+					// size
+					statement.setLong(10, itemToAdd.getSize());
 
-				// set byte_start
-				statement.setLong(2, tskFileRange.getByteStart());
+					//parent path, nulls for params 11-14
+					statement.setString(15, parentPath);
 
-				// set byte_len
-				statement.setLong(3, tskFileRange.getByteLen());
+					connection.executeUpdate(statement);
 
-				// set the sequence number
-				statement.setLong(4, tskFileRange.getSequence());
+					// Add a row in the tsk_layout_file table for each TskFileRange.
+					// INSERT INTO tsk_file_layout (obj_id, byte_start, byte_len, sequence) 
+					// VALUES (?, ?, ?, ?)
+					statement = connection.getPreparedStatement(CaseDbConnection.PREPARED_STATEMENT.INSERT_LAYOUT_FILE);
+					for (TskFileRange tskFileRange : itemToAdd.getRanges()) {
+						statement.clearParameters();
 
-				// execute it
-				connection.executeUpdate(statement);
-			}
+						// set the object ID
+						statement.setLong(1, newObjId);
 
-			connection.commitTransaction();
+						// set byte_start
+						statement.setLong(2, tskFileRange.getByteStart());
 
-			return new LayoutFile(this, newObjId, carvedFileName, type, dirType,
-					metaType, dirFlag, metaFlags, carvedFileSize, null,
-					FileKnown.UNKNOWN, parentPath);
+						// set byte_len
+						statement.setLong(3, tskFileRange.getByteLen());
 
-		} catch (SQLException ex) {
-			connection.rollbackTransaction();
-			throw new TskCoreException("Failed to add carved file to case database", ex);
-		} finally {
-			closeResultSet(rs);
-			closeStatement(s);
-			releaseExclusiveLock();
-		}
+						// set the sequence number
+						statement.setLong(4, tskFileRange.getSequence());
+
+						// execute it
+						connection.executeUpdate(statement);
+					}
+
+					addedFiles.add(new LayoutFile(this, newObjId, itemToAdd.getName(),
+							type, dirType, metaType, dirFlag, metaFlags,
+							itemToAdd.getSize(), null, FileKnown.UNKNOWN, parentPath));
+				}
+				localTrans.commit();
+				return addedFiles;
+			} catch (SQLException ex) {
+				localTrans.rollback();
+				throw new TskCoreException("Failed to add carved file to case database", ex);
+			} finally {
+				closeResultSet(rs);
+				closeStatement(s);
+				releaseExclusiveLock();
+			}
+		} // if fileToAdd != null
+		return null;
 	}
 
 	/**