diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.cpp b/bindings/java/jni/dataModel_SleuthkitJNI.cpp
index 093ea3ea1994d3bda08f02bb3d4a1ee1cb00760c..d1b5ce24381e9173943d41741fec9f0566c9ae26 100644
--- a/bindings/java/jni/dataModel_SleuthkitJNI.cpp
+++ b/bindings/java/jni/dataModel_SleuthkitJNI.cpp
@@ -278,6 +278,8 @@ JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDBLookup
         }
     }
 
+	env->ReleaseStringUTFChars(hash, (const char *) md5);
+
     return (int) file_known;
 }
 
@@ -338,7 +340,7 @@ JNIEXPORT void JNICALL
     //change to true when autopsy needs the block table.
     tskAuto->createBlockMap(false);
     //change to false if hashes aren't needed
-    tskAuto->hashFiles(true);
+    tskAuto->hashFiles(false);
 
     
 
diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java
index 26a5f79892b073f2dba56e389d890d34c83d5674..9a7bb338b11dbf122ec91ffbcc6aa5133350f36e 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java
@@ -98,7 +98,7 @@ static public ARTIFACT_TYPE fromID(int ID) {
 			throw new IllegalArgumentException("No ARTIFACT_TYPE matching type: " + ID);
 		}
 
-		String getDisplayName() {
+		public String getDisplayName() {
 			return this.displayName;
 		}
 	}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
index 8be9e73046eab856a36f1931ec5feccf84031707..1a6c130c8f16c5fc87c0a49fde6e851c4862f562 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
@@ -158,7 +158,7 @@ static public ATTRIBUTE_TYPE fromLabel(String label) {
 			throw new IllegalArgumentException("No ATTRIBUTE_TYPE matching type: " + label);
 		}
 
-		String getDisplayName() {
+		public String getDisplayName() {
 			return this.displayName;
 		}
 	}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Hash.java b/bindings/java/src/org/sleuthkit/datamodel/Hash.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb34e2e30c3d90b73f95c8724c4f1b53155cc944
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/Hash.java
@@ -0,0 +1,65 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 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.io.IOException;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.io.InputStream;
+
+class Hash {
+
+    private final static int BUFFER_SIZE = 8192;
+
+    /**
+     * generate the md5 hash for the given content
+	 * 
+	 * @param content	Content object whose md5 hash we want to calculate
+	 * @return			md5 of the given Content object
+     */
+    static String calculateMd5(Content content) {
+        String hashText = "";
+        InputStream in = new ReadContentInputStream(content);
+        Logger logger = Logger.getLogger(Hash.class.getName());
+        try {
+            MessageDigest md = MessageDigest.getInstance("md5");
+            byte[] buffer = new byte[BUFFER_SIZE];
+            int len = in.read(buffer);
+            while (len != -1) {
+                md.update(buffer, 0, len);
+                len = in.read(buffer);
+            }
+            byte[] hash = md.digest();
+            BigInteger bigInt = new BigInteger(1, hash);
+            hashText = bigInt.toString(16);
+            // zero padding
+            while (hashText.length() < 32) {
+                hashText = "0" + hashText;
+            }
+        } catch (NoSuchAlgorithmException ex) {
+            logger.log(Level.SEVERE, "No algorithm known as 'md5'", ex);
+        } catch (IOException ex) {
+            logger.log(Level.SEVERE, "Error reading content", ex);
+        }
+        return hashText;
+    }
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/ReadContentInputStream.java b/bindings/java/src/org/sleuthkit/datamodel/ReadContentInputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1e69e69c9933f1dcfb8b1734c803380b9dee8ac
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/ReadContentInputStream.java
@@ -0,0 +1,88 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 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.io.IOException;
+import java.io.InputStream;
+
+/**
+ * InputStream to read bytes from a Content object's data
+ */
+public class ReadContentInputStream extends InputStream {
+
+    private long position;
+    private long length;
+    private Content content;
+
+    public ReadContentInputStream(Content content) {
+        this.content = content;
+        this.position = 0;
+        this.length = content.getSize();
+    }
+
+    @Override
+    public int read() throws IOException {
+        byte[] buff = new byte[1];
+        return (read(buff) != -1) ? buff[0] : -1;
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        
+        // must return 0 for zero-length arrays
+        if (b.length == 0) {
+            return 0;
+        }
+        
+        // will get an error from TSK if we try to read an empty file
+        if (this.length == 0) {
+            return -1;
+        }
+        
+        if (position < length) {
+            // data remains to be read
+            
+            int lenToRead = (int) Math.min(len, length - position);
+        
+            try {
+                byte[] buff = content.read(position, lenToRead);
+                int lenRead = buff.length;
+
+                if (lenRead == 0) {
+                    // TSK could not read the whole file, ending partway
+                    return -1;
+                } else {
+                    System.arraycopy(buff, 0, b, off, lenRead);
+                    position += lenRead;
+                    return lenRead;
+                }
+            } catch (TskException ex) {
+                throw new IOException(ex);
+            }
+        } else {
+            // at end of file
+            return -1;
+        }
+    }
+}
\ No newline at end of file
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
old mode 100644
new mode 100755
index 1667a7ccf37be6dbd5e07ad1713c21f99f2534e2..ca6651aafc29324fbe141e6f03d7fd0e37640f29
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -31,6 +31,7 @@
 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
 import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess;
+import org.sleuthkit.datamodel.TskData.FileKnown;
 import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM;
 
 /**
@@ -402,6 +403,49 @@ public List<Content> getRootObjects() throws TskException {
 		}
 	}
 
+	/**
+	 * Get all blackboard artifacts of a given type
+	 * @param artifactTypeID artifact type id (must exist in database)
+	 * @return list of blackboard artifacts
+	 */
+	public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID) throws TskException {
+		String artifactTypeName = this.getArtifactTypeString(artifactTypeID);
+		try {
+			ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>();
+			Statement s = con.createStatement();
+			ResultSet rs = s.executeQuery("SELECT artifact_id, obj_id FROM blackboard_artifacts WHERE artifact_type_id = " + artifactTypeID);
+
+			while (rs.next()) {
+				artifacts.add(new BlackboardArtifact(this, rs.getLong(1), rs.getLong(2), 
+								artifactTypeID, artifactTypeName, ARTIFACT_TYPE.fromID(artifactTypeID).getDisplayName()));
+			}
+			s.close();
+			return artifacts;
+		} catch (SQLException ex) {
+			throw new TskException("Error getting or creating a blackboard artifact. " + ex.getMessage(), ex);
+		}
+	}
+
+	/**
+	 * Get all blackboard artifact types
+	 * @return list of blackboard artifact types
+	 */
+	public ArrayList<BlackboardArtifact.ARTIFACT_TYPE> getBlackboardArtifactTypes() throws TskException {
+		try {
+			ArrayList<BlackboardArtifact.ARTIFACT_TYPE> artifact_types = new ArrayList<BlackboardArtifact.ARTIFACT_TYPE>();
+			Statement s = con.createStatement();
+			ResultSet rs = s.executeQuery("SELECT artifact_type_id FROM blackboard_artifact_types");
+
+			while (rs.next()) {
+				artifact_types.add(BlackboardArtifact.ARTIFACT_TYPE.fromID(rs.getInt(1)));
+			}
+			s.close();
+			return artifact_types;
+		} catch (SQLException ex) {
+			throw new TskException("Error getting artifact types. " + ex.getMessage(), ex);
+		}
+	}
+
 	/**
 	 * helper method to get all artifacts matching the type id name and object id
 	 * @param artifactTypeID artifact type id
@@ -458,7 +502,7 @@ public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID,
 	public ArrayList<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType, long obj_id) throws TskException {
 			return getArtifactsHelper(artifactType.getTypeID(), artifactType.getLabel(), obj_id);
 	}
-
+	
 	/**
      * Get the blackboard artifact with the given artifact id
 	 * @param artifactID artifact ID
@@ -1370,4 +1414,121 @@ public void close() {
 					"Error freeing case handle.", ex);
 		}
 	}
-}
+	
+	/**
+	 * Update the given hash and known status of the object in the DB denoted by id
+	 * 
+	 * @param id		The object's unique ID in the database
+	 * @param md5Hash	The object's calculated md5 hash
+	 * @param fileKnown	The object's known status
+	 * @throws SQLException
+	 */
+	private void updateHashAndKnown(long id, String md5Hash, FileKnown fileKnown) throws SQLException{
+		Statement s = con.createStatement();
+		s.executeUpdate("UPDATE tsk_files " +
+						"SET known='" + fileKnown.toLong() + "', md5='" + md5Hash + "' " +
+						"WHERE obj_id=" + id);
+		s.close();
+	}
+	
+//	Useful if we want to queue sql updates for performance reasons
+//	/**
+//	 * Update the given hash and known status of the objects in the DB denoted by id
+//	 * 
+//	 * @param ids		The objects' unique IDs in the database
+//	 * @param md5Hashes	The objects' calculated md5 hashes
+//	 * @param knowns	The objects' known statuses
+//	 * @throws SQLException
+//	 */
+//	private void updateHashesAndKnowns(List<Long> ids, List<String> md5Hashes, List<Long> knowns) throws SQLException{
+//		int idsSize = ids.size();
+//		int md5sSize = md5Hashes.size();
+//		int knownsSize = knowns.size();
+//		if(idsSize == md5sSize && md5sSize == knownsSize && knownsSize == idsSize){
+//			StringBuilder query = new StringBuilder("UPDATE tsk_files SET known = CASE obj_id");
+//			for(int i = 0; i<idsSize; i++){
+//				// " WHEN id THEN known"
+//				query.append(" WHEN ").append(ids.get(i))
+//					 .append(" THEN ").append(knowns.get(i));
+//			}
+//			query.append(" END, md5 = CASE obj_id");
+//			for(int i = 0; i<idsSize; i++){
+//				// " WHEN id THEN hash"
+//				query.append(" WHEN ").append(ids.get(i))
+//				     .append(" THEN '").append(md5Hashes.get(i)).append("'");
+//			}
+//			query.append(" END WHERE id in (");
+//			for(int i = 0; i<idsSize; i++){
+//				// "1,2,3,4,"
+//				query.append(ids.get(i)).append(",");
+//			}
+//			// remove the last unnecessary comma
+//			query.deleteCharAt(query.length()-1);
+//			query.append(")");
+//			Statement s = con.createStatement();
+//			s.executeUpdate(query.toString());
+//			s.close();
+//		}else{
+//			throw new IllegalArgumentException("Lists must be of equal length!");
+//		}
+//	}
+	
+	/**
+	 * Calculate the given Content object's md5 hash, look it up in the
+	 * known databases, and then update the case database with both hash and
+	 * known status
+	 *
+	 * @param cont The content whose md5 you want to look up
+	 * @return	   The content's known status from the databases
+	 * @throws TskException
+	 */
+	public String lookupFileMd5(Content cont) throws TskException{
+		Logger logger = Logger.getLogger(SleuthkitCase.class.getName());
+		try{
+			long contId = cont.getId();
+			String md5Hash = Hash.calculateMd5(cont);
+			FileKnown fileKnown = SleuthkitJNI.lookupHash(md5Hash);
+			updateHashAndKnown(contId, md5Hash, fileKnown);
+			return fileKnown.getName();
+		} catch (TskException ex) {
+			logger.log(Level.SEVERE, "Error looking up known status", ex);
+		} catch(SQLException ex) {
+			logger.log(Level.SEVERE, "Error updating SQL database", ex);
+		}
+		throw new TskException("Error analyzing file");
+	}
+	
+//	Useful if we want to queue sql updates for performance reasons
+//	/**
+//	 * Calculate the given Content objects' md5 hashes, look them up in the
+//	 * known databases, and then update the case database with both hash and
+//	 * known status
+//	 *
+//	 * @param cont The list of contents whose md5s you want to look up
+//	 * @return	   The contents' known statuses from the databases
+//	 * @throws TskException
+//	 */
+//	public List<Long> lookupFilesMd5(List<? extends Content> cont) throws TskException{
+//		List<Long> ids = new ArrayList<Long>();
+//		List<String> md5Hashes = new ArrayList<String>();
+//		List<Long> knowns = new ArrayList<Long>();
+//		
+//		try{
+//			for(Content c : cont){
+//				ids.add(c.getId());
+//				String md5Hash = Hash.calculateMd5(c);
+//				md5Hashes.add(md5Hash);
+//				knowns.add(SleuthkitJNI.lookupHash(md5Hash).toLong());
+//			}
+//			updateHashesAndKnowns(ids, md5Hashes, knowns);
+//			return knowns;
+//		} catch (TskException ex) {
+//			Logger.getLogger(SleuthkitCase.class.getName()).log(Level.SEVERE,
+//					"Error looking up known status", ex);
+//		} catch(SQLException ex) {
+//			Logger.getLogger(SleuthkitCase.class.getName()).log(Level.SEVERE,
+//				"Error updating SQL database", ex);
+//		}
+//		throw new TskException("Error analyzing files");
+//	}
+} 
diff --git a/tsk3/auto/auto_db.cpp b/tsk3/auto/auto_db.cpp
index 5f0445ca137ed440cb594faf3f9bfc22c27634b4..2b9b1909eeb15c17c8e13a1e929e19dc69d94a40 100644
--- a/tsk3/auto/auto_db.cpp
+++ b/tsk3/auto/auto_db.cpp
@@ -28,7 +28,7 @@ TskAutoDb::TskAutoDb(TskDbSqlite * a_db, TSK_HDB_INFO * a_NSRLDb, TSK_HDB_INFO *
     m_curFsId = 0;
     m_curVsId = 0;
     m_blkMapFlag = false;
-    m_fileHashFlag = true;
+    m_fileHashFlag = false;
     m_vsFound = false;
     m_volFound = false;
     m_stopped = false;
@@ -511,7 +511,7 @@ TskAutoDb::processAttribute(TSK_FS_FILE * fs_file,
 
         TSK_AUTO_CASE_KNOWN_FILE_ENUM file_known = TSK_AUTO_CASE_FILE_KNOWN_UNKNOWN;
 
-        if (m_fileHashFlag && isFile(fs_file)) {
+		if (m_fileHashFlag && isFile(fs_file)) {
             if (md5HashAttr(hash, fs_attr)) {
                 return TSK_ERR;
             }