From e395203ad1bfd9adfa65ba8352519e4a0f501401 Mon Sep 17 00:00:00 2001
From: Richard Cordovano <rcordovano@basistech.com>
Date: Wed, 1 Jun 2016 16:51:49 -0400
Subject: [PATCH] Additional work on carved files API

---
 .../sleuthkit/datamodel/CarvingResult.java    |  68 ++---
 .../sleuthkit/datamodel/SleuthkitCase.java    | 277 ++++++++----------
 2 files changed, 137 insertions(+), 208 deletions(-)

diff --git a/bindings/java/src/org/sleuthkit/datamodel/CarvingResult.java b/bindings/java/src/org/sleuthkit/datamodel/CarvingResult.java
index 24708b652..b1fc7e5dd 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/CarvingResult.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/CarvingResult.java
@@ -21,65 +21,39 @@
 import java.util.List;
 
 /**
- * A carving result consisting of a set of carved files and the image, volume,
- * or file system from which they were carved.
+ * A carving result consisting of a set of carved files and the parent content
+ * (e.g., an unallocated space file or an image data source) from which the
+ * files were carved.
  */
 public final class CarvingResult {
 
-	private final Content carvedFilesParent;
+	private final Content parent;
 	private final List<CarvedFile> carvedFiles;
 
 	/**
 	 * Constructs a carving result consisting of a set of carved files and the
-	 * image from which they were carved.
+	 * parent content (e.g., an unallocated space file or an image data source)
+	 * from which the files were carved.
 	 *
-	 *
-	 * @param carvedFilesParent An image that is the parent of a set of carved
-	 *                          files.
-	 * @param carvedFiles       The carved files.
-	 */
-	public CarvingResult(Image carvedFilesParent, List<CarvedFile> carvedFiles) {
-		this.carvedFilesParent = carvedFilesParent;
-		this.carvedFiles = carvedFiles;
-	}
-
-	/**
-	 * Constructs a carving result consisting of a set of carved files and the
-	 * volume from which they were carved.
-	 *
-	 * @param carvedFilesParent A volume that is the parent of a set of carved
-	 *                          files.
-	 * @param carvedFiles       The carved files.
-	 */
-	public CarvingResult(FileSystem carvedFilesParent, List<CarvedFile> carvedFiles) {
-		this.carvedFilesParent = carvedFilesParent;
-		this.carvedFiles = carvedFiles;
-	}
-
-	/**
-	 * Constructs a carving result consisting of a set of carved files and file
-	 * system from which they were carved.
-	 *
-	 * @param carvedFilesParent A file system that is the parent of a set of
-	 *                          carved files.
-	 * @param carvedFiles       The carved files.
+	 * @param parent      The parent of a set of carved files.
+	 * @param carvedFiles The carved files.
 	 */
-	public CarvingResult(Volume carvedFilesParent, List<CarvedFile> carvedFiles) {
-		this.carvedFilesParent = carvedFilesParent;
+	public CarvingResult(Content parent, List<CarvedFile> carvedFiles) {
+		this.parent = parent;
 		this.carvedFiles = carvedFiles;
 	}
 
 	/**
-	 * Gets the parent of a set the carved files in a carving result.
+	 * Gets the parent of the carved files in a carving result.
 	 *
 	 * @return The carved files parent.
 	 */
-	final Content getCarvedFilesParent() {
-		return carvedFilesParent;
+	final Content getParent() {
+		return parent;
 	}
 
 	/**
-	 * Gets the set of carved files in a carving result.
+	 * Gets the carved files in a carving result.
 	 *
 	 * @return The carved files.
 	 */
@@ -88,7 +62,8 @@ final List<CarvedFile> getCarvedFiles() {
 	}
 
 	/**
-	 * A file carved out of a parent image, volume, or file system.
+	 * A file carved out of parent content (e.g., an unallocated space file or
+	 * an image data source).
 	 */
 	public static class CarvedFile {
 
@@ -97,8 +72,8 @@ public static class CarvedFile {
 		private final List<TskFileRange> layoutInParent;
 
 		/**
-		 * Constructs a file carved out of a parent image, volume, or file
-		 * system.
+		 * Constructs a file carved out of parent content (e.g., an unallocated
+		 * space file or an image data source).
 		 *
 		 * @param name           The name of the file.
 		 * @param sizeInBytes    The size of the file in bytes.
@@ -111,7 +86,7 @@ public CarvedFile(String name, long sizeInBytes, List<TskFileRange> layoutInPare
 		}
 
 		/**
-		 * Gets the name of a carved file.
+		 * Gets the name of the carved file.
 		 *
 		 * @return The file name.
 		 */
@@ -120,7 +95,7 @@ final String getName() {
 		}
 
 		/**
-		 * Gets the size of a carved file.
+		 * Gets the size of the carved file.
 		 *
 		 * @return The size of the file in bytes.
 		 */
@@ -129,8 +104,7 @@ final long getSizeInBytes() {
 		}
 
 		/**
-		 * Gets the layout of the carved file within its parent image, voluem,
-		 * or file system.
+		 * Gets the layout of the carved file within its parent.
 		 *
 		 * @return A list of TskRange objects representing the layoput of the
 		 *         carved file within its parent.
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
index 677128077..bed958fca 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -93,6 +93,7 @@ public class SleuthkitCase {
 	private final ConnectionPool connections;
 	private final ResultSetHelper rsHelper = new ResultSetHelper(this);
 	private final Map<Long, Long> carvedFileContainersCache = new HashMap<Long, Long>(); // Caches the IDs of the root $CarvedFiles for each volume.
+	private final Map<Long, VirtualDirectory> rootIdsToCarvedFileDirs = new HashMap<Long, VirtualDirectory>();
 	private final Map<Long, FileSystem> fileSystemIdMap = new HashMap<Long, FileSystem>(); // Cache for file system files.
 	private final ArrayList<ErrorObserver> sleuthkitCaseErrorObservers = new ArrayList<ErrorObserver>();
 	private final String dbPath;
@@ -3447,190 +3448,144 @@ public List<VirtualDirectory> getVirtualDirectoryRoots() throws TskCoreException
 	 *                          operation.
 	 */
 	public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws TskCoreException {
-		List<LayoutFile> addedFiles = new ArrayList<LayoutFile>();
-		CaseDbTransaction localTrans = null;
-		Statement s = null;
-		ResultSet rs = null;
+		CaseDbTransaction transaction = null;
+		Statement statement = null;
+		ResultSet resultSet = null;
 		acquireExclusiveLock();
 		try {
-			localTrans = beginTransaction();
-			CaseDbConnection connection = localTrans.getConnection();
+			transaction = beginTransaction();
+			CaseDbConnection connection = transaction.getConnection();
 
-			Content parent = carvingResult.getCarvedFilesParent();
+			/*
+			 * Find the root file system, volume, or image ancestor of the
+			 * carved files parent. This is necessary because the carved files
+			 * will be "re-parented" as children of the $CarvedFiles virtual
+			 * directory of this ancestor.
+			 */
+			Content root = carvingResult.getParent();
+			while ((root instanceof FileSystem == false)
+					&& (root instanceof Volume == false)
+					&& (root instanceof Image == false)) {
+				root = root.getParent();
+			}
 
-			// first, check the cache
-			long carvedFilesDirId;
-			Long cachedCarvedFilesDirId = carvedFileContainersCache.get(parent.getId());
-			if (null != cachedCarvedFilesDirId) {
-				carvedFilesDirId = cachedCarvedFilesDirId;
-			} else {
-				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 (" + parent.getId() + ") was not an image, volume or file system.");
+			/*
+			 * Get or create the cached $CarvedFiles virtual directory for the
+			 * root ancestor.
+			 */
+			VirtualDirectory carvedFilesDir = rootIdsToCarvedFileDirs.get(root.getId());
+			if (null == carvedFilesDir) {
+				List<Content> children = Collections.emptyList();
+				if (root instanceof FileSystem) {
+					FileSystem fileSystem = (FileSystem) root;
+					children = fileSystem.getRootDirectory().getChildren();
+				} else if (root instanceof Volume || root instanceof Image) {
+					children = root.getChildren();
 				}
-
-				// 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;
+					if (child instanceof VirtualDirectory && child.getName().equals(VirtualDirectory.NAME_CARVED)) {
+						carvedFilesDir = (VirtualDirectory) child;
 						break;
 					}
 				}
-
-				// if we found it, add it to the cache and grab its ID
-				if (carvedFilesDir != null) {
-					// add it to the cache
-					carvedFileContainersCache.put(parent.getId(), carvedFilesDir.getId());
-					carvedFilesDirId = carvedFilesDir.getId();
-				} else {
-					// a carved files directory does not exist; create one
-					VirtualDirectory vd = addVirtualDirectory(parent.getId(), VirtualDirectory.NAME_CARVED, localTrans);
-					carvedFilesDirId = vd.getId();
-					// add it to the cache
-					carvedFileContainersCache.put(parent.getId(), carvedFilesDirId);
+				if (null == carvedFilesDir) {
+					carvedFilesDir = addVirtualDirectory(root.getId(), VirtualDirectory.NAME_CARVED, transaction);
 				}
+				rootIdsToCarvedFileDirs.put(root.getId(), carvedFilesDir);
 			}
 
-			// get the parent path for the $CarvedFiles directory		
-			String parentPath = getFileParentPath(carvedFilesDirId, localTrans);
-			if (parentPath == null) {
-				parentPath = "/"; //NON-NLS
-			}
-			String parentName = getFileName(carvedFilesDirId, localTrans);
-			if (parentName != null) {
-				parentPath = parentPath + parentName + "/"; //NON-NLS
-			}
-
-			// TODO (AUT-1903): 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 = " + parent.getId()); //NON-NLS
-			if (rs.next()) {
-				isContainerAFs = true;
-			}
-			rs.close();
-			rs = null;
-
-			// TODO (AUT-1903): This result is another thing that should be 
-			// cached.
-			long dataSourceObjectId = getDataSourceObjectId(connection, carvedFilesDirId);
-
-			for (CarvingResult.CarvedFile itemToAdd : carvingResult.getCarvedFiles()) {
-
-				// 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(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS);
-				statement.clearParameters();
-				statement.setLong(1, carvedFilesDirId);
-				statement.setLong(2, TskData.ObjectType.ABSTRACTFILE.getObjectType());
-				connection.executeUpdate(statement);
-				rs = statement.getGeneratedKeys();
-				rs.next();
-				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, data_source_obj_id) 
-				// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)			
-				statement = connection.getPreparedStatement(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, parent.getId());
+			/*
+			 * Add the carved files to the database as children of the
+			 * $CarvedFile directory of the root ancestor.
+			 */
+			List<LayoutFile> carvedFiles = new ArrayList<LayoutFile>();
+			for (CarvingResult.CarvedFile carvedFile : carvingResult.getCarvedFiles()) {
+				/*
+				 * Insert a row for the carved file into the tsk_objects table:
+				 * INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
+				 */
+				PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS);
+				prepStmt.clearParameters();
+				prepStmt.setLong(1, carvedFilesDir.getId());
+				prepStmt.setLong(2, TskData.ObjectType.ABSTRACTFILE.getObjectType());
+				connection.executeUpdate(prepStmt);
+				resultSet = prepStmt.getGeneratedKeys();
+				resultSet.next();
+				long carvedFileId = resultSet.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, data_source_obj_id)
+				 * VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+				 */
+				prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
+				prepStmt.clearParameters();
+				prepStmt.setLong(1, carvedFileId);
+				if (root instanceof FileSystem) {
+					prepStmt.setLong(2, root.getId());
 				} else {
-					statement.setNull(2, java.sql.Types.BIGINT);
+					prepStmt.setNull(2, java.sql.Types.BIGINT);
 				}
-				statement.setString(3, itemToAdd.getName());
-
-				// type
-				final TSK_DB_FILES_TYPE_ENUM type = TSK_DB_FILES_TYPE_ENUM.CARVED;
-				statement.setShort(4, type.getFileType());
-
-				// has_path
-				statement.setShort(5, (short) 1);
-
-				// dirType
-				final TSK_FS_NAME_TYPE_ENUM dirType = TSK_FS_NAME_TYPE_ENUM.REG;
-				statement.setShort(6, dirType.getValue());
-
-				// metaType
-				final TSK_FS_META_TYPE_ENUM metaType = TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG;
-				statement.setShort(7, metaType.getValue());
-
-				// dirFlag
-				final TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.UNALLOC;
-				statement.setShort(8, dirFlag.getValue());
-
-				// metaFlags
-				final short metaFlags = TSK_FS_META_FLAG_ENUM.UNALLOC.getValue();
-				statement.setShort(9, metaFlags);
-
-				// size
-				statement.setLong(10, itemToAdd.getSizeInBytes());
-
-				// nulls for params 11-14
-				statement.setNull(11, java.sql.Types.BIGINT);
-				statement.setNull(12, java.sql.Types.BIGINT);
-				statement.setNull(13, java.sql.Types.BIGINT);
-				statement.setNull(14, java.sql.Types.BIGINT);
-
-				// parent path
-				statement.setString(15, parentPath);
-
-				// data source object id
-				statement.setLong(16, dataSourceObjectId);
-
-				connection.executeUpdate(statement);
+				prepStmt.setString(3, carvedFile.getName());
+				prepStmt.setShort(4, TSK_DB_FILES_TYPE_ENUM.CARVED.getFileType());
+				prepStmt.setShort(5, (short) 1); // Has a path
+				prepStmt.setShort(6, TSK_FS_NAME_TYPE_ENUM.REG.getValue());
+				prepStmt.setShort(7, TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue());
+				prepStmt.setShort(8, TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue());
+				prepStmt.setShort(9, TSK_FS_META_FLAG_ENUM.UNALLOC.getValue());
+				prepStmt.setLong(10, carvedFile.getSizeInBytes());
+				prepStmt.setNull(11, java.sql.Types.BIGINT);
+				prepStmt.setNull(12, java.sql.Types.BIGINT);
+				prepStmt.setNull(13, java.sql.Types.BIGINT);
+				prepStmt.setNull(14, java.sql.Types.BIGINT);
+				prepStmt.setString(15, carvedFilesDir.getUniquePath());
+				prepStmt.setLong(16, carvedFilesDir.getDataSourceObjectId());
+				connection.executeUpdate(prepStmt);
 
 				// 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(PREPARED_STATEMENT.INSERT_LAYOUT_FILE);
-				for (TskFileRange tskFileRange : itemToAdd.getLayoutInParent()) {
-					statement.clearParameters();
-
-					// set the object ID
-					statement.setLong(1, newObjId);
-
-					// set byte_start
-					statement.setLong(2, tskFileRange.getByteStart());
-
-					// set byte_len
-					statement.setLong(3, tskFileRange.getByteLen());
-
-					// set the sequence number
-					statement.setLong(4, tskFileRange.getSequence());
-
-					// execute it
-					connection.executeUpdate(statement);
+				prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LAYOUT_FILE);
+				for (TskFileRange tskFileRange : carvedFile.getLayoutInParent()) {
+					prepStmt.clearParameters();
+					prepStmt.setLong(1, carvedFileId);
+					prepStmt.setLong(2, tskFileRange.getByteStart());
+					prepStmt.setLong(3, tskFileRange.getByteLen());
+					prepStmt.setLong(4, tskFileRange.getSequence());
+					connection.executeUpdate(prepStmt);
 				}
 
-				addedFiles.add(new LayoutFile(this, newObjId, dataSourceObjectId, itemToAdd.getName(),
-						type, dirType, metaType, dirFlag, metaFlags,
-						itemToAdd.getSizeInBytes(), null, FileKnown.UNKNOWN, parentPath, null));
-			}
-			localTrans.commit();
-			return addedFiles;
+				/*
+				 * Construct a layout file representation of the carved file.
+				 */
+				carvedFiles.add(new LayoutFile(this,
+						carvedFileId,
+						carvedFilesDir.getDataSourceObjectId(),
+						carvedFile.getName(),
+						TSK_DB_FILES_TYPE_ENUM.CARVED,
+						TSK_FS_NAME_TYPE_ENUM.REG,
+						TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG,
+						TSK_FS_NAME_FLAG_ENUM.UNALLOC,
+						TSK_FS_META_FLAG_ENUM.UNALLOC.getValue(),
+						carvedFile.getSizeInBytes(),
+						null,
+						FileKnown.UNKNOWN,
+						carvedFilesDir.getUniquePath(),
+						null));
+			}
+			transaction.commit();
+			return carvedFiles;
 
 		} catch (SQLException ex) {
-			if (null != localTrans) {
-				localTrans.rollback();
+			if (null != transaction) {
+				transaction.rollback();
 			}
 			throw new TskCoreException("Failed to add carved file to case database", ex);
 		} finally {
-			closeResultSet(rs);
-			closeStatement(s);
+			closeResultSet(resultSet);
+			closeStatement(statement);
 			releaseExclusiveLock();
 		}
 	}
@@ -7130,7 +7085,7 @@ public List<LayoutFile> addCarvedFiles(List<CarvedFileContainer> filesToAdd) thr
 			CarvingResult.CarvedFile carvedFile = new CarvingResult.CarvedFile(container.getName(), container.getSize(), container.getRanges());
 			carvedFiles.add(carvedFile);
 		}
-			CarvingResult carvingResult;
+		CarvingResult carvingResult;
 		Content parent = getContentById(filesToAdd.get(0).getId());
 		if (parent instanceof Image) {
 			carvingResult = new CarvingResult((Image) parent, carvedFiles);
@@ -7143,5 +7098,5 @@ public List<LayoutFile> addCarvedFiles(List<CarvedFileContainer> filesToAdd) thr
 		}
 		return addCarvedFiles(carvingResult);
 	}
-		
+
 }
-- 
GitLab