diff --git a/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java b/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java
index 8daf6c2266a7032e01934d1dbdbc836668889a39..82776adccab51e5c539d65e76af0a2a9954654d4 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java
@@ -43,7 +43,6 @@
  * to the case.
  */
 public abstract class AbstractFile extends AbstractContent {
-
 	protected final TskData.TSK_DB_FILES_TYPE_ENUM fileType;
 	protected final TSK_FS_NAME_TYPE_ENUM dirType;
 	protected final TSK_FS_META_TYPE_ENUM metaType;
@@ -61,6 +60,7 @@ public abstract class AbstractFile extends AbstractContent {
 	private String localPath; ///< local path as stored in db tsk_files_path, is relative to the db, 
 	private String localAbsPath; ///< absolute path representation of the local path
 	private volatile RandomAccessFile localFileHandle;
+	private volatile boolean errorLoadingFromFileRepo = false;
 	private volatile java.io.File localFile;
 	private TskData.EncodingType encodingType;
 	//range support
@@ -86,6 +86,7 @@ public abstract class AbstractFile extends AbstractContent {
 	private boolean sha256HashDirty = false;	
 	private String mimeType;
 	private boolean mimeTypeDirty = false;
+	protected TskData.FileLocation location;
 	private static final Logger LOGGER = Logger.getLogger(AbstractFile.class.getName());
 	private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle");
 	private long dataSourceObjectId;
@@ -124,6 +125,7 @@ public abstract class AbstractFile extends AbstractContent {
 	 * @param knownState         knownState status of the file, or null if
 	 *                           unknown (default)
 	 * @param parentPath
+	 * @param location
 	 * @param mimeType           The MIME type of the file, can be null.
 	 * @param extension		        The extension part of the file name (not
 	 *                           including the '.'), can be null.
@@ -143,6 +145,7 @@ public abstract class AbstractFile extends AbstractContent {
 			int uid, int gid,
 			String md5Hash, String sha256Hash, FileKnown knownState,
 			String parentPath,
+			TskData.FileLocation location,
 			String mimeType,
 			String extension) {
 		super(db, objId, name);
@@ -173,6 +176,7 @@ public abstract class AbstractFile extends AbstractContent {
 			this.knownState = knownState;
 		}
 		this.parentPath = parentPath;
+		this.location = location;
 		this.mimeType = mimeType;
 		this.extension = extension == null ? "" : extension;
 		this.encodingType = TskData.EncodingType.NONE;
@@ -777,6 +781,13 @@ public boolean isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM flag) {
 	public String getDirFlagAsString() {
 		return dirFlag.toString();
 	}
+	
+	/**
+	 * @return The directory flag value.
+	 */
+	public TSK_FS_NAME_FLAG_ENUM getDirFlag() {
+		return dirFlag;
+	}
 
 	/**
 	 * @return a string representation of the meta flags
@@ -791,6 +802,13 @@ public String getMetaFlagsAsString() {
 		return str;
 	}
 
+	/**
+	 * @return The meta flags as stored in the database.
+	 */
+	public short getMetaFlagsAsInt() {
+		return TSK_FS_META_FLAG_ENUM.toInt(metaFlags);
+	}
+	
 	/**
 	 * @param metaFlag the TSK_FS_META_FLAG_ENUM to check
 	 *
@@ -804,7 +822,7 @@ public boolean isMetaFlagSet(TSK_FS_META_FLAG_ENUM metaFlag) {
 	public final int read(byte[] buf, long offset, long len) throws TskCoreException {
 		//template method
 		//if localPath is set, use local, otherwise, use readCustom() supplied by derived class
-		if (localPathSet) {
+		if ((location == TskData.FileLocation.REPOSITORY) || localPathSet) {
 			return readLocal(buf, offset, len);
 		} else {
 			return readInt(buf, offset, len);
@@ -839,7 +857,7 @@ protected int readInt(byte[] buf, long offset, long len) throws TskCoreException
 	 * @throws TskCoreException exception thrown when file could not be read
 	 */
 	protected final int readLocal(byte[] buf, long offset, long len) throws TskCoreException {
-		if (!localPathSet) {
+		if ((location == TskData.FileLocation.LOCAL) && !localPathSet) {
 			throw new TskCoreException(
 					BUNDLE.getString("AbstractFile.readLocal.exception.msg1.text"));
 		}
@@ -959,6 +977,15 @@ public String getLocalPath() {
 		return localPath;
 	}
 
+	/**
+	 * Get the location of the file data
+	 * 
+	 * @return file location
+	 */
+	public TskData.FileLocation getFileLocation() {
+		return location;
+	}
+	
 	/**
 	 * Get local absolute path of the file, if localPath has been set
 	 *
@@ -1024,21 +1051,36 @@ public boolean canRead() {
 	 * @throws org.sleuthkit.datamodel.TskCoreException If the local path is not
 	 *                                                  set.
 	 */
-	private void loadLocalFile() throws TskCoreException {
-		if (!localPathSet) {
-			throw new TskCoreException(
-					BUNDLE.getString("AbstractFile.readLocal.exception.msg1.text"));
-		}
-
+	private synchronized void loadLocalFile() throws TskCoreException {
+		
 		// already been set
 		if (localFile != null) {
 			return;
 		}
+		
+		if (location.equals(TskData.FileLocation.LOCAL)) {
+			if (!localPathSet) {
+				throw new TskCoreException(
+						BUNDLE.getString("AbstractFile.readLocal.exception.msg1.text"));
+			}
 
-		synchronized (this) {
 			if (localFile == null) {
 				localFile = new java.io.File(localAbsPath);
 			}
+		} else {
+			if (errorLoadingFromFileRepo == true) {
+				// Don't try to download it again
+				throw new TskCoreException("Previously failed to load file with object ID " + getId() + " from file repository.");
+			}
+			
+			// Copy the file from the server
+			try {
+				localFile = FileRepository.downloadFromFileRepository(this);
+			} catch (TskCoreException ex) {
+				// If we've failed to download from the file repository, don't try again for this session.
+				errorLoadingFromFileRepo = true;
+				throw ex;
+			}
 		}
 	}
 
@@ -1087,6 +1129,7 @@ public String toString(boolean preserveState) {
 				+ "\t" + "parentPath " + parentPath + "\t" + "size " + size //NON-NLS
 				+ "\t" + "knownState " + knownState + "\t" + "md5Hash " + md5Hash + "\t" + "sha256Hash " + sha256Hash //NON-NLS
 				+ "\t" + "localPathSet " + localPathSet + "\t" + "localPath " + localPath //NON-NLS
+				+ "\t" + "location " + location // NON-NLS
 				+ "\t" + "localAbsPath " + localAbsPath + "\t" + "localFile " + localFile //NON-NLS
 				+ "]\t";
 	}
@@ -1222,7 +1265,7 @@ protected AbstractFile(SleuthkitCase db, long objId, TskData.TSK_FS_ATTR_TYPE_EN
 			TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType, TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
 			long size, long ctime, long crtime, long atime, long mtime, short modes, int uid, int gid, String md5Hash, FileKnown knownState,
 			String parentPath) {
-		this(db, objId, db.getDataSourceObjectId(objId), attrType, (int) attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, null, null);
+		this(db, objId, db.getDataSourceObjectId(objId), attrType, (int) attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, TskData.FileLocation.LOCAL, null, null);
 	}
 
 	/**
@@ -1267,7 +1310,7 @@ protected AbstractFile(SleuthkitCase db, long objId, TskData.TSK_FS_ATTR_TYPE_EN
 			String name, TskData.TSK_DB_FILES_TYPE_ENUM fileType, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime, short modes,
 			int uid, int gid, String md5Hash, FileKnown knownState, String parentPath, String mimeType) {
-		this(db, objId, dataSourceObjectId, attrType, (int) attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, null, null);
+		this(db, objId, dataSourceObjectId, attrType, (int) attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, TskData.FileLocation.LOCAL, null, null);
 	}
 
 	/**
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
index c3da2ea73394283627e42d91e29f5c069ecb2841..74eeb7f4a9e9cc75adb712f49373d558e8eeb9e3 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
+++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
@@ -259,6 +259,7 @@ TskData.fileKnown.known=known
 TskData.fileKnown.knownBad=notable
 TskData.fileKnown.exception.msg1.text=No FileKnown of value\: {0}
 TskData.encodingType.exception.msg1.text=No EncodingType of value\: {0}
+TskData.fileLocation.exception.msg1.text=No FileLocation of value\: {0}
 TskData.tskDbFilesTypeEnum.exception.msg1.text=No TSK_FILE_TYPE_ENUM of value\: {0}
 TskData.objectTypeEnum.exception.msg1.text=No ObjectType of value\: {0}
 TskData.tskImgTypeEnum.exception.msg1.text=No TSK_IMG_TYPE_ENUM of value\: {0}
@@ -352,4 +353,7 @@ IntersectionFilter.displayName.text=Intersection
 tagsFilter.displayName.text=Must be tagged
 TextFilter.displayName.text=Must include text:
 TypeFilter.displayName.text=Limit event types to
-FileTypesFilter.displayName.text=Limit file types to
\ No newline at end of file
+FileTypesFilter.displayName.text=Limit file types to
+FileRepository.downloadError.title.text=Error downloading from file repository
+FileRepository.downloadError.msg.text=Failed to download file with object ID {0} and SHA-256 hash {1}
+FileRepository.notEnabled.msg.text=File repository is not enabled
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED
index c3da2ea73394283627e42d91e29f5c069ecb2841..74eeb7f4a9e9cc75adb712f49373d558e8eeb9e3 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED
+++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED
@@ -259,6 +259,7 @@ TskData.fileKnown.known=known
 TskData.fileKnown.knownBad=notable
 TskData.fileKnown.exception.msg1.text=No FileKnown of value\: {0}
 TskData.encodingType.exception.msg1.text=No EncodingType of value\: {0}
+TskData.fileLocation.exception.msg1.text=No FileLocation of value\: {0}
 TskData.tskDbFilesTypeEnum.exception.msg1.text=No TSK_FILE_TYPE_ENUM of value\: {0}
 TskData.objectTypeEnum.exception.msg1.text=No ObjectType of value\: {0}
 TskData.tskImgTypeEnum.exception.msg1.text=No TSK_IMG_TYPE_ENUM of value\: {0}
@@ -352,4 +353,7 @@ IntersectionFilter.displayName.text=Intersection
 tagsFilter.displayName.text=Must be tagged
 TextFilter.displayName.text=Must include text:
 TypeFilter.displayName.text=Limit event types to
-FileTypesFilter.displayName.text=Limit file types to
\ No newline at end of file
+FileTypesFilter.displayName.text=Limit file types to
+FileRepository.downloadError.title.text=Error downloading from file repository
+FileRepository.downloadError.msg.text=Failed to download file with object ID {0} and SHA-256 hash {1}
+FileRepository.notEnabled.msg.text=File repository is not enabled
diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java
index eca9630950c463208eac5a2b6e5c4367ecebbc3f..4d7b38f01ed786a9c161b109162c44e5a6015bbf 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java
@@ -204,7 +204,7 @@ private void createFileTables(Statement stmt) throws SQLException {
 				+ "crtime " + dbQueryHelper.getBigIntType() + ", atime " + dbQueryHelper.getBigIntType() + ", "
 				+ "mtime " + dbQueryHelper.getBigIntType() + ", mode INTEGER, uid INTEGER, gid INTEGER, md5 TEXT, sha256 TEXT, "
 				+ "known INTEGER, "
-				+ "parent_path TEXT, mime_type TEXT, extension TEXT, "
+				+ "parent_path TEXT, location INTEGER NOT NULL, mime_type TEXT, extension TEXT, "
 				+ "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
 				+ "FOREIGN KEY(fs_obj_id) REFERENCES tsk_fs_info(obj_id) ON DELETE CASCADE, "
 				+ "FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE)");
diff --git a/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java b/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java
index ef147fb10c980eb874594e42931342e0ba336dd2..0e676e4ee6313e92d0ea0cfa82c0834281453d5d 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java
@@ -71,11 +71,14 @@ public class DerivedFile extends AbstractFile {
 	 * @param mtime              The modified time of the file.
 	 * @param md5Hash            The MD5 hash of the file, null if not yet
 	 *                           calculated.
+	 * @param sha256Hash         The SHA-256 hash of the file, null if not yet
+	 *                           calculated.
 	 * @param knownState         The known state of the file from a hash
 	 *                           database lookup, null if not yet looked up.
 	 * @param parentPath         The path of the parent of the file.
 	 * @param localPath          The absolute path of the file in secondary
 	 *                           storage.
+	 * @param location           The location of the file.
 	 * @param parentId           The object id of parent of the file.
 	 * @param mimeType           The MIME type of the file, null if it has not
 	 *                           yet been determined.
@@ -94,6 +97,7 @@ public class DerivedFile extends AbstractFile {
 			String md5Hash, String sha256Hash, FileKnown knownState,
 			String parentPath,
 			String localPath,
+			TskData.FileLocation location,
 			long parentId,
 			String mimeType,
 			TskData.EncodingType encodingType,
@@ -102,7 +106,7 @@ public class DerivedFile extends AbstractFile {
 		// through the class hierarchy contructors.
 		super(db, objId, dataSourceObjectId, TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
 				name, TSK_DB_FILES_TYPE_ENUM.LOCAL, 0L, 0, dirType, metaType, dirFlag,
-				metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension);
+				metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, location, mimeType, extension);
 		setLocalFilePath(localPath);
 		setEncodingType(encodingType);
 	}
@@ -306,7 +310,7 @@ protected DerivedFile(SleuthkitCase db,
 		this(db, objId, db.getDataSourceObjectId(objId), name, dirType, metaType, dirFlag, metaFlags, size,
 				ctime, crtime, atime, mtime,
 				md5Hash, null, knownState,
-				parentPath, localPath, parentId, null, TskData.EncodingType.NONE, null);
+				parentPath, localPath, TskData.FileLocation.LOCAL, parentId, null, TskData.EncodingType.NONE, null);
 	}
 
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Directory.java b/bindings/java/src/org/sleuthkit/datamodel/Directory.java
index 39b0c62188ef261354eedeb1596ba6f6f2fd0e6f..49e690cd1701ec2965200317773538c1c3dbf799 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/Directory.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/Directory.java
@@ -69,9 +69,12 @@ public class Directory extends FsContent {
 	 * @param gid                The GID for the file.
 	 * @param md5Hash            The MD5 hash of the file, null if not yet
 	 *                           calculated.
+	 * @param sha256Hash         The SHA-256 hash of the file, null if not yet
+	 *                           calculated.
 	 * @param knownState         The known state of the file from a hash
 	 *                           database lookup, null if not yet looked up.
 	 * @param parentPath         The path of the parent of the file.
+	 * @param location           The location of the file.
 	 */
 	Directory(SleuthkitCase db,
 			long objId,
@@ -85,8 +88,8 @@ public class Directory extends FsContent {
 			long size,
 			long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid,
-			String md5Hash, String sha256Hash, FileKnown knownState, String parentPath) {
-		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, null, null);
+			String md5Hash, String sha256Hash, FileKnown knownState, String parentPath, TskData.FileLocation location) {
+		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, location, null, null);
 	}
 
 	/**
@@ -247,6 +250,6 @@ protected Directory(SleuthkitCase db,
 			long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid,
 			String md5Hash, FileKnown knownState, String parentPath) {
-		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath);
+		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, TskData.FileLocation.LOCAL);
 	}
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/File.java b/bindings/java/src/org/sleuthkit/datamodel/File.java
index c6758c0ff88657acb513b55810acbc445b518df3..a0178482fb04a2eb17df9a20445a51daeb42d7fb 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/File.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/File.java
@@ -72,6 +72,7 @@ public class File extends FsContent {
 	 * @param knownState         The known state of the file from a hash
 	 *                           database lookup, null if not yet looked up.
 	 * @param parentPath         The path of the parent of the file.
+	 * @param location           The location of the file data.
 	 * @param mimeType           The MIME type of the file, null if it has not
 	 *                           yet been determined.
 	 * @param extension	         The extension part of the file name (not
@@ -89,9 +90,9 @@ public class File extends FsContent {
 			long size,
 			long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid,
-			String md5Hash, String sha256Hash, FileKnown knownState, String parentPath, String mimeType,
+			String md5Hash, String sha256Hash, FileKnown knownState, String parentPath, TskData.FileLocation location, String mimeType,
 			String extension) {
-		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension);
+		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, location, mimeType, extension);
 	}
 
 	/**
@@ -245,6 +246,6 @@ protected File(SleuthkitCase db,
 			String name, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid, String md5Hash, FileKnown knownState, String parentPath, String mimeType) {
-		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, mimeType, null);
+		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, TskData.FileLocation.LOCAL, mimeType, null);
 	}
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/FileRepository.java b/bindings/java/src/org/sleuthkit/datamodel/FileRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c4b456b4acc1f195adbeda028df9c0efa0a5b7a
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/FileRepository.java
@@ -0,0 +1,315 @@
+/*
+ * SleuthKit Java Bindings
+ *
+ * Copyright 2020 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.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Class to represent a file repository.
+ */
+public class FileRepository {
+	private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle");
+	private static final Logger logger = Logger.getLogger(FileRepository.class.getName());
+	private static FileRepositoryErrorHandler errorHandler;
+	
+	private final static String FILE_PATH = "v1/files/";
+	private final FileRepositorySettings settings;
+	private final File fileDownloadFolder;
+	
+	private static FileRepository instance;
+
+	/**
+     * Create the file repository.
+     *
+     * @param settings          The file repository settings
+     * @param fileDownloadPath  The temporary folder to download files to from the repository
+     */
+	private FileRepository(FileRepositorySettings settings, File fileDownloadPath) {
+		this.settings = settings;
+		this.fileDownloadFolder = fileDownloadPath;
+	}
+	
+	/**
+     * Initializes the file repository.
+     *
+     * @param settings          The file repository settings
+     * @param fileDownloadPath  The temporary folder to download files to from the repository
+     */	
+	public static synchronized void initialize(FileRepositorySettings settings, File fileDownloadPath) {
+		// If the download path is changing, delete any files in the old one
+		if ((instance != null) && (instance.fileDownloadFolder != null)
+				&& ( ! instance.fileDownloadFolder.equals(fileDownloadPath))) {
+			deleteDownloadFolder(instance.fileDownloadFolder);
+		}
+		instance = new FileRepository(settings, fileDownloadPath);
+	}
+	
+	/**
+     * De-initializes the file repository.
+     */	
+	public static synchronized void deinitialize() {
+		if (instance != null) {
+			// Delete the temp folder
+			deleteDownloadFolder(instance.fileDownloadFolder);
+		}
+		
+		instance = null;
+	}
+	
+	/**
+	 * Check if the file repository is enabled.
+	 * 
+	 * @return true if enabled, false otherwise.
+	 */
+	public static boolean isEnabled() {
+		return instance != null;
+	}
+	
+	/**
+	 * Set the error handling callback.
+	 * 
+	 * @param handler The error handler.
+	 */
+	public static synchronized void setErrorHandler(FileRepositoryErrorHandler handler) {
+		errorHandler = handler;
+	}
+	
+	/**
+	 * Report an error to the user.
+	 * The idea is to use this for cases where it's a user error that may be able
+	 * to be corrected through changing the repository settings.
+	 * 
+	 * @param errorTitle The title for the error display.
+	 * @param errorStr   The error message.
+	 */
+	private static synchronized void reportError(String errorTitle, String errorStr) {
+		if (errorHandler != null) {
+			errorHandler.displayErrorToUser(errorTitle, errorStr);
+		}
+	}
+	
+	/**
+	 * Delete the folder of downloaded files.
+	 */
+	private static synchronized void deleteDownloadFolder(File dirPath) {
+        if (dirPath.isDirectory() == false || dirPath.exists() == false) {
+            return;
+        }
+
+        File[] files = dirPath.listFiles();
+        if (files != null) {
+            for (File file : files) {
+                if (file.isDirectory()) {
+                    deleteDownloadFolder(file);
+                } else {
+                    if (file.delete() == false) {
+                        logger.log(Level.WARNING, "Failed to delete file {0}", file.getPath()); //NON-NLS
+                    }
+                }
+            }
+        }
+        if (dirPath.delete() == false) {
+            logger.log(Level.WARNING, "Failed to delete the empty directory at {0}", dirPath.getPath()); //NON-NLS
+        }
+	}
+	
+	/**
+     * Download a file from the file repository.
+	 * The caller must ensure that this is not called on the same file multiple times concurrently. 
+     *
+     * @param abstractFile The file being downloaded. 
+	 * 
+	 * @return The downloaded file.
+	 * 
+	 * @throws TskCoreException
+     */
+	public static synchronized File downloadFromFileRepository(AbstractFile abstractFile) throws TskCoreException {
+
+		if (instance == null) {
+			String title = BUNDLE.getString("FileRepository.downloadError.title.text");
+			String msg = BUNDLE.getString("FileRepository.notEnabled.msg.text");
+			reportError(title, msg);
+			throw new TskCoreException("File repository is not enabled");
+		}
+		
+		if (! abstractFile.getFileLocation().equals(TskData.FileLocation.REPOSITORY)) {
+			throw new TskCoreException("File with object ID " + abstractFile.getId() + " is not stored in the file repository");
+		}
+		
+		if (abstractFile.getSha256Hash() == null || abstractFile.getSha256Hash().isEmpty()) {
+			throw new TskCoreException("File with object ID " + abstractFile.getId() + " has no SHA-256 hash and can not be downloaded");
+		}
+		
+		// Download the file if it's not already there.
+		String targetPath = Paths.get(instance.fileDownloadFolder.getAbsolutePath(), abstractFile.getSha256Hash()).toString();
+		if ( ! new File(targetPath).exists()) {
+			instance.downloadFile(abstractFile, targetPath);
+		}
+		
+		// Check that we got the file.
+		File tempFile = new File(targetPath);
+		if (tempFile.exists()) {
+			return tempFile;
+		} else {
+			String title = BUNDLE.getString("FileRepository.downloadError.title.text");
+			String msg = MessageFormat.format(BUNDLE.getString("FileRepository.downloadError.msg.text"), abstractFile.getId(), abstractFile.getSha256Hash());
+			reportError(title, msg);
+			throw new TskCoreException("Failed to download file with object ID " + abstractFile.getId() 
+					+ " and SHA-256 hash " + abstractFile.getSha256Hash() + " from file repository");
+		}
+	}
+	
+	/**
+     * Download the file.
+     *
+     * @param abstractFile The file being downloaded.
+     * @param targetPath   The location to save the file to.
+	 * 
+	 * @throws TskCoreException
+     */
+	private void downloadFile(AbstractFile abstractFile, String targetPath) throws TskCoreException {		
+		
+		String url = "http://" + settings.getAddress() + ":" + settings.getPort() + "/" + FILE_PATH + abstractFile.getSha256Hash();
+		
+		List<String> command = new ArrayList<>();
+		command.add("curl");
+		command.add("-X");
+		command.add("GET");
+		command.add(url);
+		command.add("-H");
+		command.add("accept: */*");
+		command.add("--output");
+		command.add(targetPath);
+		
+		ProcessBuilder processBuilder = new ProcessBuilder(command).inheritIO();
+		try {
+			Process process = processBuilder.start();
+			process.waitFor();
+		} catch (IOException ex) {
+			String title = BUNDLE.getString("FileRepository.downloadError.title.text");
+			String msg = MessageFormat.format(BUNDLE.getString("FileRepository.downloadError.msg.text"), abstractFile.getId(), abstractFile.getSha256Hash());
+			reportError(title, msg);
+			throw new TskCoreException("Error downloading file with SHA-256 hash " + abstractFile.getSha256Hash() + " from file repository", ex);
+		} catch (InterruptedException ex) {
+			throw new TskCoreException("Interrupted while downloading file with SHA-256 hash " + abstractFile.getSha256Hash() + " from file repository", ex);
+		}
+	}
+	
+	/**
+     * Upload a given file to the file repository.
+     *
+     * @param filePath The path on disk to the file being uploaded.
+	 * 
+	 * @throws TskCoreException
+     */
+	public static synchronized void uploadToFileRepository(String filePath) throws TskCoreException {
+	
+		if (instance == null) {
+			throw new TskCoreException("File repository is not enabled");
+		}
+		
+		File file = new File(filePath);
+		if (! file.exists()) {
+			throw new TskCoreException("Error uploading file " + filePath + " to file repository - file does not exist");
+		}
+		
+		// Upload the file.
+		instance.uploadFile(file);
+	}
+	
+	/**
+     * Upload the file.
+     *
+     * @param file The file being uploaded.
+	 * 
+	 * @throws TskCoreException
+     */	
+	private void uploadFile(File file) throws TskCoreException {
+		String url = "http://" + settings.getAddress() + ":" + settings.getPort() + "/" + FILE_PATH;
+		
+		// Example: curl -X POST "http://localhost:8080/api/files" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "file=@Report.xml"
+		List<String> command = new ArrayList<>();
+		command.add("curl");
+		command.add("-X");
+		command.add("POST");
+		command.add(url);
+		command.add("-H");
+		command.add("accept: application/json");
+		command.add("-H");
+		command.add("Content-Type: multipart/form-data");
+		command.add("-F");
+		command.add("file=@" + file.getAbsolutePath());
+		
+		ProcessBuilder processBuilder = new ProcessBuilder(command).inheritIO();
+		try {
+			Process process = processBuilder.start();
+			process.waitFor();
+		} catch (IOException | InterruptedException ex) {
+			throw new TskCoreException("Error saving file at " + file.getAbsolutePath() + " to file repository", ex);
+		}	
+	}	
+		
+	/**
+	 * Utility class to hold the file repository server settings.
+	 */
+	static public class FileRepositorySettings {
+		private final String address;
+		private final String port;
+		
+		/**
+		 * Create a FileRepositorySettings instance for the server.
+		 * 
+		 * @param address The IP address/hostname of the server.
+		 * @param port    The port.
+		 */
+		public FileRepositorySettings(String address, String port) {
+			this.address = address;
+			this.port = port;
+		}
+		
+		String getAddress() {
+			return address;
+		}
+		
+		String getPort() {
+			return port;
+		}
+	}
+	
+	/**
+	 * Callback class to use for error reporting. 
+	 */
+	public interface FileRepositoryErrorHandler {
+		/**
+		 * Handles displaying an error message to the user (if appropriate).
+		 * 
+		 * @param title The title for the error display.
+		 * @param error The more detailed error message to display.
+		 */
+		void displayErrorToUser(String title, String error);
+	}
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/FsContent.java b/bindings/java/src/org/sleuthkit/datamodel/FsContent.java
index aa4bb739d06a0bad71b343ab9766c75f5f38511a..29f5158d1e29a9272d7cc0881d28f9eaf1e66fa9 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/FsContent.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/FsContent.java
@@ -101,6 +101,7 @@ public abstract class FsContent extends AbstractFile {
 	 * @param knownState         The known state of the file from a hash
 	 *                           database lookup, null if not yet looked up.
 	 * @param parentPath         The path of the parent of the file.
+	 * @param location           The location of the file data.
 	 * @param mimeType           The MIME type of the file, null if it has not
 	 *                           yet been determined.
 	 * @param extension          The extension part of the file name (not
@@ -122,9 +123,10 @@ public abstract class FsContent extends AbstractFile {
 			short modes, int uid, int gid,
 			String md5Hash, String sha256Hash, FileKnown knownState,
 			String parentPath,
+			TskData.FileLocation location,
 			String mimeType,
 			String extension) {
-		super(db, objId, dataSourceObjectId, attrType, attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension);
+		super(db, objId, dataSourceObjectId, attrType, attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, location, mimeType, extension);
 		this.fsObjId = fsObjId;
 	}
 
@@ -383,7 +385,7 @@ public String toString(boolean preserveState) {
 			String name, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid, String md5Hash, FileKnown knownState, String parentPath) {
-		this(db, objId, db.getDataSourceObjectId(objId), fsObjId, attrType, (int) attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, null, null);
+		this(db, objId, db.getDataSourceObjectId(objId), fsObjId, attrType, (int) attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, TskData.FileLocation.LOCAL, null, null);
 	}
 
 	/**
@@ -442,6 +444,6 @@ public String toString(boolean preserveState) {
 			String name, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid, String md5Hash, FileKnown knownState, String parentPath, String mimeType) {
-		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, mimeType, null);
+		this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, knownState, parentPath, TskData.FileLocation.LOCAL, mimeType, null);
 	}
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java b/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java
index ab98c64e75a8ffc50219df350fbe2c63b2a539f6..9f8a0a8e4ed495fe9a12a7c565ec611e2df82a60 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java
@@ -79,6 +79,7 @@ public class LayoutFile extends AbstractFile {
 	 * @param knownState         The known state of the file from a hash
 	 *                           database lookup, null if not yet looked up.
 	 * @param parentPath         The path of the parent of the file.
+	 * @param location           The location of the file.
 	 * @param mimeType           The MIME type of the file, null if it has not
 	 *                           yet been determined.
 	 */
@@ -92,8 +93,8 @@ public class LayoutFile extends AbstractFile {
 			long size,
 			long ctime, long crtime, long atime, long mtime,
 			String md5Hash, String sha256Hash, FileKnown knownState,
-			String parentPath, String mimeType) {
-		super(db, objId, dataSourceObjectId, TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0, name, fileType, 0L, 0, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, mimeType, SleuthkitCase.extractExtension(name));
+			String parentPath, TskData.FileLocation location, String mimeType) {
+		super(db, objId, dataSourceObjectId, TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0, name, fileType, 0L, 0, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, location, mimeType, SleuthkitCase.extractExtension(name));
 	}
 
 	/**
@@ -280,6 +281,6 @@ protected LayoutFile(SleuthkitCase db, long objId, String name,
 			TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
 			long size, String md5Hash, FileKnown knownState, String parentPath) {
-		this(db, objId, db.getDataSourceObjectId(objId), name, fileType, dirType, metaType, dirFlag, metaFlags, size, 0L, 0L, 0L, 0L, md5Hash, null, knownState, parentPath, null);
+		this(db, objId, db.getDataSourceObjectId(objId), name, fileType, dirType, metaType, dirFlag, metaFlags, size, 0L, 0L, 0L, 0L, md5Hash, null, knownState, parentPath, TskData.FileLocation.LOCAL, null);
 	}
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/LocalDirectory.java b/bindings/java/src/org/sleuthkit/datamodel/LocalDirectory.java
index 8eafbefdbf6f4ea45cd4677d9107701fbf6ef4a3..92418608e1052f03025e213c35100d548947d726 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/LocalDirectory.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/LocalDirectory.java
@@ -55,6 +55,7 @@ public class LocalDirectory extends SpecialDirectory {
 	 * @param md5Hash            The MD5 hash for the local directory.
 	 * @param knownState         The known state for the local directory
 	 * @param parentPath         The parent path for the local directory
+	 * @param location           The location of the directory.
 	 */
 	LocalDirectory(SleuthkitCase db,
 			long objId,
@@ -63,10 +64,10 @@ public class LocalDirectory extends SpecialDirectory {
 			TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
 			String md5Hash, String sha256Hash, FileKnown knownState,
-			String parentPath) {
+			String parentPath, TskData.FileLocation location) {
 		super(db, objId, dataSourceObjectId, TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0, name,
 				TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR, 0L, 0, dirType, metaType, dirFlag,
-				metaFlags, 0L, 0L, 0L, 0L, 0L, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, null);
+				metaFlags, 0L, 0L, 0L, 0L, 0L, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, location, null);
 	}
 	
 	/**
diff --git a/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java b/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java
index 99abf74e70cd7f77cbaa062e0fa1316072064611..f3ed981b0a0ae21abec95ec027be73ecda37d798 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java
@@ -68,6 +68,7 @@ public class LocalFile extends AbstractFile {
 	 *                           database lookup, null if not yet looked up.
 	 * @param parentId           The object id of parent of the file.
 	 * @param parentPath         The path of the parent of the file.
+	 * @param location           The location of the file.
 	 * @param dataSourceObjectId The object id of the data source for the file.
 	 * @param localPath          The absolute path of the file in secondary
 	 *                           storage.
@@ -84,14 +85,15 @@ public class LocalFile extends AbstractFile {
 			long size,
 			long ctime, long crtime, long atime, long mtime,
 			String mimeType, String md5Hash, String sha256Hash, FileKnown knownState,
-			long parentId, String parentPath,
+			long parentId, String parentPath, TskData.FileLocation location,
 			long dataSourceObjectId,
 			String localPath,
 			TskData.EncodingType encodingType,
 			String extension) {
 		super(db, objId, dataSourceObjectId, TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
 				name, fileType, 0L, 0, dirType, metaType, dirFlag,
-				metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension);
+				metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, 
+				location, mimeType, extension);
 		// TODO (AUT-1904): The parent id should be passed to AbstractContent 
 		// through the class hierarchy contructors, using 
 		// AbstractContent.UNKNOWN_ID as needed.
@@ -219,7 +221,7 @@ protected LocalFile(SleuthkitCase db,
 				size,
 				ctime, crtime, atime, mtime,
 				null, md5Hash, null, knownState,
-				AbstractContent.UNKNOWN_ID, parentPath,
+				AbstractContent.UNKNOWN_ID, parentPath, TskData.FileLocation.LOCAL,
 				db.getDataSourceObjectId(objId),
 				localPath,
 				TskData.EncodingType.NONE, null);
diff --git a/bindings/java/src/org/sleuthkit/datamodel/LocalFilesDataSource.java b/bindings/java/src/org/sleuthkit/datamodel/LocalFilesDataSource.java
index 0572291890859ea7ecaa7a61236cde43b1c102e5..f34e58785d26ec198c105bf94f354498efa332dd 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/LocalFilesDataSource.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/LocalFilesDataSource.java
@@ -66,9 +66,12 @@ public class LocalFilesDataSource extends VirtualDirectory implements DataSource
 	 * @param parentPath         The parent path for the virtual directory,
 	 *                           should be "/" if the virtual directory is a
 	 *                           data source.
+	 * @param location           The location of the file
 	 */
-	public LocalFilesDataSource(SleuthkitCase db, long objId, long dataSourceObjectId, String deviceId, String name, TskData.TSK_FS_NAME_TYPE_ENUM dirType, TskData.TSK_FS_META_TYPE_ENUM metaType, TskData.TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, String timezone, String md5Hash, String sha256Hash, TskData.FileKnown knownState, String parentPath) {
-		super(db, objId, dataSourceObjectId, name, dirType, metaType, dirFlag, metaFlags, md5Hash, sha256Hash, knownState, parentPath);
+	public LocalFilesDataSource(SleuthkitCase db, long objId, long dataSourceObjectId, String deviceId, String name, TskData.TSK_FS_NAME_TYPE_ENUM dirType, 
+			TskData.TSK_FS_META_TYPE_ENUM metaType, TskData.TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, String timezone, 
+			String md5Hash, String sha256Hash, TskData.FileKnown knownState, String parentPath, TskData.FileLocation location) {
+		super(db, objId, dataSourceObjectId, name, dirType, metaType, dirFlag, metaFlags, md5Hash, sha256Hash, knownState, parentPath, location);
 		this.objectId = objId;
 		this.deviceId = deviceId;
 		this.timezone = timezone;
@@ -263,6 +266,6 @@ private static void closeStatement(Statement statement) {
 	 */
 	@Deprecated
 	public LocalFilesDataSource(SleuthkitCase db, long objId, long dataSourceObjectId, String deviceId, String name, TskData.TSK_FS_NAME_TYPE_ENUM dirType, TskData.TSK_FS_META_TYPE_ENUM metaType, TskData.TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, String timezone, String md5Hash, TskData.FileKnown knownState, String parentPath) {
-		this(db, objId, dataSourceObjectId, deviceId, name, dirType, metaType, dirFlag, metaFlags, timezone, md5Hash, null, knownState, parentPath);
+		this(db, objId, dataSourceObjectId, deviceId, name, dirType, metaType, dirFlag, metaFlags, timezone, md5Hash, null, knownState, parentPath, TskData.FileLocation.LOCAL);
 	}	
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java b/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java
index df038e6021013a8f0a5b706e27721a721b2856d5..00ab42f5b5f4a9417a8349d569585b3c7765512f 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java
@@ -72,6 +72,7 @@ public class SlackFile extends FsContent {
 	 * @param knownState         The known state of the file from a hash
 	 *                           database lookup, null if not yet looked up.
 	 * @param parentPath         The path of the parent of the file.
+	 * @param location           The location of the file,
 	 * @param mimeType           The MIME type of the file, null if it has not
 	 *                           yet been determined.
 	 * @param extension	         The extension part of the file name (not
@@ -89,9 +90,9 @@ public class SlackFile extends FsContent {
 			long size,
 			long ctime, long crtime, long atime, long mtime,
 			short modes, int uid, int gid,
-			String md5Hash, String sha256Hash, FileKnown knownState, String parentPath, String mimeType,
+			String md5Hash, String sha256Hash, FileKnown knownState, String parentPath, TskData.FileLocation location, String mimeType,
 			String extension) {
-		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.SLACK, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension);
+		super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.SLACK, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, location, mimeType, extension);
 	}
 
 	/**
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
index f380bc88aedeeeaa47004a371a6fd833317bb446..3c171a0648b04bab432d5a4845fd07b35548a82a 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -2205,6 +2205,7 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot5toSchema8dot6(CaseDbSchem
 		acquireSingleUserCaseWriteLock();
 		try {
 			statement.execute("ALTER TABLE tsk_files ADD COLUMN sha256 TEXT");
+			statement.execute("ALTER TABLE tsk_files ADD COLUMN location INTEGER NOT NULL DEFAULT " + TskData.FileLocation.LOCAL.getValue());
 
 			return new CaseDbSchemaVersionNumber(8, 6);
 
@@ -2852,7 +2853,7 @@ public List<DataSource> getDataSources() throws TskCoreException {
 					final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue()
 							| TSK_FS_META_FLAG_ENUM.USED.getValue());
 					String parentPath = "/"; //NON-NLS
-					dataSource = new LocalFilesDataSource(this, objectId, objectId, deviceId, dsName, dirType, metaType, dirFlag, metaFlags, timezone, null, null, FileKnown.UNKNOWN, parentPath);
+					dataSource = new LocalFilesDataSource(this, objectId, objectId, deviceId, dsName, dirType, metaType, dirFlag, metaFlags, timezone, null, null, FileKnown.UNKNOWN, parentPath, TskData.FileLocation.LOCAL);
 				} else {
 					/*
 					 * Data found in 'tsk_image_info', so we build an Image.
@@ -2951,7 +2952,7 @@ public DataSource getDataSource(long objectId) throws TskDataException, TskCoreE
 					final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue()
 							| TSK_FS_META_FLAG_ENUM.USED.getValue());
 					String parentPath = "/"; //NON-NLS
-					dataSource = new LocalFilesDataSource(this, objectId, objectId, deviceId, dsName, dirType, metaType, dirFlag, metaFlags, timezone, null, null, FileKnown.UNKNOWN, parentPath);
+					dataSource = new LocalFilesDataSource(this, objectId, objectId, deviceId, dsName, dirType, metaType, dirFlag, metaFlags, timezone, null, null, FileKnown.UNKNOWN, parentPath, TskData.FileLocation.LOCAL);
 				} else {
 					/*
 					 * Data found in 'tsk_image_info', so we build an Image.
@@ -5618,8 +5619,8 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName,
 
 			// Insert a row for the virtual directory 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, md5, known, mime_type, parent_path, data_source_obj_id,extension)
-			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
+			// dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, known, mime_type, parent_path, location, data_source_obj_id,extension)
+			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 			PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 			statement.clearParameters();
 			statement.setLong(1, newObjId);
@@ -5673,6 +5674,8 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName,
 			// parent path
 			statement.setString(19, parentPath);
 
+			statement.setLong(20, TskData.FileLocation.LOCAL.getValue());
+			
 			// data source object id (same as object id if this is a data source)
 			long dataSourceObjectId;
 			if (0 == parentId) {
@@ -5680,15 +5683,15 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName,
 			} else {
 				dataSourceObjectId = getDataSourceObjectId(connection, parentId);
 			}
-			statement.setLong(20, dataSourceObjectId);
+			statement.setLong(21, dataSourceObjectId);
 
 			//extension, since this is not really file we just set it to null
-			statement.setString(21, null);
+			statement.setString(22, null);
 			connection.executeUpdate(statement);
 
 			return new VirtualDirectory(this, newObjId, dataSourceObjectId, directoryName, dirType,
 					metaType, dirFlag, metaFlags, null, null, FileKnown.UNKNOWN,
-					parentPath);
+					parentPath, TskData.FileLocation.LOCAL);
 		} catch (SQLException e) {
 			throw new TskCoreException("Error creating virtual directory '" + directoryName + "'", e);
 		} finally {
@@ -5763,8 +5766,8 @@ public LocalDirectory addLocalDirectory(long parentId, String directoryName, Cas
 
 			// Insert a row for the local directory 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, md5, sha256, known, mime_type, parent_path, data_source_obj_id)
-			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+			// dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, sha256, known, mime_type, parent_path, location, data_source_obj_id)
+			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 			PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 			statement.clearParameters();
 			statement.setLong(1, newObjId);
@@ -5808,19 +5811,21 @@ public LocalDirectory addLocalDirectory(long parentId, String directoryName, Cas
 
 			// parent path
 			statement.setString(19, parentPath);
+			
+			statement.setLong(20, TskData.FileLocation.LOCAL.getValue());
 
 			// data source object id
 			long dataSourceObjectId = getDataSourceObjectId(connection, parentId);
-			statement.setLong(20, dataSourceObjectId);
+			statement.setLong(21, dataSourceObjectId);
 
 			//extension, since this is a directory we just set it to null
-			statement.setString(21, null);
+			statement.setString(22, null);
 
 			connection.executeUpdate(statement);
 
 			return new LocalDirectory(this, newObjId, dataSourceObjectId, directoryName, dirType,
 					metaType, dirFlag, metaFlags, null, null, FileKnown.UNKNOWN,
-					parentPath);
+					parentPath, TskData.FileLocation.LOCAL);
 		} catch (SQLException e) {
 			throw new TskCoreException("Error creating local directory '" + directoryName + "'", e);
 		} finally {
@@ -5867,8 +5872,8 @@ public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String root
 			// its own object id.
 			// 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, md5, known, mime_type, parent_path, data_source_obj_id, extension)
-			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
+			// atime, mtime, md5, sha256, known, mime_type, parent_path, location, data_source_obj_id, extension)
+			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
 			PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 			preparedStatement.clearParameters();
 			preparedStatement.setLong(1, newObjId);
@@ -5896,11 +5901,12 @@ public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String root
 			preparedStatement.setNull(18, java.sql.Types.VARCHAR); // MIME type	
 			String parentPath = "/"; //NON-NLS
 			preparedStatement.setString(19, parentPath);
-			preparedStatement.setLong(20, newObjId);
-			preparedStatement.setString(21, null); //extension, just set it to null
+			preparedStatement.setLong(20, TskData.FileLocation.LOCAL.getValue());
+			preparedStatement.setLong(21, newObjId);
+			preparedStatement.setString(22, null); //extension, just set it to null
 			connection.executeUpdate(preparedStatement);
 
-			return new LocalFilesDataSource(this, newObjId, newObjId, deviceId, rootDirectoryName, dirType, metaType, dirFlag, metaFlags, timeZone, null, null, FileKnown.UNKNOWN, parentPath);
+			return new LocalFilesDataSource(this, newObjId, newObjId, deviceId, rootDirectoryName, dirType, metaType, dirFlag, metaFlags, timeZone, null, null, FileKnown.UNKNOWN, parentPath, TskData.FileLocation.LOCAL);
 
 		} catch (SQLException ex) {
 			throw new TskCoreException(String.format("Error creating local files data source with device id %s and directory name %s", deviceId, rootDirectoryName), ex);
@@ -6186,6 +6192,122 @@ public FileSystem addFileSystem(long parentObjId, long imgOffset, TskData.TSK_FS
 			releaseSingleUserCaseWriteLock();
 		}
 	}
+	
+	/**
+	 * Add a file system file to the database by supplying all fields to copy into the 
+	 * tsk_files entry. This should generally only be used when the location
+	 * field is not set to LOCAL since it does not create any additional database
+	 * entries for reading the file data (such as rows in tsk_files_path for local files,
+	 * rows in tsk_file_layout for layout files, etc). 
+	 *
+	 * @param fsObjId              The fs object ID or null
+	 * @param fileName		       The name of the file.
+	 * @param fileType             The type of file
+	 * @param metaAddr		       The meta address of the file.
+	 * @param metaSeq		       The meta address sequence of the file.
+	 * @param attrType		       The attributed type of the file.
+	 * @param attrId		       The attribute id
+	 * @param dirFlag		       The allocated status from the name structure
+	 * @param metaFlags             The meta flags.
+	 * @param size			        The size of the file in bytes.
+	 * @param ctime			        The changed time of the file.
+	 * @param crtime		        The creation time of the file.
+	 * @param atime			        The accessed time of the file
+	 * @param mtime			        The modified time of the file.
+	 * @param md5                   The MD5 hash of the file (may be null).
+	 * @param sha256                The SHA-256 hash of the file (may be null).
+	 * @param known                 The FileKnown value of the file.
+	 * @param mimeType              The MIME type of the file (may be null).
+	 * @param isFile		        True, unless the file is a directory.
+	 * @param location              The location the file is stored.
+	 * @param parent		        The parent of the file (e.g., a virtual directory)
+	 * @param transaction           The current transaction
+	 *
+	 * @return Newly created file
+	 *
+	 * @throws TskCoreException
+	 */
+	public AbstractFile addFileSystemFile(long fsObjId,
+			String fileName,
+			TskData.TSK_DB_FILES_TYPE_ENUM fileType,
+			long metaAddr, long metaSeq,
+			TSK_FS_ATTR_TYPE_ENUM attrType, int attrId,
+			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size,
+			long ctime, long crtime, long atime, long mtime,
+			String md5, String sha256, FileKnown known, String mimeType,
+			boolean isFile, TskData.FileLocation location, Content parent,
+			CaseDbTransaction transaction) throws TskCoreException {
+
+		TimelineManager timelineManager = getTimelineManager();
+		Statement queryStatement = null;
+		try {
+			CaseDbConnection connection = transaction.getConnection();
+
+			// Insert a row for the local/logical file into the tsk_objects table.
+			// INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
+			long objectId = addObject(parent.getId(), TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection);
+
+			String parentPath;
+			long dataSourceObjId;
+
+			if (parent instanceof AbstractFile) {
+				AbstractFile parentFile = (AbstractFile) parent;
+				if (isRootDirectory(parentFile, transaction)) {
+					parentPath = "/";
+				} else {
+					parentPath = parentFile.getParentPath() + parent.getName() + "/"; //NON-NLS
+				}
+				dataSourceObjId = parentFile.getDataSourceObjectId();
+			} else {
+				parentPath = "/";
+				dataSourceObjId = getDataSourceObjectId(connection, parent.getId());
+			}
+
+			PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE_SYSTEM_FILE);
+			statement.clearParameters();
+			statement.setLong(1, objectId);											// obj_is
+			statement.setLong(2, fsObjId);											// fs_obj_id 
+			statement.setLong(3, dataSourceObjId);									// data_source_obj_id 
+			statement.setShort(4, (short) attrType.getValue());						// attr_type
+			statement.setInt(5, attrId);											// attr_id
+			statement.setString(6, fileName);										// name
+			statement.setLong(7, metaAddr);											// meta_addr
+			statement.setInt(8, (int)metaSeq);											// meta_addr
+			statement.setShort(9, fileType.getFileType());	//type
+			statement.setShort(10, (short) 1);										// has_path
+			TSK_FS_NAME_TYPE_ENUM dirType = isFile ? TSK_FS_NAME_TYPE_ENUM.REG : TSK_FS_NAME_TYPE_ENUM.DIR;
+			statement.setShort(11, dirType.getValue());								// dir_type
+			TSK_FS_META_TYPE_ENUM metaType = isFile ? TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG : TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR;
+			statement.setShort(12, metaType.getValue());							// meta_type
+			statement.setShort(13, dirFlag.getValue());								// dir_flags
+			statement.setShort(14, metaFlags);										// meta_flags
+			statement.setLong(15, size < 0 ? 0 : size);
+			statement.setLong(16, ctime);
+			statement.setLong(17, crtime);
+			statement.setLong(18, atime);
+			statement.setLong(19, mtime);
+			statement.setString(20, md5);                      // MD5
+			statement.setString(21, sha256);                   // SHA-256
+			statement.setByte(22, known.getFileKnownValue());  // Known
+			statement.setString(23, mimeType);                 // MIME type
+			statement.setString(24, parentPath);
+			statement.setLong(25, location.getValue());
+			final String extension = extractExtension(fileName);
+			statement.setString(26, extension);
+
+			connection.executeUpdate(statement);
+
+			DerivedFile derivedFile = new DerivedFile(this, objectId, dataSourceObjId, fileName, dirType, metaType, dirFlag, metaFlags,
+					size, ctime, crtime, atime, mtime, md5, sha256, known, parentPath, mimeType, TskData.FileLocation.LOCAL, parent.getId(), null, null, extension);
+			timelineManager.addEventsForNewFile(derivedFile, connection);
+
+			return getAbstractFileById(objectId, connection);
+		} catch (SQLException ex) {
+			throw new TskCoreException("Failed to add file system file", ex);
+		} finally {
+			closeStatement(queryStatement);
+		}
+	}	
 
 	/**
 	 * Add a file system file.
@@ -6267,15 +6389,19 @@ public FsContent addFileSystemFile(long dataSourceObjId, long fsObjId,
 			statement.setLong(17, crtime);
 			statement.setLong(18, atime);
 			statement.setLong(19, mtime);
-			statement.setString(20, parentPath);
+			statement.setNull(20, java.sql.Types.VARCHAR);                 // MD5
+			statement.setNull(21, java.sql.Types.VARCHAR);                 // SHA-256
+			statement.setByte(22, FileKnown.UNKNOWN.getFileKnownValue());  // Known
+			statement.setNull(23, java.sql.Types.VARCHAR);                 // MIME type
+			statement.setString(24, parentPath);
+			statement.setLong(25, TskData.FileLocation.LOCAL.getValue());
 			final String extension = extractExtension(fileName);
-			statement.setString(21, extension);
+			statement.setString(26, extension);
 
 			connection.executeUpdate(statement);
 
 			DerivedFile derivedFile = new DerivedFile(this, objectId, dataSourceObjId, fileName, dirType, metaType, dirFlag, metaFlags,
-					size, ctime, crtime, atime, mtime, null, null, null, parentPath, null, parent.getId(), null, null, extension);
-
+					size, ctime, crtime, atime, mtime, null, null, null, parentPath, null, TskData.FileLocation.LOCAL, parent.getId(), null, null, extension);
 			timelineManager.addEventsForNewFile(derivedFile, connection);
 
 			transaction.commit();
@@ -6285,7 +6411,7 @@ public FsContent addFileSystemFile(long dataSourceObjId, long fsObjId,
 					attrType, attrId, fileName, metaAddr, metaSeq,
 					dirType, metaType, dirFlag, metaFlags,
 					size, ctime, crtime, atime, mtime,
-					(short) 0, 0, 0, null, null, null, parentPath, null,
+					(short) 0, 0, 0, null, null, null, parentPath, TskData.FileLocation.LOCAL, null,
 					extension);
 
 		} catch (SQLException ex) {
@@ -6382,8 +6508,8 @@ public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange>
 				 * 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, md5, known, mime_type,
-				 * parent_path, data_source_obj_id,extension) VALUES (?, ?, ?,
-				 * ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
+				 * parent_path, location, data_source_obj_id,extension) VALUES (?, ?, ?,
+				 * ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
 				 */
 				PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 				prepStmt.clearParameters();
@@ -6406,10 +6532,11 @@ public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange>
 				prepStmt.setByte(17, FileKnown.UNKNOWN.getFileKnownValue()); // Known
 				prepStmt.setNull(18, java.sql.Types.VARCHAR); // MIME type
 				prepStmt.setNull(19, java.sql.Types.VARCHAR); // parent path
-				prepStmt.setLong(20, parent.getId()); // data_source_obj_id
+				prepStmt.setLong(20, TskData.FileLocation.LOCAL.getValue()); // location
+				prepStmt.setLong(21, parent.getId()); // data_source_obj_id
 
 				//extension, since this is not a FS file we just set it to null
-				prepStmt.setString(21, null);
+				prepStmt.setString(22, null);
 				connection.executeUpdate(prepStmt);
 
 				/*
@@ -6441,7 +6568,7 @@ public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange>
 						0L, 0L, 0L, 0L,
 						null, null,
 						FileKnown.UNKNOWN,
-						parent.getUniquePath(),
+						parent.getUniquePath(), TskData.FileLocation.LOCAL,
 						null));
 			}
 
@@ -6563,8 +6690,8 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws
 				 * 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, md5, known, mime_type,
-				 * parent_path, data_source_obj_id,extenion) VALUES (?, ?, ?, ?,
-				 * ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
+				 * parent_path, location, data_source_obj_id,extenion) VALUES (?, ?, ?, ?,
+				 * ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
 				 */
 				PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 				prepStmt.clearParameters();
@@ -6591,8 +6718,9 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws
 				prepStmt.setByte(17, FileKnown.UNKNOWN.getFileKnownValue()); // Known
 				prepStmt.setNull(18, java.sql.Types.VARCHAR); // MIME type	
 				prepStmt.setString(19, parentPath); // parent path
-				prepStmt.setLong(20, carvedFilesDir.getDataSourceObjectId()); // data_source_obj_id
-				prepStmt.setString(21, extractExtension(carvedFile.getName())); //extension
+				prepStmt.setLong(20, TskData.FileLocation.LOCAL.getValue());
+				prepStmt.setLong(21, carvedFilesDir.getDataSourceObjectId()); // data_source_obj_id
+				prepStmt.setString(22, extractExtension(carvedFile.getName())); //extension
 				connection.executeUpdate(prepStmt);
 
 				/*
@@ -6626,7 +6754,7 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws
 						0L, 0L, 0L, 0L,
 						null, null,
 						FileKnown.UNKNOWN,
-						parentPath,
+						parentPath, TskData.FileLocation.LOCAL,
 						null));
 			}
 
@@ -6762,13 +6890,15 @@ public DerivedFile addDerivedFile(String fileName, String localPath,
 
 			//parent path
 			statement.setString(19, parentPath);
+			
+			statement.setLong(20, TskData.FileLocation.LOCAL.getValue());
 
 			// root data source object id
 			long dataSourceObjId = getDataSourceObjectId(connection, parentId);
-			statement.setLong(20, dataSourceObjId);
+			statement.setLong(21, dataSourceObjId);
 			final String extension = extractExtension(fileName);
 			//extension
-			statement.setString(21, extension);
+			statement.setString(22, extension);
 
 			connection.executeUpdate(statement);
 
@@ -6776,7 +6906,7 @@ public DerivedFile addDerivedFile(String fileName, String localPath,
 			addFilePath(connection, newObjId, localPath, encodingType);
 
 			DerivedFile derivedFile = new DerivedFile(this, newObjId, dataSourceObjId, fileName, dirType, metaType, dirFlag, metaFlags,
-					savedSize, ctime, crtime, atime, mtime, null, null, null, parentPath, localPath, parentId, null, encodingType, extension);
+					savedSize, ctime, crtime, atime, mtime, null, null, null, parentPath, localPath, TskData.FileLocation.LOCAL, parentId, null, encodingType, extension);
 
 			timelineManager.addEventsForNewFile(derivedFile, connection);
 			transaction.commit();
@@ -6886,7 +7016,7 @@ public DerivedFile updateDerivedFile(DerivedFile derivedFile, String localPath,
 			long dataSourceObjId = getDataSourceObjectId(connection, parentId);
 			final String extension = extractExtension(derivedFile.getName());
 			return new DerivedFile(this, derivedFile.getId(), dataSourceObjId, derivedFile.getName(), dirType, metaType, dirFlag, metaFlags,
-					savedSize, ctime, crtime, atime, mtime, null, null, null, parentPath, localPath, parentId, null, encodingType, extension);
+					savedSize, ctime, crtime, atime, mtime, null, null, null, parentPath, localPath, TskData.FileLocation.LOCAL, parentId, null, encodingType, extension);
 		} catch (SQLException ex) {
 			connection.rollbackTransaction();
 			throw new TskCoreException("Failed to add derived file to case database", ex);
@@ -6987,9 +7117,11 @@ public LocalFile addLocalFile(String fileName, String localPath,
 	 * @param crtime       The creation time of the file.
 	 * @param atime        The accessed time of the file
 	 * @param mtime        The modified time of the file.
-	 * @param md5          The MD5 hash of the file
+	 * @param md5          The MD5 hash of the file.
+	 * @param sha256       The SHA256 hash of the file.
 	 * @param known        The known status of the file (can be null)
 	 * @param mimeType     The MIME type of the file
+	 * @param location     The location of the file
 	 * @param isFile       True, unless the file is a directory.
 	 * @param encodingType Type of encoding used on the file
 	 * @param parent       The parent of the file (e.g., a virtual directory)
@@ -7003,9 +7135,11 @@ public LocalFile addLocalFile(String fileName, String localPath,
 	 */
 	public LocalFile addLocalFile(String fileName, String localPath,
 			long size, long ctime, long crtime, long atime, long mtime,
-			String md5, String sha256, FileKnown known, String mimeType,
+			String md5, String sha256, FileKnown known, String mimeType, 
+			TskData.FileLocation location,
 			boolean isFile, TskData.EncodingType encodingType,
 			Content parent, CaseDbTransaction transaction) throws TskCoreException {
+				
 		CaseDbConnection connection = transaction.getConnection();
 		Statement queryStatement = null;
 		try {
@@ -7017,8 +7151,8 @@ public LocalFile addLocalFile(String fileName, String localPath,
 			// Insert a row for the local/logical 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, md5, known, mime_type,
-			// parent_path, data_source_obj_id,extension)
-			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
+			// parent_path, location, data_source_obj_id,extension)
+			// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
 			PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
 			statement.clearParameters();
 			statement.setLong(1, objectId);
@@ -7065,9 +7199,10 @@ public LocalFile addLocalFile(String fileName, String localPath,
 				dataSourceObjId = getDataSourceObjectId(connection, parent.getId());
 			}
 			statement.setString(19, parentPath);
-			statement.setLong(20, dataSourceObjId);
+			statement.setLong(20, location.getValue());
+			statement.setLong(21, dataSourceObjId);
 			final String extension = extractExtension(fileName);
-			statement.setString(21, extension);
+			statement.setString(22, extension);
 
 			connection.executeUpdate(statement);
 			addFilePath(connection, objectId, localPath, encodingType);
@@ -7082,7 +7217,7 @@ public LocalFile addLocalFile(String fileName, String localPath,
 					savedSize,
 					ctime, crtime, atime, mtime,
 					mimeType, md5, sha256, known,
-					parent.getId(), parentPath,
+					parent.getId(), parentPath, location,
 					dataSourceObjId,
 					localPath,
 					encodingType, extension);
@@ -7196,8 +7331,8 @@ public LayoutFile addLayoutFile(String fileName,
 			 * Insert a row for the 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, md5, known, mime_type, parent_path,
-			 * data_source_obj_id,extenion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?,
+			 * mtime, md5, known, mime_type, parent_path, location,
+			 * data_source_obj_id,extenion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
 			 * ?, ?, ?, ?, ?, ?, ?,?)
 			 */
 			PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
@@ -7234,9 +7369,10 @@ public LayoutFile addLayoutFile(String fileName,
 			prepStmt.setByte(17, FileKnown.UNKNOWN.getFileKnownValue()); // Known
 			prepStmt.setNull(18, java.sql.Types.VARCHAR); // MIME type	
 			prepStmt.setString(19, parentPath); // parent path
-			prepStmt.setLong(20, parent.getDataSource().getId()); // data_source_obj_id
+			prepStmt.setLong(20, TskData.FileLocation.LOCAL.getValue());  // location
+			prepStmt.setLong(21, parent.getDataSource().getId()); // data_source_obj_id
 
-			prepStmt.setString(21, extractExtension(fileName)); 				//extension
+			prepStmt.setString(22, extractExtension(fileName)); 				//extension
 			connection.executeUpdate(prepStmt);
 
 			/*
@@ -7270,7 +7406,7 @@ public LayoutFile addLayoutFile(String fileName,
 					ctime, crtime, atime, mtime,
 					null, null,
 					FileKnown.UNKNOWN,
-					parentPath,
+					parentPath, TskData.FileLocation.LOCAL,
 					null);
 
 			transaction.commit();
@@ -8570,7 +8706,8 @@ private List<AbstractFile> resultSetToAbstractFiles(ResultSet rs, CaseDbConnecti
 							TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), rs.getShort("meta_flags"), //NON-NLS
 							rs.getLong("size"), //NON-NLS
 							rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
-							rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), parentPath, rs.getString("mime_type")); //NON-NLS
+							rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), parentPath, //NON-NLS
+							TskData.FileLocation.valueOf(rs.getByte("location")), rs.getString("mime_type")); //NON-NLS
 					results.add(lf);
 				} else if (type == TSK_DB_FILES_TYPE_ENUM.DERIVED.getFileType()) {
 					final DerivedFile df;
@@ -8616,7 +8753,8 @@ org.sleuthkit.datamodel.File file(ResultSet rs, FileSystem fs) throws SQLExcepti
 				rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
 				(short) rs.getInt("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS
 				rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
-				rs.getString("parent_path"), rs.getString("mime_type"), rs.getString("extension")); //NON-NLS
+				rs.getString("parent_path"), TskData.FileLocation.valueOf(rs.getByte("location")), //NON-NLS
+				rs.getString("mime_type"), rs.getString("extension")); //NON-NLS
 		f.setFileSystem(fs);
 		return f;
 	}
@@ -8643,7 +8781,7 @@ Directory directory(ResultSet rs, FileSystem fs) throws SQLException {
 				rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
 				rs.getShort("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS
 				rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
-				rs.getString("parent_path")); //NON-NLS
+				rs.getString("parent_path"), TskData.FileLocation.valueOf(rs.getByte("location"))); //NON-NLS
 		dir.setFileSystem(fs);
 		return dir;
 	}
@@ -8701,7 +8839,8 @@ VirtualDirectory virtualDirectory(ResultSet rs, CaseDbConnection connection) thr
 					rs.getString("md5"),
 					rs.getString("sha256"),
 					FileKnown.valueOf(rs.getByte("known")),
-					parentPath);
+					parentPath,
+					TskData.FileLocation.valueOf(rs.getByte("location")));
 		} else {
 			final VirtualDirectory vd = new VirtualDirectory(this,
 					objId, dsObjId,
@@ -8710,7 +8849,8 @@ VirtualDirectory virtualDirectory(ResultSet rs, CaseDbConnection connection) thr
 					TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS
 					TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS
 					rs.getShort("meta_flags"), rs.getString("md5"), rs.getString("sha256"), //NON-NLS
-					FileKnown.valueOf(rs.getByte("known")), parentPath); //NON-NLS
+					FileKnown.valueOf(rs.getByte("known")), parentPath, //NON-NLS
+					TskData.FileLocation.valueOf(rs.getByte("location"))); //NON-NLS
 			return vd;
 		}
 	}
@@ -8735,7 +8875,7 @@ LocalDirectory localDirectory(ResultSet rs) throws SQLException {
 				TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS
 				TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS
 				rs.getShort("meta_flags"), rs.getString("md5"), rs.getString("sha256"), //NON-NLS
-				FileKnown.valueOf(rs.getByte("known")), parentPath); //NON-NLS
+				FileKnown.valueOf(rs.getByte("known")), parentPath, TskData.FileLocation.valueOf(rs.getByte("location"))); //NON-NLS
 		return ld;
 	}
 
@@ -8788,7 +8928,7 @@ private DerivedFile derivedFile(ResultSet rs, CaseDbConnection connection, long
 				rs.getLong("size"), //NON-NLS
 				rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
 				rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
-				parentPath, localPath, parentId, rs.getString("mime_type"),
+				parentPath, localPath, TskData.FileLocation.valueOf(rs.getByte("location")), parentId, rs.getString("mime_type"),
 				encodingType, rs.getString("extension"));
 		return df;
 	}
@@ -8841,7 +8981,7 @@ private LocalFile localFile(ResultSet rs, CaseDbConnection connection, long pare
 				rs.getLong("size"), //NON-NLS
 				rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
 				rs.getString("mime_type"), rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
-				parentId, parentPath, rs.getLong("data_source_obj_id"),
+				parentId, parentPath, TskData.FileLocation.valueOf(rs.getByte("location")), rs.getLong("data_source_obj_id"),
 				localPath, encodingType, rs.getString("extension"));
 		return file;
 	}
@@ -8869,7 +9009,8 @@ org.sleuthkit.datamodel.SlackFile slackFile(ResultSet rs, FileSystem fs) throws
 				rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
 				(short) rs.getInt("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS
 				rs.getString("md5"), rs.getString("sha256"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
-				rs.getString("parent_path"), rs.getString("mime_type"), rs.getString("extension")); //NON-NLS
+				rs.getString("parent_path"), TskData.FileLocation.valueOf(rs.getByte("location")), //NON-NLS
+				rs.getString("mime_type"), rs.getString("extension")); //NON-NLS
 		f.setFileSystem(fs);
 		return f;
 	}
@@ -8931,7 +9072,8 @@ List<Content> fileChildren(ResultSet rs, CaseDbConnection connection, long paren
 								rs.getLong("size"),
 								rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"),
 								rs.getString("md5"), rs.getString("sha256"),
-								FileKnown.valueOf(rs.getByte("known")), parentPath, rs.getString("mime_type"));
+								FileKnown.valueOf(rs.getByte("known")), parentPath, 
+								TskData.FileLocation.valueOf(rs.getByte("location")), rs.getString("mime_type"));
 						children.add(lf);
 						break;
 					}
@@ -11111,6 +11253,30 @@ private List<IngestModuleInfo> getIngestModules(int ingestJobId, CaseDbConnectio
 
 		}
 	}
+	
+	/**
+	 * Check whether any files in the case are stored in the repository.
+	 * 
+	 * @return true if any files have the location field set to "REPOSITORY", false otherwise.
+	 * 
+	 * @throws TskCoreException 
+	 */
+	public boolean caseUsesFileRepository() throws TskCoreException {
+		acquireSingleUserCaseReadLock();
+		try (CaseDbConnection connection = getConnection();
+			Statement statement = connection.createStatement();
+			ResultSet rs = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM tsk_files WHERE location=" + TskData.FileLocation.REPOSITORY.getValue());) {
+			int count = 0;
+			if (rs.next()) {
+				count = rs.getInt("count");
+			}
+			return count > 0;
+		} catch (SQLException ex) {
+			throw new TskCoreException("Error querying case database for files stored in repository", ex);
+		} finally {
+			releaseSingleUserCaseReadLock();
+		}
+	}
 
 	/**
 	 * Stores a pair of object ID and its type
@@ -11203,10 +11369,10 @@ private enum PREPARED_STATEMENT {
 		SELECT_FILE_DERIVATION_METHOD("SELECT tool_name, tool_version, other FROM tsk_files_derived_method WHERE derived_id = ?"), //NON-NLS
 		SELECT_MAX_OBJECT_ID("SELECT MAX(obj_id) AS max_obj_id FROM tsk_objects"), //NON-NLS
 		INSERT_OBJECT("INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)"), //NON-NLS
-		INSERT_FILE("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, md5, sha256, known, mime_type, parent_path, data_source_obj_id,extension) " //NON-NLS
-				+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), //NON-NLS
-		INSERT_FILE_SYSTEM_FILE("INSERT INTO tsk_files(obj_id, fs_obj_id, data_source_obj_id, attr_type, attr_id, name, meta_addr, meta_seq, type, has_path, dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime, atime, mtime, parent_path, extension)"
-				+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), // NON-NLS
+		INSERT_FILE("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, md5, sha256, known, mime_type, parent_path, location, data_source_obj_id,extension) " //NON-NLS
+				+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), //NON-NLS
+		INSERT_FILE_SYSTEM_FILE("INSERT INTO tsk_files(obj_id, fs_obj_id, data_source_obj_id, attr_type, attr_id, name, meta_addr, meta_seq, type, has_path, dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, sha256, known, mime_type, parent_path, location, extension)"
+				+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), // NON-NLS
 		UPDATE_DERIVED_FILE("UPDATE tsk_files SET type = ?, dir_type = ?, meta_type = ?, dir_flags = ?,  meta_flags = ?, size= ?, ctime= ?, crtime= ?, atime= ?, mtime= ?, mime_type = ?  "
 				+ "WHERE obj_id = ?"), //NON-NLS
 		INSERT_LAYOUT_FILE("INSERT INTO tsk_file_layout (obj_id, byte_start, byte_len, sequence) " //NON-NLS
@@ -12653,7 +12819,7 @@ public LocalFile addLocalFile(String fileName, String localPath,
 			Content parent, CaseDbTransaction transaction) throws TskCoreException {	
 		
 		return addLocalFile(fileName, localPath, size, ctime, crtime, atime, mtime,
-				md5, null, known, mimeType, isFile, encodingType,
+				md5, null, known, mimeType, TskData.FileLocation.LOCAL, isFile, encodingType,
 				parent, transaction);
 	}
 
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SpecialDirectory.java b/bindings/java/src/org/sleuthkit/datamodel/SpecialDirectory.java
index 5db5888d5b34d8a0ebbd926d18b1fa52559c2512..573ec25d756605f6fb6d5d064de308897af553ba 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SpecialDirectory.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SpecialDirectory.java
@@ -44,11 +44,11 @@ public abstract class SpecialDirectory extends AbstractFile {
 			short modes,
 			int uid, int gid,
 			String md5Hash, String sha256Hash, FileKnown knownState,
-			String parentPath,
+			String parentPath, TskData.FileLocation location,
 			String mimeType) {
 		super(db, objId, dataSourceObjectId, attrType, attrId, name,
 				fileType, metaAddr, metaSeq, dirType, metaType, dirFlag,
-				metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, null);
+				metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, location, mimeType, null);
 	}
 
 	/**
diff --git a/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java b/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java
index c66755ac36cabafe5c6a2ba38df60417ecc73adc..d91aea2ed1cba28c862ef19b84fb9bcaf76b428a 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java
@@ -789,8 +789,8 @@ private long addFileToDb(long parentObjId,
 			// INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
 			long objectId = caseDb.addObject(parentObjId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection);
 			
-			String fileInsert = "INSERT INTO tsk_files (fs_obj_id, obj_id, data_source_obj_id, type, attr_type, attr_id, name, meta_addr, meta_seq, dir_type, meta_type, dir_flags, meta_flags, size, crtime, ctime, atime, mtime, mode, gid, uid, md5, known, parent_path, extension, has_layout)"
-				+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; // NON-NLS
+			String fileInsert = "INSERT INTO tsk_files (fs_obj_id, obj_id, data_source_obj_id, type, attr_type, attr_id, name, meta_addr, meta_seq, dir_type, meta_type, dir_flags, meta_flags, size, crtime, ctime, atime, mtime, mode, gid, uid, md5, known, parent_path, location, extension, has_layout)"
+				+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; // NON-NLS
 			PreparedStatement preparedStatement = connection.getPreparedStatement(fileInsert, Statement.NO_GENERATED_KEYS);			
 			preparedStatement.clearParameters();
 			
@@ -866,11 +866,12 @@ private long addFileToDb(long parentObjId,
 			preparedStatement.setString(22, md5);                   // md5
 			preparedStatement.setInt(23, known.getFileKnownValue());// known
 			preparedStatement.setString(24, escaped_path);          // parent_path
-			preparedStatement.setString(25, extension);             // extension
+			preparedStatement.setLong(25, TskData.FileLocation.LOCAL.getValue());  // location
+			preparedStatement.setString(26, extension);             // extension
 			if (hasLayout) {
-				preparedStatement.setInt(26, 1);                    // has_layout
+				preparedStatement.setInt(27, 1);                    // has_layout
 			} else {
-				preparedStatement.setNull(26, java.sql.Types.INTEGER);
+				preparedStatement.setNull(27, java.sql.Types.INTEGER);
 			}
 			connection.executeUpdate(preparedStatement);
 
@@ -884,7 +885,7 @@ private long addFileToDb(long parentObjId,
 						TskData.TSK_FS_META_TYPE_ENUM.valueOf((short) metaType),
 						TskData.TSK_FS_NAME_FLAG_ENUM.valueOf(dirFlags),
 						(short) metaFlags,
-						size, ctime, crtime, atime, mtime, null, null, null, escaped_path, null, parentObjId, null, null, extension);
+						size, ctime, crtime, atime, mtime, null, null, null, escaped_path, null, TskData.FileLocation.LOCAL, parentObjId, null, null, extension);
 
 				timelineManager.addEventsForNewFileQuiet(derivedFile, connection);
 			}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/TskData.java b/bindings/java/src/org/sleuthkit/datamodel/TskData.java
index 6ff21a0a03cb007cc49d3d6fe5fa3579307ce8c2..ca07b7b650ae46ec5abfbb16fe082809cec3e406 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/TskData.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/TskData.java
@@ -878,4 +878,31 @@ public static EncodingType valueOf(int type) {
 					MessageFormat.format(bundle.getString("TskData.encodingType.exception.msg1.text"), type));
 		}
 	}
+	
+	/**
+	 * Location of the data for an AbstractFile.
+	 */
+	public enum FileLocation{
+		LOCAL(0),
+		REPOSITORY(1);
+		
+		private final int value;
+		
+		private FileLocation(int value){
+			this.value = value;
+		}
+		
+		public int getValue(){
+			return value;
+		}
+		
+		public static FileLocation valueOf(int value) {
+			for (FileLocation v : FileLocation.values()) {
+				if (v.value == value) {
+					return v;
+				}
+			}
+			throw new IllegalArgumentException(MessageFormat.format(bundle.getString("TskData.fileLocation.exception.msg1.text"), value));
+		}
+	}
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/VirtualDirectory.java b/bindings/java/src/org/sleuthkit/datamodel/VirtualDirectory.java
index 729707128e93f097312caca5a16222c30927943b..7fac11eb5a923ed83c305ce53f9752f9fa2ccb6c 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/VirtualDirectory.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/VirtualDirectory.java
@@ -63,10 +63,12 @@ public class VirtualDirectory extends SpecialDirectory {
 	 *                           directory.
 	 * @param metaFlags          The meta flags for the virtual directory.
 	 * @param md5Hash            The MD5 hash for the virtual directory.
+	 * @param sha256Hash         The SHA-256 hash for the virtual directory.
 	 * @param knownState         The known state for the virtual directory
 	 * @param parentPath         The parent path for the virtual directory,
 	 *                           should be "/" if the virtual directory is a
 	 *                           data source.
+	 * @param location           The location of the file.
 	 */
 	VirtualDirectory(SleuthkitCase db,
 			long objId,
@@ -75,10 +77,10 @@ public class VirtualDirectory extends SpecialDirectory {
 			TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
 			TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
 			String md5Hash, String sha256Hash, FileKnown knownState,
-			String parentPath) {
+			String parentPath, TskData.FileLocation location) {
 		super(db, objId, dataSourceObjectId, TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0, name,
 				TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR, 0L, 0, dirType, metaType, dirFlag,
-				metaFlags, 0L, 0L, 0L, 0L, 0L, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, null);
+				metaFlags, 0L, 0L, 0L, 0L, 0L, (short) 0, 0, 0, md5Hash, sha256Hash, knownState, parentPath, location, null);
 	}
 
 	/**