diff --git a/include/clang/Basic/FileManager.h b/include/clang/Basic/FileManager.h
index 5f22481..d68936a 100644
--- a/include/clang/Basic/FileManager.h
+++ b/include/clang/Basic/FileManager.h
@@ -17,6 +17,7 @@
 
 #include "clang/Basic/FileSystemOptions.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/VirtualFileSystem.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/OwningPtr.h"
@@ -24,7 +25,6 @@
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Allocator.h"
-#include "llvm/Support/FileSystem.h"
 // FIXME: Enhance libsystem to support inode and other fields in stat.
 #include <sys/types.h>
 
@@ -119,6 +119,7 @@ struct FileData;
 /// as a single file.
 ///
 class FileManager : public RefCountedBase<FileManager> {
+  IntrusiveRefCntPtr<AbstractFileSystem> FS;
   FileSystemOptions FileSystemOpts;
 
   class UniqueDirContainer;
@@ -179,7 +180,8 @@ class FileManager : public RefCountedBase<FileManager> {
   void addAncestorsAsVirtualDirs(StringRef Path);
 
 public:
-  FileManager(const FileSystemOptions &FileSystemOpts);
+  FileManager(const FileSystemOptions &FileSystemOpts,
+              llvm::IntrusiveRefCntPtr<AbstractFileSystem> FS = 0);
   ~FileManager();
 
   /// \brief Installs the provided FileSystemStatCache object within
@@ -248,7 +250,7 @@ public:
   ///
   /// \returns false on success, true on error.
   bool getNoncachedStatValue(StringRef Path,
-                             llvm::sys::fs::file_status &Result);
+                             AbstractFileSystem::Status &Result);
 
   /// \brief Remove the real file \p Entry from the cache.
   void invalidateCache(const FileEntry *Entry);
diff --git a/include/clang/Basic/FileSystemStatCache.h b/include/clang/Basic/FileSystemStatCache.h
index 23d8256..61dbe6a 100644
--- a/include/clang/Basic/FileSystemStatCache.h
+++ b/include/clang/Basic/FileSystemStatCache.h
@@ -24,6 +24,8 @@
 
 namespace clang {
 
+class AbstractFileSystem;
+
 struct FileData {
   uint64_t Size;
   time_t ModTime;
@@ -60,7 +62,8 @@ public:
   /// implementation can optionally fill in FileDescriptor with a valid
   /// descriptor and the client guarantees that it will close it.
   static bool get(const char *Path, FileData &Data, bool isFile,
-                  int *FileDescriptor, FileSystemStatCache *Cache);
+                  int *FileDescriptor, FileSystemStatCache *Cache,
+                  AbstractFileSystem &FS);
 
   /// \brief Sets the next stat call cache in the chain of stat caches.
   /// Takes ownership of the given stat cache.
@@ -78,17 +81,17 @@ public:
   
 protected:
   virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                               int *FileDescriptor) = 0;
+                               int *FileDescriptor, AbstractFileSystem &FS) = 0;
 
   LookupResult statChained(const char *Path, FileData &Data, bool isFile,
-                           int *FileDescriptor) {
+                           int *FileDescriptor, AbstractFileSystem &FS) {
     if (FileSystemStatCache *Next = getNextStatCache())
-      return Next->getStat(Path, Data, isFile, FileDescriptor);
+      return Next->getStat(Path, Data, isFile, FileDescriptor, FS);
 
     // If we hit the end of the list of stat caches to try, just compute and
     // return it without a cache.
-    return get(Path, Data, isFile, FileDescriptor, 0) ? CacheMissing
-                                                      : CacheExists;
+    return get(Path, Data, isFile, FileDescriptor, 0, FS) ? CacheMissing
+                                                          : CacheExists;
   }
 };
 
@@ -107,7 +110,7 @@ public:
   iterator end() const { return StatCalls.end(); }
 
   virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                               int *FileDescriptor);
+                               int *FileDescriptor, AbstractFileSystem &FS);
 };
 
 } // end namespace clang
diff --git a/include/clang/Basic/VirtualFileSystem.h b/include/clang/Basic/VirtualFileSystem.h
new file mode 100644
index 0000000..6fb0d40
--- /dev/null
+++ b/include/clang/Basic/VirtualFileSystem.h
@@ -0,0 +1,227 @@
+//===- VirtualFileSystem.h - Virtual File System Layer ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// \brief Defines the virtual file system interface AbstractFileSystem.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
+#define LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
+
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/FileSystem.h"
+
+namespace llvm {
+template <typename T> class OwningPtr;
+class MemoryBuffer;
+}
+
+namespace clang {
+
+/// \brief The virtual file system interface, modeled on llvm::sys::fs.
+class AbstractFileSystem : public llvm::RefCountedBase<AbstractFileSystem> {
+public:
+  /// \brief The result of a \p status operation.
+  ///
+  /// In addition to providing the usual stat information, this class provides
+  /// \p Name and \p RealName fields, which can be used by clients that need
+  /// to differentiate between the name that this object was looked up by,
+  /// and the real name of this object on disk (if there is one).
+  class Status {
+    std::string Name;
+    std::string RealName;
+    llvm::sys::fs::UniqueID UID;
+    llvm::sys::TimeValue MTime;
+    uint32_t User;
+    uint32_t Group;
+    uint64_t Size;
+    llvm::sys::fs::file_type Type;
+    llvm::sys::fs::perms Perms;
+
+  public:
+    Status() : Type(llvm::sys::fs::file_type::status_error) {}
+    Status(const llvm::sys::fs::file_status &Status);
+    Status(llvm::StringRef Name, llvm::StringRef RealName,
+           llvm::sys::fs::UniqueID UID, llvm::sys::TimeValue MTime,
+           uint32_t User, uint32_t Group, uint64_t Size,
+           llvm::sys::fs::file_type Type, llvm::sys::fs::perms Perms);
+
+    /// \brief Returns the name this status was looked up by.
+    llvm::StringRef getName() const { return Name; }
+    /// \brief Returns the real name of this file/directory on disk, if
+    /// there is one.
+    llvm::StringRef getRealName() const { return RealName; }
+
+    void setName(llvm::StringRef N) { Name = N; }
+    void setRealName(llvm::StringRef N) { RealName = N; }
+
+    /// @name Status interface from llvm::sys::fs
+    /// @{
+    llvm::sys::fs::file_type type() const { return Type; }
+    llvm::sys::fs::perms permissions() const { return Perms; }
+    llvm::sys::TimeValue getLastModificationTime() const { return MTime; }
+    llvm::sys::fs::UniqueID getUniqueID() const { return UID; }
+    uint32_t getUser() const { return User; }
+    uint32_t getGroup() const { return Group; }
+    uint64_t getSize() const { return Size; }
+    void type(llvm::sys::fs::file_type v) { Type = v; }
+    void permissions(llvm::sys::fs::perms p) { Perms = p; }
+    /// @}
+  };
+
+  /// \brief A type representing an open file.
+  /// FIXME: right now we only support real files, so this is just an alias for
+  /// a raw operating system file descriptor.
+  typedef int FileDescriptor;
+
+  /// @name Status queries
+  /// @{
+  static bool equivalent(const Status &A, const Status &B);
+  static bool is_directory(const Status &Status);
+  static bool is_regular_file(const Status &Status);
+  static bool is_other(const Status &Status);
+  static bool is_symlink(const Status &Status);
+  static bool status_known(const Status &Status);
+  static bool exists(const Status &Status);
+  /// }
+
+  virtual ~AbstractFileSystem();
+
+  /// @name Convenience methods
+  /// @{
+  /// Convencience method for users that don't need to differentiate between
+  /// errors and a negative result.
+  bool exists(const llvm::Twine &Path) {
+    bool Result;
+    return !exists(Path, Result) && Result;
+  }
+  bool equivalent(const llvm::Twine &A, const llvm::Twine &B) {
+    bool Result;
+    return !equivalent(A, B, Result) && Result;
+  }
+  bool is_directory(const llvm::Twine &Path) {
+    bool Result;
+    return !is_directory(Path, Result) && Result;
+  }
+  bool is_regular_file(const llvm::Twine &Path) {
+    bool Result;
+    return !is_regular_file(Path, Result) && Result;
+  }
+  bool is_other(const llvm::Twine &Path) {
+    bool Result;
+    return !is_other(Path, Result) && Result;
+  }
+  bool is_symlink(const llvm::Twine &Path) {
+    bool Result;
+    return !is_symlink(Path, Result) && Result;
+  }
+  bool status_known(const llvm::Twine &Path) {
+    bool Result;
+    return !status_known(Path, Result) && Result;
+  }
+  /// @}
+  /// @name Operations on open files
+  /// This is the public interface. \see{Open file interface} for the interface
+  /// for subclasses.
+  /// @{
+  llvm::error_code status(FileDescriptor FD, Status &Result);
+  llvm::error_code getBufferForFile(FileDescriptor FD, const llvm::Twine &Name,
+                                    llvm::OwningPtr<llvm::MemoryBuffer> &Result,
+                                    int64_t FileSize = -1,
+                                    bool RequiresNullTerminator = true);
+  /// @}
+  /// @name Virtual file system interface
+  /// @{
+  virtual llvm::error_code status(const llvm::Twine &Path, Status &Result) = 0;
+  virtual llvm::error_code openFileForRead(const llvm::Twine &Path,
+                                           FileDescriptor &ResultFD) = 0;
+  virtual llvm::error_code getBufferForFile(
+      const llvm::Twine &Name, llvm::OwningPtr<llvm::MemoryBuffer> &Result,
+      int64_t FileSize = -1, bool RequiresNullTerminator = true) = 0;
+  /// @}
+  /// @name Operations implmented on top of \p status
+  /// @{
+
+  llvm::error_code exists(const llvm::Twine &Path, bool &Result);
+  llvm::error_code equivalent(const llvm::Twine &A, const llvm::Twine &B,
+                              bool &Result);
+
+  llvm::error_code is_directory(const llvm::Twine &Path, bool &Result);
+  llvm::error_code is_regular_file(const llvm::Twine &Path, bool &Result);
+  llvm::error_code is_other(const llvm::Twine &Path, bool &Result);
+  llvm::error_code is_symlink(const llvm::Twine &Path, bool &Result);
+  llvm::error_code file_size(const llvm::Twine &Path, uint64_t &Result);
+  llvm::error_code status_known(const llvm::Twine &Path, bool &Result);
+  llvm::error_code getUniqueID(const llvm::Twine &Path,
+                               llvm::sys::fs::UniqueID &Result);
+  /// @}
+
+protected:
+  /// @name Open file interface
+  /// These methods are intended to be implemented by file systems that provide
+  /// a source of openable files. FIXME: right now only \p RealFileSystem does
+  /// this.
+  /// @{
+
+  /// \brief Get the \p Status of an open file.
+  virtual llvm::error_code statusOfOpenFile(FileDescriptor FD, Status &Result);
+
+  /// \brief Get a \p MemoryBuffer for an open file.
+  virtual llvm::error_code
+  getBufferForOpenFile(FileDescriptor FD, const llvm::Twine &Name,
+                       llvm::OwningPtr<llvm::MemoryBuffer> &Result,
+                       int64_t FileSize = -1,
+                       bool RequiresNullTerminator = true);
+  /// @}
+};
+
+/// \brief A file system that allows overlaying one \p AbstractFileSystem on top
+/// of another.
+///
+/// Consists of a stack of >=1 \p AbstractFileSytem objects, which are treated
+/// as being one merged file system. When there is a directory that exists
+/// in more than one file system, the \p OverlayFileSystem contains a directory
+/// containing the union of their contents.  When there is a file that exists in
+/// more that one file system, the file in the top-most (most recently added)
+/// file system overrides the other(s).
+class OverlayFileSystem : public AbstractFileSystem {
+  typedef llvm::SmallVector<llvm::IntrusiveRefCntPtr<AbstractFileSystem>, 1>
+    FileSystemList;
+  typedef FileSystemList::reverse_iterator iterator;
+
+  /// \brief The stack of file systems, implemented as a list in order of
+  /// their addition.
+  FileSystemList FSList;
+
+  /// \brief Get an iterator pointing to the most recently added file system.
+  iterator begin() { return FSList.rbegin(); }
+
+  /// \brief Get an iterator pointing one-past the least recently added file
+  /// system.
+  iterator end() { return FSList.rend(); }
+
+public:
+  OverlayFileSystem(llvm::IntrusiveRefCntPtr<AbstractFileSystem> Base);
+  /// \brief Pushes a file system on top of the stack.
+  void pushOverlay(llvm::IntrusiveRefCntPtr<AbstractFileSystem> FS);
+
+  llvm::error_code status(const llvm::Twine &Path, Status &Result);
+  llvm::error_code openFileForRead(const llvm::Twine &Path,
+                                   FileDescriptor &ResultFD);
+  llvm::error_code getBufferForFile(const llvm::Twine &Name,
+                                    llvm::OwningPtr<llvm::MemoryBuffer> &Result,
+                                    int64_t FileSize = -1,
+                                    bool RequiresNullTerminator = true);
+};
+
+/// \brief Gets an \p AbstractFileSystem for the 'real' file system, as seen by
+/// the operating system.
+llvm::IntrusiveRefCntPtr<AbstractFileSystem> getRealFileSystem();
+
+} // end namespace clang
+#endif // LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h
index fa20ae4..45d4492 100644
--- a/include/clang/Frontend/CompilerInstance.h
+++ b/include/clang/Frontend/CompilerInstance.h
@@ -75,6 +75,9 @@ class CompilerInstance : public ModuleLoader {
   /// The target being compiled for.
   IntrusiveRefCntPtr<TargetInfo> Target;
 
+  /// The virtual file system.
+  IntrusiveRefCntPtr<OverlayFileSystem> VirtualFileSystem;
+
   /// The file manager.
   IntrusiveRefCntPtr<FileManager> FileMgr;
 
@@ -314,6 +317,23 @@ public:
   void setTarget(TargetInfo *Value);
 
   /// }
+  /// @name Virtual File System
+  /// {
+
+  bool hasVirtualFileSystem() const { return VirtualFileSystem != 0; }
+
+  OverlayFileSystem &getVirtualFileSystem() const {
+    assert(hasVirtualFileSystem() &&
+           "Compiler instance has no virtual file system");
+    return *VirtualFileSystem;
+  }
+
+  /// \brief Replace the current virtual file system.
+  void setVirtualFileSystem(OverlayFileSystem *FS) {
+    VirtualFileSystem = FS;
+  }
+
+  /// }
   /// @name File Manager
   /// {
 
@@ -527,6 +547,11 @@ public:
                     bool ShouldOwnClient = true,
                     const CodeGenOptions *CodeGenOpts = 0);
 
+  /// Create the virtual file system and replace any existing one with it.
+  /// The top level of the virtual file system is an OverlayFileSystem with the
+  /// RealFileSystem as its base and initially no overlays.
+  void createVirtualFileSystem();
+
   /// Create the file manager and replace any existing one with it.
   void createFileManager();
 
diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt
index 43622eb..2efe812 100644
--- a/lib/Basic/CMakeLists.txt
+++ b/lib/Basic/CMakeLists.txt
@@ -23,6 +23,7 @@ add_clang_library(clangBasic
   TokenKinds.cpp
   Version.cpp
   VersionTuple.cpp
+  VirtualFileSystem.cpp
   )
 
 # Determine Subversion revision.
diff --git a/lib/Basic/FileManager.cpp b/lib/Basic/FileManager.cpp
index af9b266..96d3a6b 100644
--- a/lib/Basic/FileManager.cpp
+++ b/lib/Basic/FileManager.cpp
@@ -101,13 +101,19 @@ public:
 // Common logic.
 //===----------------------------------------------------------------------===//
 
-FileManager::FileManager(const FileSystemOptions &FSO)
-  : FileSystemOpts(FSO),
+FileManager::FileManager(const FileSystemOptions &FSO,
+                         IntrusiveRefCntPtr<AbstractFileSystem> FS)
+  : FS(FS), FileSystemOpts(FSO),
     UniqueRealDirs(*new UniqueDirContainer()),
     UniqueRealFiles(*new UniqueFileContainer()),
     SeenDirEntries(64), SeenFileEntries(64), NextFileUID(0) {
   NumDirLookups = NumFileLookups = 0;
   NumDirCacheMisses = NumFileCacheMisses = 0;
+
+  // If the caller doesn't provide a virtual file system, just grab the real
+  // file system.
+  if (!FS)
+    this->FS = getRealFileSystem();
 }
 
 FileManager::~FileManager() {
@@ -445,7 +451,7 @@ getBufferForFile(const FileEntry *Entry, std::string *ErrorStr,
   const char *Filename = Entry->getName();
   // If the file is already open, use the open file descriptor.
   if (Entry->FD != -1) {
-    ec = llvm::MemoryBuffer::getOpenFile(Entry->FD, Filename, Result, FileSize);
+    ec = FS->getBufferForFile(Entry->FD, Filename, Result, FileSize);
     if (ErrorStr)
       *ErrorStr = ec.message();
 
@@ -457,7 +463,7 @@ getBufferForFile(const FileEntry *Entry, std::string *ErrorStr,
   // Otherwise, open the file.
 
   if (FileSystemOpts.WorkingDir.empty()) {
-    ec = llvm::MemoryBuffer::getFile(Filename, Result, FileSize);
+    ec = FS->getBufferForFile(Filename, Result, FileSize);
     if (ec && ErrorStr)
       *ErrorStr = ec.message();
     return Result.take();
@@ -465,7 +471,7 @@ getBufferForFile(const FileEntry *Entry, std::string *ErrorStr,
 
   SmallString<128> FilePath(Entry->getName());
   FixupRelativePath(FilePath);
-  ec = llvm::MemoryBuffer::getFile(FilePath.str(), Result, FileSize);
+  ec = FS->getBufferForFile(FilePath.str(), Result, FileSize);
   if (ec && ErrorStr)
     *ErrorStr = ec.message();
   return Result.take();
@@ -476,7 +482,7 @@ getBufferForFile(StringRef Filename, std::string *ErrorStr) {
   OwningPtr<llvm::MemoryBuffer> Result;
   llvm::error_code ec;
   if (FileSystemOpts.WorkingDir.empty()) {
-    ec = llvm::MemoryBuffer::getFile(Filename, Result);
+    ec = FS->getBufferForFile(Filename, Result);
     if (ec && ErrorStr)
       *ErrorStr = ec.message();
     return Result.take();
@@ -484,7 +490,7 @@ getBufferForFile(StringRef Filename, std::string *ErrorStr) {
 
   SmallString<128> FilePath(Filename);
   FixupRelativePath(FilePath);
-  ec = llvm::MemoryBuffer::getFile(FilePath.c_str(), Result);
+  ec = FS->getBufferForFile(FilePath.c_str(), Result);
   if (ec && ErrorStr)
     *ErrorStr = ec.message();
   return Result.take();
@@ -501,21 +507,21 @@ bool FileManager::getStatValue(const char *Path, FileData &Data, bool isFile,
   // absolute!
   if (FileSystemOpts.WorkingDir.empty())
     return FileSystemStatCache::get(Path, Data, isFile, FileDescriptor,
-                                    StatCache.get());
+                                    StatCache.get(), *FS);
 
   SmallString<128> FilePath(Path);
   FixupRelativePath(FilePath);
 
   return FileSystemStatCache::get(FilePath.c_str(), Data, isFile,
-                                  FileDescriptor, StatCache.get());
+                                  FileDescriptor, StatCache.get(), *FS);
 }
 
 bool FileManager::getNoncachedStatValue(StringRef Path,
-                                        llvm::sys::fs::file_status &Result) {
+                                        AbstractFileSystem::Status &Result) {
   SmallString<128> FilePath(Path);
   FixupRelativePath(FilePath);
 
-  return llvm::sys::fs::status(FilePath.c_str(), Result);
+  return FS->status(FilePath.c_str(), Result);
 }
 
 void FileManager::invalidateCache(const FileEntry *Entry) {
diff --git a/lib/Basic/FileSystemStatCache.cpp b/lib/Basic/FileSystemStatCache.cpp
index 7a01bff..cbe2f17 100644
--- a/lib/Basic/FileSystemStatCache.cpp
+++ b/lib/Basic/FileSystemStatCache.cpp
@@ -12,7 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Basic/FileSystemStatCache.h"
-#include "llvm/Support/FileSystem.h"
+#include "clang/Basic/VirtualFileSystem.h"
 #include "llvm/Support/Path.h"
 
 // FIXME: This is terrible, we need this for ::close.
@@ -30,12 +30,12 @@ using namespace clang;
 
 void FileSystemStatCache::anchor() { }
 
-static void copyStatusToFileData(const llvm::sys::fs::file_status &Status,
+static void copyStatusToFileData(const AbstractFileSystem::Status &Status,
                                  FileData &Data) {
   Data.Size = Status.getSize();
   Data.ModTime = Status.getLastModificationTime().toEpochTime();
   Data.UniqueID = Status.getUniqueID();
-  Data.IsDirectory = is_directory(Status);
+  Data.IsDirectory = AbstractFileSystem::is_directory(Status);
   Data.IsNamedPipe = Status.type() == llvm::sys::fs::file_type::fifo_file;
   Data.InPCH = false;
 }
@@ -50,18 +50,19 @@ static void copyStatusToFileData(const llvm::sys::fs::file_status &Status,
 /// implementation can optionally fill in FileDescriptor with a valid
 /// descriptor and the client guarantees that it will close it.
 bool FileSystemStatCache::get(const char *Path, FileData &Data, bool isFile,
-                              int *FileDescriptor, FileSystemStatCache *Cache) {
+                              int *FileDescriptor, FileSystemStatCache *Cache,
+                              AbstractFileSystem &FS) {
   LookupResult R;
   bool isForDir = !isFile;
 
   // If we have a cache, use it to resolve the stat query.
   if (Cache)
-    R = Cache->getStat(Path, Data, isFile, FileDescriptor);
+    R = Cache->getStat(Path, Data, isFile, FileDescriptor, FS);
   else if (isForDir || !FileDescriptor) {
     // If this is a directory or a file descriptor is not needed and we have
     // no cache, just go to the file system.
-    llvm::sys::fs::file_status Status;
-    if (llvm::sys::fs::status(Path, Status)) {
+    AbstractFileSystem::Status Status;
+    if (FS.status(Path, Status)) {
       R = CacheMissing;
     } else {
       R = CacheExists;
@@ -75,7 +76,7 @@ bool FileSystemStatCache::get(const char *Path, FileData &Data, bool isFile,
     //
     // Because of this, check to see if the file exists with 'open'.  If the
     // open succeeds, use fstat to get the stat info.
-    llvm::error_code EC = llvm::sys::fs::openFileForRead(Path, *FileDescriptor);
+    llvm::error_code EC = FS.openFileForRead(Path, *FileDescriptor);
 
     if (EC) {
       // If the open fails, our "stat" fails.
@@ -84,8 +85,8 @@ bool FileSystemStatCache::get(const char *Path, FileData &Data, bool isFile,
       // Otherwise, the open succeeded.  Do an fstat to get the information
       // about the file.  We'll end up returning the open file descriptor to the
       // client to do what they please with it.
-      llvm::sys::fs::file_status Status;
-      if (!llvm::sys::fs::status(*FileDescriptor, Status)) {
+      AbstractFileSystem::Status Status;
+      if (!FS.status(*FileDescriptor, Status)) {
         R = CacheExists;
         copyStatusToFileData(Status, Data);
       } else {
@@ -118,8 +119,8 @@ bool FileSystemStatCache::get(const char *Path, FileData &Data, bool isFile,
 
 MemorizeStatCalls::LookupResult
 MemorizeStatCalls::getStat(const char *Path, FileData &Data, bool isFile,
-                           int *FileDescriptor) {
-  LookupResult Result = statChained(Path, Data, isFile, FileDescriptor);
+                           int *FileDescriptor, AbstractFileSystem &FS) {
+  LookupResult Result = statChained(Path, Data, isFile, FileDescriptor, FS);
 
   // Do not cache failed stats, it is easy to construct common inconsistent
   // situations if we do, and they are not important for PCH performance (which
diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp
new file mode 100644
index 0000000..5163f79
--- /dev/null
+++ b/lib/Basic/VirtualFileSystem.cpp
@@ -0,0 +1,284 @@
+//===- VirtualFileSystem.h - Virtual File System Layer ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This file implements the VirtualFileSystem interface.
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/VirtualFileSystem.h"
+#include "llvm/ADT/OwningPtr.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/Path.h"
+
+using namespace clang;
+using namespace llvm;
+using llvm::sys::fs::file_status;
+using llvm::sys::fs::file_type;
+using llvm::sys::fs::perms;
+using llvm::sys::fs::UniqueID;
+
+AbstractFileSystem::Status::Status(const file_status &Status)
+    : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
+      User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
+      Type(Status.type()), Perms(Status.permissions()) {}
+
+AbstractFileSystem::Status::Status(StringRef Name, StringRef RealName,
+                                   UniqueID UID, sys::TimeValue MTime,
+                                   uint32_t User, uint32_t Group, uint64_t Size,
+                                   file_type Type, perms Perms)
+    : Name(Name), RealName(RealName), UID(UID), MTime(MTime), User(User),
+      Group(Group), Size(Size), Type(Type), Perms(Perms) {}
+
+bool AbstractFileSystem::equivalent(const AbstractFileSystem::Status &A,
+                                    const AbstractFileSystem::Status &B) {
+  return A.getUniqueID() == B.getUniqueID();
+}
+bool
+AbstractFileSystem::is_directory(const AbstractFileSystem::Status &Status) {
+  return Status.type() == file_type::directory_file;
+}
+bool
+AbstractFileSystem::is_regular_file(const AbstractFileSystem::Status &Status) {
+  return Status.type() == file_type::regular_file;
+}
+bool AbstractFileSystem::is_other(const AbstractFileSystem::Status &Status) {
+  return Status.type() == exists(Status) && !is_regular_file(Status) &&
+         !is_directory(Status) && !is_symlink(Status);
+}
+bool AbstractFileSystem::is_symlink(const AbstractFileSystem::Status &Status) {
+  return Status.type() == file_type::symlink_file;
+}
+bool
+AbstractFileSystem::status_known(const AbstractFileSystem::Status &Status) {
+  return Status.type() != file_type::status_error;
+}
+bool AbstractFileSystem::exists(const AbstractFileSystem::Status &Status) {
+  return status_known(Status) && Status.type() != file_type::file_not_found;
+}
+
+AbstractFileSystem::~AbstractFileSystem() {}
+
+error_code AbstractFileSystem::exists(const Twine &Path, bool &Result) {
+  Status S;
+  if (error_code EC = status(Path, S))
+    return EC;
+  Result = exists(S);
+  return error_code::success();
+}
+
+error_code AbstractFileSystem::equivalent(const Twine &A, const Twine &B,
+                                          bool &Result) {
+  Status SA, SB;
+  if (error_code EC = status(A, SA))
+    return EC;
+  if (error_code EC = status(B, SB))
+    return EC;
+  Result = equivalent(A, B);
+  return error_code::success();
+}
+error_code AbstractFileSystem::is_directory(const Twine &Path, bool &Result) {
+  Status S;
+  if (error_code EC = status(Path, S))
+    return EC;
+  Result = is_directory(S);
+  return error_code::success();
+}
+error_code AbstractFileSystem::is_regular_file(const Twine &Path,
+                                               bool &Result) {
+  Status S;
+  if (error_code EC = status(Path, S))
+    return EC;
+  Result = is_regular_file(S);
+  return error_code::success();
+}
+error_code AbstractFileSystem::is_other(const llvm::Twine &Path, bool &Result) {
+  Status S;
+  if (error_code EC = status(Path, S))
+    return EC;
+  Result = is_other(S);
+  return error_code::success();
+}
+error_code AbstractFileSystem::is_symlink(const llvm::Twine &Path,
+                                          bool &Result) {
+  Status S;
+  if (error_code EC = status(Path, S))
+    return EC;
+  Result = is_symlink(S);
+  return error_code::success();
+}
+error_code AbstractFileSystem::file_size(const Twine &Path, uint64_t &Result) {
+  Status S;
+  if (error_code EC = status(Path, S))
+    return EC;
+  Result = S.getSize();
+  return error_code::success();
+}
+error_code AbstractFileSystem::status_known(const Twine &Path, bool &Result) {
+  Status S;
+  if (error_code EC = status(Path, S))
+    return EC;
+  Result = status_known(S);
+  return error_code::success();
+}
+error_code AbstractFileSystem::getUniqueID(const Twine &Path,
+                                           UniqueID &Result) {
+  Status S;
+  if (error_code EC = status(Path, S))
+    return EC;
+  Result = S.getUniqueID();
+  return error_code::success();
+}
+
+error_code AbstractFileSystem::status(AbstractFileSystem::FileDescriptor FD,
+                                      AbstractFileSystem::Status &Result) {
+  // FIXME: when we support virtual files, use information from the FD to lookup
+  // which AbstractFileSystem to perform this operation on.
+  return getRealFileSystem()->statusOfOpenFile(FD, Result);
+}
+
+error_code AbstractFileSystem::getBufferForFile(
+    FileDescriptor FD, const llvm::Twine &Name,
+    llvm::OwningPtr<llvm::MemoryBuffer> &Result, int64_t FileSize,
+    bool RequiresNullTerminator) {
+  // FIXME: when we support virtual files, use information from the FD to lookup
+  // which AbstractFileSystem to perform this operation on.
+  return getRealFileSystem()->getBufferForOpenFile(FD, Name, Result, FileSize,
+                                                   RequiresNullTerminator);
+}
+
+error_code
+AbstractFileSystem::statusOfOpenFile(AbstractFileSystem::FileDescriptor FD,
+                                     AbstractFileSystem::Status &Result) {
+  llvm_unreachable("status for open file not provided");
+}
+
+error_code AbstractFileSystem::getBufferForOpenFile(
+    FileDescriptor FD, const llvm::Twine &Name,
+    llvm::OwningPtr<llvm::MemoryBuffer> &Result, int64_t FileSize,
+    bool RequiresNullTerminator) {
+  llvm_unreachable("get buffer for open file not provided");
+}
+
+//===-----------------------------------------------------------------------===/
+// RealFileSystem implementation
+//===-----------------------------------------------------------------------===/
+
+/// \brief The file system according to your operating system.
+class RealFileSystem : public AbstractFileSystem {
+public:
+  error_code status(const Twine &Path, Status &Result);
+  error_code statusOfOpenFile(FileDescriptor FD, Status &Result);
+  error_code openFileForRead(const Twine &Path, FileDescriptor &ResultFD);
+
+protected:
+  error_code getBufferForFile(const Twine &Name,
+                              OwningPtr<MemoryBuffer> &Result,
+                              int64_t FileSize = -1,
+                              bool RequiresNullTerminator = true);
+  error_code getBufferForOpenFile(FileDescriptor FD, const Twine &Name,
+                                  OwningPtr<MemoryBuffer> &Result,
+                                  int64_t FileSize = -1,
+                                  bool RequiresNullTerminator = true);
+};
+
+IntrusiveRefCntPtr<AbstractFileSystem> clang::getRealFileSystem() {
+  static IntrusiveRefCntPtr<AbstractFileSystem> FS = new RealFileSystem();
+  return FS;
+}
+
+error_code RealFileSystem::status(const Twine &Path,
+                                  AbstractFileSystem::Status &Result) {
+  sys::fs::file_status RealStatus;
+  error_code EC = sys::fs::status(Path, RealStatus);
+  Result = AbstractFileSystem::Status(RealStatus);
+  Result.setName(Path.str());
+  Result.setRealName(Path.str());
+  return EC;
+}
+
+error_code
+RealFileSystem::statusOfOpenFile(AbstractFileSystem::FileDescriptor FD,
+                                 AbstractFileSystem::Status &Result) {
+  sys::fs::file_status RealStatus;
+  error_code EC = sys::fs::status(FD, RealStatus);
+  Result = AbstractFileSystem::Status(RealStatus);
+  return EC;
+}
+
+error_code
+RealFileSystem::openFileForRead(const Twine &Name,
+                                AbstractFileSystem::FileDescriptor &ResultFD) {
+  return llvm::sys::fs::openFileForRead(Name, ResultFD);
+}
+
+error_code RealFileSystem::getBufferForFile(const Twine &Name,
+                                            OwningPtr<MemoryBuffer> &Result,
+                                            int64_t FileSize,
+                                            bool RequiresNullTerminator) {
+  return MemoryBuffer::getFile(Name, Result, FileSize, RequiresNullTerminator);
+}
+error_code RealFileSystem::getBufferForOpenFile(
+    AbstractFileSystem::FileDescriptor FD, const Twine &Name,
+    OwningPtr<MemoryBuffer> &Result, int64_t FileSize,
+    bool RequiresNullTerminator) {
+  return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
+                                   RequiresNullTerminator);
+}
+
+//===-----------------------------------------------------------------------===/
+// OverlayFileSystem implementation
+//===-----------------------------------------------------------------------===/
+OverlayFileSystem::OverlayFileSystem(
+    IntrusiveRefCntPtr<AbstractFileSystem> BaseFS) {
+  pushOverlay(BaseFS);
+}
+
+void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<AbstractFileSystem> FS) {
+  FSList.push_back(FS);
+}
+
+error_code OverlayFileSystem::status(const Twine &Path,
+                                     AbstractFileSystem::Status &Result) {
+  error_code EC;
+  for (iterator I = begin(), E = end(); I != E; ++I) {
+    EC = (*I)->status(Path, Result);
+    // FIXME: for directories we need to create a virtual directory for the
+    // merged contents (if there is more than one FS that contains the
+    // named directory).
+    if (EC == errc::success || EC != errc::no_such_file_or_directory)
+      return EC;
+  }
+  assert(EC == errc::no_such_file_or_directory);
+  return EC;
+}
+
+error_code OverlayFileSystem::openFileForRead(
+    const llvm::Twine &Path, AbstractFileSystem::FileDescriptor &ResultFD) {
+  error_code EC;
+  for (iterator I = begin(), E = end(); I != E; ++I) {
+    EC = (*I)->openFileForRead(Path, ResultFD);
+    if (EC == errc::success || EC != errc::no_such_file_or_directory)
+      return EC;
+  }
+  assert(EC == errc::no_such_file_or_directory);
+  return EC;
+}
+
+error_code OverlayFileSystem::getBufferForFile(const Twine &Name,
+                                               OwningPtr<MemoryBuffer> &Result,
+                                               int64_t FileSize,
+                                               bool RequiresNullTerminator) {
+  error_code EC;
+  for (iterator I = begin(), E = end(); I != E; ++I) {
+    EC = (*I)->getBufferForFile(Name, Result, FileSize, RequiresNullTerminator);
+    if (EC == errc::success || EC != errc::no_such_file_or_directory)
+      return EC;
+  }
+  assert(EC == errc::no_such_file_or_directory);
+  return EC;
+}
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index f57a4bc..f654a8e 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -20,6 +20,7 @@
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/TargetOptions.h"
+#include "clang/Basic/VirtualFileSystem.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -37,7 +38,6 @@
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/Atomic.h"
 #include "llvm/Support/CrashRecoveryContext.h"
-#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Host.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Mutex.h"
@@ -1463,7 +1463,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble(
              REnd = PreprocessorOpts.remapped_file_end();
            !AnyFileChanged && R != REnd;
            ++R) {
-        llvm::sys::fs::file_status Status;
+        AbstractFileSystem::Status Status;
         if (FileMgr->getNoncachedStatValue(R->second, Status)) {
           // If we can't stat the file we're remapping to, assume that something
           // horrible happened.
@@ -1499,7 +1499,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble(
         }
         
         // The file was not remapped; check whether it has changed on disk.
-        llvm::sys::fs::file_status Status;
+        AbstractFileSystem::Status Status;
         if (FileMgr->getNoncachedStatValue(F->first(), Status)) {
           // If we can't stat the file, assume that something horrible happened.
           AnyFileChanged = true;
diff --git a/lib/Frontend/CacheTokens.cpp b/lib/Frontend/CacheTokens.cpp
index 0c30b04..c8940c3 100644
--- a/lib/Frontend/CacheTokens.cpp
+++ b/lib/Frontend/CacheTokens.cpp
@@ -516,8 +516,8 @@ public:
   ~StatListener() {}
 
   LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                       int *FileDescriptor) {
-    LookupResult Result = statChained(Path, Data, isFile, FileDescriptor);
+                       int *FileDescriptor, AbstractFileSystem &FS) {
+    LookupResult Result = statChained(Path, Data, isFile, FileDescriptor, FS);
 
     if (Result == CacheMissing) // Failed 'stat'.
       PM.insert(PTHEntryKeyVariant(Path), PTHEntry());
diff --git a/lib/Frontend/ChainedIncludesSource.cpp b/lib/Frontend/ChainedIncludesSource.cpp
index 442177e..d6d63bf 100644
--- a/lib/Frontend/ChainedIncludesSource.cpp
+++ b/lib/Frontend/ChainedIncludesSource.cpp
@@ -101,6 +101,7 @@ ChainedIncludesSource *ChainedIncludesSource::create(CompilerInstance &CI) {
     Clang->setDiagnostics(Diags.getPtr());
     Clang->setTarget(TargetInfo::CreateTargetInfo(Clang->getDiagnostics(),
                                                   &Clang->getTargetOpts()));
+    Clang->createVirtualFileSystem();
     Clang->createFileManager();
     Clang->createSourceManager(Clang->getFileManager());
     Clang->createPreprocessor();
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index d121180..5014dfb 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -194,9 +194,14 @@ CompilerInstance::createDiagnostics(DiagnosticOptions *Opts,
   return Diags;
 }
 
+void CompilerInstance::createVirtualFileSystem() {
+  VirtualFileSystem = new OverlayFileSystem(getRealFileSystem());
+}
+
 // File Manager
 
 void CompilerInstance::createFileManager() {
+  assert(hasVirtualFileSystem() && "expected virtual file system");
   FileMgr = new FileManager(getFileSystemOpts());
 }
 
@@ -867,6 +872,8 @@ static void compileModule(CompilerInstance &ImportingInstance,
                                    ImportingInstance.getDiagnosticClient()),
                              /*ShouldOwnClient=*/true);
 
+  Instance.setVirtualFileSystem(&ImportingInstance.getVirtualFileSystem());
+
   // Note that this module is part of the module build stack, so that we
   // can detect cycles in the module graph.
   Instance.createFileManager(); // FIXME: Adopt file manager from importer?
diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp
index 0baf3e5..4e04c2c 100644
--- a/lib/Frontend/FrontendAction.cpp
+++ b/lib/Frontend/FrontendAction.cpp
@@ -159,7 +159,6 @@ ASTConsumer* FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI,
   return new MultiplexConsumer(Consumers);
 }
 
-
 bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
                                      const FrontendInputFile &Input) {
   assert(!Instance && "Already processing a source file!");
@@ -213,6 +212,8 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
   }
 
   // Set up the file and source managers, if needed.
+  if (!CI.hasVirtualFileSystem())
+    CI.createVirtualFileSystem();
   if (!CI.hasFileManager())
     CI.createFileManager();
   if (!CI.hasSourceManager())
diff --git a/lib/Lex/PTHLexer.cpp b/lib/Lex/PTHLexer.cpp
index e174222..81cd4d8 100644
--- a/lib/Lex/PTHLexer.cpp
+++ b/lib/Lex/PTHLexer.cpp
@@ -675,13 +675,13 @@ public:
   ~PTHStatCache() {}
 
   LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                       int *FileDescriptor) {
+                       int *FileDescriptor, AbstractFileSystem &FS) {
     // Do the lookup for the file's data in the PTH file.
     CacheTy::iterator I = Cache.find(Path);
 
     // If we don't get a hit in the PTH file just forward to 'stat'.
     if (I == Cache.end())
-      return statChained(Path, Data, isFile, FileDescriptor);
+      return statChained(Path, Data, isFile, FileDescriptor, FS);
 
     const PTHStatData &D = *I;
 
diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt
index e8b766c..b8f69bf 100644
--- a/unittests/Basic/CMakeLists.txt
+++ b/unittests/Basic/CMakeLists.txt
@@ -6,6 +6,7 @@ add_clang_unittest(BasicTests
   CharInfoTest.cpp
   FileManagerTest.cpp
   SourceManagerTest.cpp
+  VirtualFileSystemTest.cpp
   )
 
 target_link_libraries(BasicTests
diff --git a/unittests/Basic/FileManagerTest.cpp b/unittests/Basic/FileManagerTest.cpp
index f8ce50d..2ee0d4c 100644
--- a/unittests/Basic/FileManagerTest.cpp
+++ b/unittests/Basic/FileManagerTest.cpp
@@ -48,7 +48,7 @@ public:
 
   // Implement FileSystemStatCache::getStat().
   virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                               int *FileDescriptor) {
+                               int *FileDescriptor, AbstractFileSystem &FS) {
     if (StatCalls.count(Path) != 0) {
       Data = StatCalls[Path];
       return CacheExists;
diff --git a/unittests/Basic/VirtualFileSystemTest.cpp b/unittests/Basic/VirtualFileSystemTest.cpp
new file mode 100644
index 0000000..b5a5ec0
--- /dev/null
+++ b/unittests/Basic/VirtualFileSystemTest.cpp
@@ -0,0 +1,160 @@
+//===- unittests/Basic/VirtualFileSystem.cpp ---------------- VFS tests ---===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/VirtualFileSystem.h"
+#include "gtest/gtest.h"
+#include <map>
+using namespace clang;
+using namespace llvm;
+using llvm::sys::fs::UniqueID;
+
+namespace {
+class DummyFileSystem : public AbstractFileSystem {
+  int FSID;   // used to produce UniqueIDs
+  int FileID; // used to produce UniqueIDs
+  std::map<std::string, Status> FilesAndDirs;
+
+  static int getNextFSID() {
+    static int Count = 0;
+    return Count++;
+  }
+
+public:
+  DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
+
+  error_code status(const Twine &Path, Status &Result) {
+    std::map<std::string, Status>::iterator I = FilesAndDirs.find(Path.str());
+    if (I == FilesAndDirs.end())
+      return error_code(errc::no_such_file_or_directory, posix_category());
+    Result = I->second;
+    return error_code::success();
+  }
+  error_code openFileForRead(const Twine &Path, FileDescriptor &ResultFD) {
+    llvm_unreachable("unimplemented");
+  }
+  error_code getBufferForFile(const Twine &Name,
+                              OwningPtr<MemoryBuffer> &Result,
+                              int64_t FileSize = -1,
+                              bool RequiresNullTerminator = true) {
+    llvm_unreachable("unimplemented");
+  }
+
+  void addEntry(StringRef Path, const Status &Status) {
+    FilesAndDirs[Path] = Status;
+  }
+
+  void addRegularFile(StringRef Path) {
+    Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0,
+             1024, sys::fs::file_type::regular_file, sys::fs::all_all);
+    addEntry(Path, S);
+  }
+
+  void addDirectory(StringRef Path) {
+    Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0,
+             0, sys::fs::file_type::directory_file, sys::fs::all_all);
+    addEntry(Path, S);
+  }
+
+  void addSymlink(StringRef Path) {
+    Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0,
+             0, sys::fs::file_type::symlink_file, sys::fs::all_all);
+    addEntry(Path, S);
+  }
+};
+
+TEST(VirtualFileSystemTest, status_queries) {
+  IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
+  AbstractFileSystem::Status Status;
+  EXPECT_FALSE(AbstractFileSystem::status_known(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_directory(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_regular_file(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_symlink(Status));
+  EXPECT_TRUE(AbstractFileSystem::is_other(Status));
+  EXPECT_FALSE(AbstractFileSystem::exists(Status));
+
+  D->addRegularFile("/foo");
+  ASSERT_EQ(D->status("/foo", Status), errc::success);
+  EXPECT_TRUE(AbstractFileSystem::status_known(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_directory(Status));
+  EXPECT_TRUE(AbstractFileSystem::is_regular_file(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_symlink(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_other(Status));
+  EXPECT_TRUE(AbstractFileSystem::exists(Status));
+
+  D->addDirectory("/bar");
+  ASSERT_EQ(D->status("/bar", Status), errc::success);
+  EXPECT_TRUE(AbstractFileSystem::status_known(Status));
+  EXPECT_TRUE(AbstractFileSystem::is_directory(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_regular_file(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_symlink(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_other(Status));
+  EXPECT_TRUE(AbstractFileSystem::exists(Status));
+
+  D->addSymlink("/baz");
+  ASSERT_EQ(D->status("/baz", Status), errc::success);
+  EXPECT_TRUE(AbstractFileSystem::status_known(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_directory(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_regular_file(Status));
+  EXPECT_TRUE(AbstractFileSystem::is_symlink(Status));
+  EXPECT_FALSE(AbstractFileSystem::is_other(Status));
+  EXPECT_TRUE(AbstractFileSystem::exists(Status));
+
+  EXPECT_TRUE(AbstractFileSystem::equivalent(Status, Status));
+  AbstractFileSystem::Status Status2;
+  ASSERT_EQ(D->status("/foo", Status2), errc::success);
+  EXPECT_FALSE(AbstractFileSystem::equivalent(Status, Status2));
+}
+
+TEST(VirtualFileSystemTest, base_only_overlay) {
+  IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
+  AbstractFileSystem::Status Status;
+  EXPECT_EQ(errc::no_such_file_or_directory, D->status("/foo", Status));
+
+  IntrusiveRefCntPtr<OverlayFileSystem> O(new OverlayFileSystem(D));
+  EXPECT_EQ(errc::no_such_file_or_directory, O->status("/foo", Status));
+
+  D->addRegularFile("/foo");
+  EXPECT_EQ(errc::success, D->status("/foo", Status));
+
+  AbstractFileSystem::Status Status2;
+  EXPECT_EQ(errc::success, O->status("/foo", Status2));
+  EXPECT_TRUE(AbstractFileSystem::equivalent(Status, Status2));
+}
+
+TEST(VirtualFileSystemTest, overlay_files) {
+  IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
+  IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
+  IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
+  IntrusiveRefCntPtr<OverlayFileSystem> O(new OverlayFileSystem(Base));
+  O->pushOverlay(Middle);
+  O->pushOverlay(Top);
+
+  AbstractFileSystem::Status Status1, Status2, Status3, StatusB, StatusM,
+      StatusT;
+
+  Base->addRegularFile("/foo");
+  ASSERT_EQ(errc::success, Base->status("/foo", StatusB));
+  ASSERT_EQ(errc::success, O->status("/foo", Status1));
+  Middle->addRegularFile("/foo");
+  ASSERT_EQ(errc::success, Middle->status("/foo", StatusM));
+  ASSERT_EQ(errc::success, O->status("/foo", Status2));
+  Top->addRegularFile("/foo");
+  ASSERT_EQ(errc::success, Top->status("/foo", StatusT));
+  ASSERT_EQ(errc::success, O->status("/foo", Status3));
+
+  EXPECT_TRUE(AbstractFileSystem::equivalent(Status1, StatusB));
+  EXPECT_TRUE(AbstractFileSystem::equivalent(Status2, StatusM));
+  EXPECT_TRUE(AbstractFileSystem::equivalent(Status3, StatusT));
+
+  EXPECT_FALSE(AbstractFileSystem::equivalent(Status1, Status2));
+  EXPECT_FALSE(AbstractFileSystem::equivalent(Status2, Status3));
+  EXPECT_FALSE(AbstractFileSystem::equivalent(Status1, Status3));
+}
+
+} // end anonymous namespace
