r201818 - Recommit virtual file system

Ben Langmuir blangmuir at apple.com
Thu Feb 20 13:59:23 PST 2014


Author: benlangmuir
Date: Thu Feb 20 15:59:23 2014
New Revision: 201818

URL: http://llvm.org/viewvc/llvm-project?rev=201818&view=rev
Log:
Recommit virtual file system

Previously reverted in r201755 due to causing an assertion failure.

I've removed the offending assertion, and taught the CompilerInstance to
create a default virtual file system inside createFileManager. In the
future, we should be able to reach into the CompilerInvocation to
customize this behaviour without breaking clients that don't care.

Added:
    cfe/trunk/include/clang/Basic/VirtualFileSystem.h
    cfe/trunk/lib/Basic/VirtualFileSystem.cpp
    cfe/trunk/unittests/Basic/VirtualFileSystemTest.cpp
Modified:
    cfe/trunk/include/clang/Basic/FileManager.h
    cfe/trunk/include/clang/Basic/FileSystemStatCache.h
    cfe/trunk/include/clang/Frontend/CompilerInstance.h
    cfe/trunk/lib/Basic/CMakeLists.txt
    cfe/trunk/lib/Basic/FileManager.cpp
    cfe/trunk/lib/Basic/FileSystemStatCache.cpp
    cfe/trunk/lib/Frontend/ASTUnit.cpp
    cfe/trunk/lib/Frontend/CacheTokens.cpp
    cfe/trunk/lib/Frontend/CompilerInstance.cpp
    cfe/trunk/lib/Frontend/FrontendAction.cpp
    cfe/trunk/lib/Lex/PTHLexer.cpp
    cfe/trunk/lib/Serialization/ModuleManager.cpp
    cfe/trunk/unittests/Basic/CMakeLists.txt
    cfe/trunk/unittests/Basic/FileManagerTest.cpp

Modified: cfe/trunk/include/clang/Basic/FileManager.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/FileManager.h?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/FileManager.h (original)
+++ cfe/trunk/include/clang/Basic/FileManager.h Thu Feb 20 15:59:23 2014
@@ -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>
 
@@ -55,7 +55,7 @@ public:
 /// \brief Cached information about one file (either on disk
 /// or in the virtual file system).
 ///
-/// If the 'FD' member is valid, then this FileEntry has an open file
+/// If the 'File' member is valid, then this FileEntry has an open file
 /// descriptor for the file.
 class FileEntry {
   const char *Name;           // Name of the file.
@@ -67,31 +67,33 @@ class FileEntry {
   bool IsNamedPipe;
   bool InPCH;
 
-  /// FD - The file descriptor for the file entry if it is opened and owned
-  /// by the FileEntry.  If not, this is set to -1.
-  mutable int FD;
+  /// \brief The open file, if it is owned by the \p FileEntry.
+  mutable OwningPtr<vfs::File> File;
   friend class FileManager;
 
+  void closeFile() const {
+    File.reset(0); // rely on destructor to close File
+  }
+
 public:
   FileEntry(llvm::sys::fs::UniqueID UniqueID, bool IsNamedPipe, bool InPCH)
-      : Name(0), UniqueID(UniqueID), IsNamedPipe(IsNamedPipe), InPCH(InPCH),
-        FD(-1) {}
+      : Name(0), UniqueID(UniqueID), IsNamedPipe(IsNamedPipe), InPCH(InPCH)
+  {}
   // Add a default constructor for use with llvm::StringMap
   FileEntry()
-      : Name(0), UniqueID(0, 0), IsNamedPipe(false), InPCH(false), FD(-1) {}
+      : Name(0), UniqueID(0, 0), IsNamedPipe(false), InPCH(false)
+  {}
 
   FileEntry(const FileEntry &FE) {
     memcpy(this, &FE, sizeof(FE));
-    assert(FD == -1 && "Cannot copy a file-owning FileEntry");
+    assert(!File && "Cannot copy a file-owning FileEntry");
   }
 
   void operator=(const FileEntry &FE) {
     memcpy(this, &FE, sizeof(FE));
-    assert(FD == -1 && "Cannot assign a file-owning FileEntry");
+    assert(!File && "Cannot assign a file-owning FileEntry");
   }
 
-  ~FileEntry();
-
   const char *getName() const { return Name; }
   off_t getSize() const { return Size; }
   unsigned getUID() const { return UID; }
@@ -119,6 +121,7 @@ struct FileData;
 /// as a single file.
 ///
 class FileManager : public RefCountedBase<FileManager> {
+  IntrusiveRefCntPtr<vfs::FileSystem> FS;
   FileSystemOptions FileSystemOpts;
 
   class UniqueDirContainer;
@@ -172,14 +175,15 @@ class FileManager : public RefCountedBas
   OwningPtr<FileSystemStatCache> StatCache;
 
   bool getStatValue(const char *Path, FileData &Data, bool isFile,
-                    int *FileDescriptor);
+                    vfs::File **F);
 
   /// Add all ancestors of the given path (pointing to either a file
   /// or a directory) as virtual directories.
   void addAncestorsAsVirtualDirs(StringRef Path);
 
 public:
-  FileManager(const FileSystemOptions &FileSystemOpts);
+  FileManager(const FileSystemOptions &FileSystemOpts,
+              IntrusiveRefCntPtr<vfs::FileSystem> FS = 0);
   ~FileManager();
 
   /// \brief Installs the provided FileSystemStatCache object within
@@ -226,6 +230,10 @@ public:
   /// \brief Returns the current file system options
   const FileSystemOptions &getFileSystemOptions() { return FileSystemOpts; }
 
+  IntrusiveRefCntPtr<vfs::FileSystem> getVirtualFileSystem() const {
+    return FS;
+  }
+
   /// \brief Retrieve a file entry for a "virtual" file that acts as
   /// if there were a file with the given name on disk.
   ///
@@ -248,7 +256,7 @@ public:
   ///
   /// \returns false on success, true on error.
   bool getNoncachedStatValue(StringRef Path,
-                             llvm::sys::fs::file_status &Result);
+                             vfs::Status &Result);
 
   /// \brief Remove the real file \p Entry from the cache.
   void invalidateCache(const FileEntry *Entry);

Modified: cfe/trunk/include/clang/Basic/FileSystemStatCache.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/FileSystemStatCache.h?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/FileSystemStatCache.h (original)
+++ cfe/trunk/include/clang/Basic/FileSystemStatCache.h Thu Feb 20 15:59:23 2014
@@ -24,6 +24,11 @@
 
 namespace clang {
 
+namespace vfs {
+class File;
+class FileSystem;
+}
+
 struct FileData {
   uint64_t Size;
   time_t ModTime;
@@ -57,10 +62,11 @@ public:
   /// If isFile is true, then this lookup should only return success for files
   /// (not directories).  If it is false this lookup should only return
   /// success for directories (not files).  On a successful file lookup, the
-  /// implementation can optionally fill in FileDescriptor with a valid
-  /// descriptor and the client guarantees that it will close it.
+  /// implementation can optionally fill in \p F with a valid \p File object and
+  /// the client guarantees that it will close it.
   static bool get(const char *Path, FileData &Data, bool isFile,
-                  int *FileDescriptor, FileSystemStatCache *Cache);
+                  vfs::File **F, FileSystemStatCache *Cache,
+                  vfs::FileSystem &FS);
 
   /// \brief Sets the next stat call cache in the chain of stat caches.
   /// Takes ownership of the given stat cache.
@@ -78,17 +84,16 @@ public:
   
 protected:
   virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                               int *FileDescriptor) = 0;
+                               vfs::File **F, vfs::FileSystem &FS) = 0;
 
   LookupResult statChained(const char *Path, FileData &Data, bool isFile,
-                           int *FileDescriptor) {
+                           vfs::File **F, vfs::FileSystem &FS) {
     if (FileSystemStatCache *Next = getNextStatCache())
-      return Next->getStat(Path, Data, isFile, FileDescriptor);
+      return Next->getStat(Path, Data, isFile, F, 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, F, 0, FS) ? CacheMissing : CacheExists;
   }
 };
 
@@ -107,7 +112,7 @@ public:
   iterator end() const { return StatCalls.end(); }
 
   virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                               int *FileDescriptor);
+                               vfs::File **F, vfs::FileSystem &FS);
 };
 
 } // end namespace clang

Added: cfe/trunk/include/clang/Basic/VirtualFileSystem.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/VirtualFileSystem.h?rev=201818&view=auto
==============================================================================
--- cfe/trunk/include/clang/Basic/VirtualFileSystem.h (added)
+++ cfe/trunk/include/clang/Basic/VirtualFileSystem.h Thu Feb 20 15:59:23 2014
@@ -0,0 +1,162 @@
+//===- 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 vfs::FileSystem.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
+#define LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/ErrorOr.h"
+
+namespace llvm {
+class MemoryBuffer;
+}
+
+namespace clang {
+namespace vfs {
+
+/// \brief The result of a \p status operation.
+class Status {
+  std::string Name;
+  std::string ExternalName;
+  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(StringRef Name, 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.
+  StringRef getName() const { return Name; }
+
+  /// \brief Returns the name to use outside the compiler.
+  ///
+  /// For example, in diagnostics or debug info we should use this name.
+  StringRef getExternalName() const { return ExternalName; }
+
+  void setName(StringRef N) { Name = N; }
+  void setExternalName(StringRef N) { ExternalName = N; }
+
+  /// @name Status interface from llvm::sys::fs
+  /// @{
+  llvm::sys::fs::file_type getType() const { return Type; }
+  llvm::sys::fs::perms getPermissions() 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 setType(llvm::sys::fs::file_type v) { Type = v; }
+  void setPermissions(llvm::sys::fs::perms p) { Perms = p; }
+  /// @}
+  /// @name Status queries
+  /// These are static queries in llvm::sys::fs.
+  /// @{
+  bool equivalent(const Status &Other) const;
+  bool isDirectory() const;
+  bool isRegularFile() const;
+  bool isOther() const;
+  bool isSymlink() const;
+  bool isStatusKnown() const;
+  bool exists() const;
+  /// @}
+};
+
+/// \brief Represents an open file.
+class File {
+public:
+  /// \brief Destroy the file after closing it (if open).
+  /// Sub-classes should generally call close() inside their destructors.  We
+  /// cannot do that from the base class, since close is virtual.
+  virtual ~File();
+  /// \brief Get the status of the file.
+  virtual llvm::ErrorOr<Status> status() = 0;
+  /// \brief Get the contents of the file as a \p MemoryBuffer.
+  virtual llvm::error_code getBuffer(const Twine &Name,
+                                     OwningPtr<llvm::MemoryBuffer> &Result,
+                                     int64_t FileSize = -1,
+                                     bool RequiresNullTerminator = true) = 0;
+  /// \brief Closes the file.
+  virtual llvm::error_code close() = 0;
+};
+
+/// \brief The virtual file system interface.
+class FileSystem : public RefCountedBase<FileSystem> {
+public:
+  virtual ~FileSystem();
+
+  /// \brief Get the status of the entry at \p Path, if one exists.
+  virtual llvm::ErrorOr<Status> status(const Twine &Path) = 0;
+  /// \brief Get a \p File object for the file at \p Path, if one exists.
+  virtual llvm::error_code openFileForRead(const Twine &Path,
+                                           OwningPtr<File> &Result) = 0;
+
+  /// This is a convenience method that opens a file, gets its content and then
+  /// closes the file.
+  llvm::error_code getBufferForFile(const Twine &Name,
+                                    OwningPtr<llvm::MemoryBuffer> &Result,
+                                    int64_t FileSize = -1,
+                                    bool RequiresNullTerminator = true);
+};
+
+/// \brief Gets an \p vfs::FileSystem for the 'real' file system, as seen by
+/// the operating system.
+IntrusiveRefCntPtr<FileSystem> getRealFileSystem();
+
+/// \brief A file system that allows overlaying one \p AbstractFileSystem on top
+/// of another.
+///
+/// Consists of a stack of >=1 \p FileSytem 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.  The attributes (permissions, etc.) of the
+/// top-most (most recently added) directory are used.  When there is a file
+/// that exists in more than one file system, the file in the top-most file
+/// system overrides the other(s).
+class OverlayFileSystem : public FileSystem {
+  typedef SmallVector<IntrusiveRefCntPtr<FileSystem>, 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 overlays_begin() { return FSList.rbegin(); }
+
+  /// \brief Get an iterator pointing one-past the least recently added file
+  /// system.
+  iterator overlays_end() { return FSList.rend(); }
+
+public:
+  OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> Base);
+  /// \brief Pushes a file system on top of the stack.
+  void pushOverlay(IntrusiveRefCntPtr<FileSystem> FS);
+
+  llvm::ErrorOr<Status> status(const Twine &Path) LLVM_OVERRIDE;
+  llvm::error_code openFileForRead(const Twine &Path,
+                                   OwningPtr<File> &Result) LLVM_OVERRIDE;
+};
+
+} // end namespace vfs
+} // end namespace clang
+#endif // LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H

Modified: cfe/trunk/include/clang/Frontend/CompilerInstance.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CompilerInstance.h?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/CompilerInstance.h (original)
+++ cfe/trunk/include/clang/Frontend/CompilerInstance.h Thu Feb 20 15:59:23 2014
@@ -75,6 +75,9 @@ class CompilerInstance : public ModuleLo
   /// The target being compiled for.
   IntrusiveRefCntPtr<TargetInfo> Target;
 
+  /// The virtual file system.
+  IntrusiveRefCntPtr<vfs::FileSystem> VirtualFileSystem;
+
   /// The file manager.
   IntrusiveRefCntPtr<FileManager> FileMgr;
 
@@ -314,6 +317,26 @@ public:
   void setTarget(TargetInfo *Value);
 
   /// }
+  /// @name Virtual File System
+  /// {
+
+  bool hasVirtualFileSystem() const { return VirtualFileSystem != 0; }
+
+  vfs::FileSystem &getVirtualFileSystem() const {
+    assert(hasVirtualFileSystem() &&
+           "Compiler instance has no virtual file system");
+    return *VirtualFileSystem;
+  }
+
+  /// \brief Replace the current virtual file system.
+  ///
+  /// \note Most clients should use setFileManager, which will implicitly reset
+  /// the virtual file system to the one contained in the file manager.
+  void setVirtualFileSystem(IntrusiveRefCntPtr<vfs::FileSystem> FS) {
+    VirtualFileSystem = FS;
+  }
+
+  /// }
   /// @name File Manager
   /// {
 
@@ -330,7 +353,7 @@ public:
     FileMgr.resetWithoutRelease();
   }
 
-  /// setFileManager - Replace the current file manager.
+  /// \brief Replace the current file manager and virtual file system.
   void setFileManager(FileManager *Value);
 
   /// }

Modified: cfe/trunk/lib/Basic/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/CMakeLists.txt?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/CMakeLists.txt (original)
+++ cfe/trunk/lib/Basic/CMakeLists.txt Thu Feb 20 15:59:23 2014
@@ -23,6 +23,7 @@ add_clang_library(clangBasic
   TokenKinds.cpp
   Version.cpp
   VersionTuple.cpp
+  VirtualFileSystem.cpp
   )
 
 # Determine Subversion revision.

Modified: cfe/trunk/lib/Basic/FileManager.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/FileManager.cpp?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/FileManager.cpp (original)
+++ cfe/trunk/lib/Basic/FileManager.cpp Thu Feb 20 15:59:23 2014
@@ -30,19 +30,6 @@
 #include <set>
 #include <string>
 
-// FIXME: This is terrible, we need this for ::close.
-#if !defined(_MSC_VER) && !defined(__MINGW32__)
-#include <unistd.h>
-#include <sys/uio.h>
-#else
-#include <io.h>
-#ifndef S_ISFIFO
-#define S_ISFIFO(x) (0)
-#endif
-#endif
-#if defined(LLVM_ON_UNIX)
-#include <limits.h>
-#endif
 using namespace clang;
 
 // FIXME: Enhance libsystem to support inode and other fields.
@@ -57,12 +44,6 @@ using namespace clang;
 #define NON_EXISTENT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1)
 
 
-FileEntry::~FileEntry() {
-  // If this FileEntry owns an open file descriptor that never got used, close
-  // it.
-  if (FD != -1) ::close(FD);
-}
-
 class FileManager::UniqueDirContainer {
   /// UniqueDirs - Cache from ID's to existing directories/files.
   std::map<llvm::sys::fs::UniqueID, DirectoryEntry> UniqueDirs;
@@ -101,13 +82,19 @@ public:
 // Common logic.
 //===----------------------------------------------------------------------===//
 
-FileManager::FileManager(const FileSystemOptions &FSO)
-  : FileSystemOpts(FSO),
+FileManager::FileManager(const FileSystemOptions &FSO,
+                         IntrusiveRefCntPtr<vfs::FileSystem> 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 = vfs::getRealFileSystem();
 }
 
 FileManager::~FileManager() {
@@ -309,10 +296,9 @@ const FileEntry *FileManager::getFile(St
   // FIXME: This will reduce the # syscalls.
 
   // Nope, there isn't.  Check to see if the file exists.
-  int FileDescriptor = -1;
+  vfs::File *F = 0;
   FileData Data;
-  if (getStatValue(InterndFileName, Data, true,
-                   openFile ? &FileDescriptor : 0)) {
+  if (getStatValue(InterndFileName, Data, true, openFile ? &F : 0)) {
     // There's no real file at the given path.
     if (!CacheFailure)
       SeenFileEntries.erase(Filename);
@@ -320,10 +306,7 @@ const FileEntry *FileManager::getFile(St
     return 0;
   }
 
-  if (FileDescriptor != -1 && !openFile) {
-    close(FileDescriptor);
-    FileDescriptor = -1;
-  }
+  assert(openFile || !F && "undesired open file");
 
   // It exists.  See if we have already opened a file with the same inode.
   // This occurs when one dir is symlinked to another, for example.
@@ -333,8 +316,8 @@ const FileEntry *FileManager::getFile(St
   NamedFileEnt.setValue(&UFE);
   if (UFE.getName()) { // Already have an entry with this inode, return it.
     // If the stat process opened the file, close it to avoid a FD leak.
-    if (FileDescriptor != -1)
-      close(FileDescriptor);
+    if (F)
+      delete F;
 
     return &UFE;
   }
@@ -347,7 +330,7 @@ const FileEntry *FileManager::getFile(St
   UFE.ModTime = Data.ModTime;
   UFE.Dir     = DirInfo;
   UFE.UID     = NextFileUID++;
-  UFE.FD      = FileDescriptor;
+  UFE.File.reset(F);
   return &UFE;
 }
 
@@ -393,10 +376,8 @@ FileManager::getVirtualFile(StringRef Fi
     // If we had already opened this file, close it now so we don't
     // leak the descriptor. We're not going to use the file
     // descriptor anyway, since this is a virtual file.
-    if (UFE->FD != -1) {
-      close(UFE->FD);
-      UFE->FD = -1;
-    }
+    if (UFE->File)
+      UFE->closeFile();
 
     // If we already have an entry with this inode, return it.
     if (UFE->getName())
@@ -414,7 +395,7 @@ FileManager::getVirtualFile(StringRef Fi
   UFE->ModTime = ModificationTime;
   UFE->Dir     = DirInfo;
   UFE->UID     = NextFileUID++;
-  UFE->FD      = -1;
+  UFE->File.reset();
   return UFE;
 }
 
@@ -444,20 +425,18 @@ getBufferForFile(const FileEntry *Entry,
 
   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);
+  if (Entry->File) {
+    ec = Entry->File->getBuffer(Filename, Result, FileSize);
     if (ErrorStr)
       *ErrorStr = ec.message();
-
-    close(Entry->FD);
-    Entry->FD = -1;
+    Entry->closeFile();
     return Result.take();
   }
 
   // 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 +444,7 @@ getBufferForFile(const FileEntry *Entry,
 
   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 +455,7 @@ getBufferForFile(StringRef Filename, std
   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 +463,7 @@ getBufferForFile(StringRef Filename, std
 
   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();
@@ -496,26 +475,29 @@ getBufferForFile(StringRef Filename, std
 /// false if it's an existent real file.  If FileDescriptor is NULL,
 /// do directory look-up instead of file look-up.
 bool FileManager::getStatValue(const char *Path, FileData &Data, bool isFile,
-                               int *FileDescriptor) {
+                               vfs::File **F) {
   // FIXME: FileSystemOpts shouldn't be passed in here, all paths should be
   // absolute!
   if (FileSystemOpts.WorkingDir.empty())
-    return FileSystemStatCache::get(Path, Data, isFile, FileDescriptor,
-                                    StatCache.get());
+    return FileSystemStatCache::get(Path, Data, isFile, F,StatCache.get(), *FS);
 
   SmallString<128> FilePath(Path);
   FixupRelativePath(FilePath);
 
-  return FileSystemStatCache::get(FilePath.c_str(), Data, isFile,
-                                  FileDescriptor, StatCache.get());
+  return FileSystemStatCache::get(FilePath.c_str(), Data, isFile, F,
+                                  StatCache.get(), *FS);
 }
 
 bool FileManager::getNoncachedStatValue(StringRef Path,
-                                        llvm::sys::fs::file_status &Result) {
+                                        vfs::Status &Result) {
   SmallString<128> FilePath(Path);
   FixupRelativePath(FilePath);
 
-  return llvm::sys::fs::status(FilePath.c_str(), Result);
+  llvm::ErrorOr<vfs::Status> S = FS->status(FilePath.c_str());
+  if (!S)
+    return true;
+  Result = *S;
+  return false;
 }
 
 void FileManager::invalidateCache(const FileEntry *Entry) {

Modified: cfe/trunk/lib/Basic/FileSystemStatCache.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/FileSystemStatCache.cpp?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/FileSystemStatCache.cpp (original)
+++ cfe/trunk/lib/Basic/FileSystemStatCache.cpp Thu Feb 20 15:59:23 2014
@@ -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,13 +30,13 @@ using namespace clang;
 
 void FileSystemStatCache::anchor() { }
 
-static void copyStatusToFileData(const llvm::sys::fs::file_status &Status,
+static void copyStatusToFileData(const vfs::Status &Status,
                                  FileData &Data) {
   Data.Size = Status.getSize();
   Data.ModTime = Status.getLastModificationTime().toEpochTime();
   Data.UniqueID = Status.getUniqueID();
-  Data.IsDirectory = is_directory(Status);
-  Data.IsNamedPipe = Status.type() == llvm::sys::fs::file_type::fifo_file;
+  Data.IsDirectory = Status.isDirectory();
+  Data.IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
   Data.InPCH = false;
 }
 
@@ -50,22 +50,23 @@ static void copyStatusToFileData(const l
 /// 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) {
+                              vfs::File **F, FileSystemStatCache *Cache,
+                              vfs::FileSystem &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);
-  else if (isForDir || !FileDescriptor) {
+    R = Cache->getStat(Path, Data, isFile, F, FS);
+  else if (isForDir || !F) {
     // 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)) {
+    llvm::ErrorOr<vfs::Status> Status = FS.status(Path);
+    if (!Status) {
       R = CacheMissing;
     } else {
       R = CacheExists;
-      copyStatusToFileData(Status, Data);
+      copyStatusToFileData(*Status, Data);
     }
   } else {
     // Otherwise, we have to go to the filesystem.  We can always just use
@@ -75,7 +76,8 @@ bool FileSystemStatCache::get(const char
     //
     // 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::OwningPtr<vfs::File> OwnedFile;
+    llvm::error_code EC = FS.openFileForRead(Path, OwnedFile);
 
     if (EC) {
       // If the open fails, our "stat" fails.
@@ -84,16 +86,16 @@ bool FileSystemStatCache::get(const char
       // 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)) {
+      llvm::ErrorOr<vfs::Status> Status = OwnedFile->status();
+      if (Status) {
         R = CacheExists;
-        copyStatusToFileData(Status, Data);
+        copyStatusToFileData(*Status, Data);
+        *F = OwnedFile.take();
       } else {
         // fstat rarely fails.  If it does, claim the initial open didn't
         // succeed.
         R = CacheMissing;
-        ::close(*FileDescriptor);
-        *FileDescriptor = -1;
+        *F = 0;
       }
     }
   }
@@ -105,9 +107,9 @@ bool FileSystemStatCache::get(const char
   // demands.
   if (Data.IsDirectory != isForDir) {
     // If not, close the file if opened.
-    if (FileDescriptor && *FileDescriptor != -1) {
-      ::close(*FileDescriptor);
-      *FileDescriptor = -1;
+    if (F && *F) {
+      (*F)->close();
+      *F = 0;
     }
     
     return true;
@@ -118,8 +120,8 @@ bool FileSystemStatCache::get(const char
 
 MemorizeStatCalls::LookupResult
 MemorizeStatCalls::getStat(const char *Path, FileData &Data, bool isFile,
-                           int *FileDescriptor) {
-  LookupResult Result = statChained(Path, Data, isFile, FileDescriptor);
+                           vfs::File **F, vfs::FileSystem &FS) {
+  LookupResult Result = statChained(Path, Data, isFile, F, 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

Added: cfe/trunk/lib/Basic/VirtualFileSystem.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/VirtualFileSystem.cpp?rev=201818&view=auto
==============================================================================
--- cfe/trunk/lib/Basic/VirtualFileSystem.cpp (added)
+++ cfe/trunk/lib/Basic/VirtualFileSystem.cpp Thu Feb 20 15:59:23 2014
@@ -0,0 +1,193 @@
+//===- VirtualFileSystem.cpp - 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 clang::vfs;
+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;
+
+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()) {}
+
+Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
+               sys::TimeValue MTime, uint32_t User, uint32_t Group,
+               uint64_t Size, file_type Type, perms Perms)
+    : Name(Name), ExternalName(ExternalName), UID(UID), MTime(MTime),
+      User(User), Group(Group), Size(Size), Type(Type), Perms(Perms) {}
+
+bool Status::equivalent(const Status &Other) const {
+  return getUniqueID() == Other.getUniqueID();
+}
+bool Status::isDirectory() const {
+  return Type == file_type::directory_file;
+}
+bool Status::isRegularFile() const {
+  return Type == file_type::regular_file;
+}
+bool Status::isOther() const {
+  return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
+}
+bool Status::isSymlink() const {
+  return Type == file_type::symlink_file;
+}
+bool Status::isStatusKnown() const {
+  return Type != file_type::status_error;
+}
+bool Status::exists() const {
+  return isStatusKnown() && Type != file_type::file_not_found;
+}
+
+File::~File() {}
+
+FileSystem::~FileSystem() {}
+
+error_code FileSystem::getBufferForFile(const llvm::Twine &Name,
+                                        OwningPtr<MemoryBuffer> &Result,
+                                        int64_t FileSize,
+                                        bool RequiresNullTerminator) {
+  llvm::OwningPtr<File> F;
+  if (error_code EC = openFileForRead(Name, F))
+    return EC;
+
+  error_code EC = F->getBuffer(Name, Result, FileSize, RequiresNullTerminator);
+  return EC;
+}
+
+//===-----------------------------------------------------------------------===/
+// RealFileSystem implementation
+//===-----------------------------------------------------------------------===/
+
+/// \brief Wrapper around a raw file descriptor.
+class RealFile : public File {
+  int FD;
+  friend class RealFileSystem;
+  RealFile(int FD) : FD(FD) {
+    assert(FD >= 0 && "Invalid or inactive file descriptor");
+  }
+public:
+  ~RealFile();
+  ErrorOr<Status> status() LLVM_OVERRIDE;
+  error_code getBuffer(const Twine &Name, OwningPtr<MemoryBuffer> &Result,
+                       int64_t FileSize = -1,
+                       bool RequiresNullTerminator = true) LLVM_OVERRIDE;
+  error_code close() LLVM_OVERRIDE;
+};
+RealFile::~RealFile() {
+  close();
+}
+
+ErrorOr<Status> RealFile::status() {
+  assert(FD != -1 && "cannot stat closed file");
+  file_status RealStatus;
+  if (error_code EC = sys::fs::status(FD, RealStatus))
+    return EC;
+  return Status(RealStatus);
+}
+
+error_code RealFile::getBuffer(const Twine &Name,
+                               OwningPtr<MemoryBuffer> &Result,
+                               int64_t FileSize, bool RequiresNullTerminator) {
+  assert(FD != -1 && "cannot get buffer for closed file");
+  return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
+                                   RequiresNullTerminator);
+}
+
+// FIXME: This is terrible, we need this for ::close.
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#include <sys/uio.h>
+#else
+#include <io.h>
+#ifndef S_ISFIFO
+#define S_ISFIFO(x) (0)
+#endif
+#endif
+error_code RealFile::close() {
+  if (::close(FD))
+    return error_code(errno, system_category());
+  FD = -1;
+  return error_code::success();
+}
+
+/// \brief The file system according to your operating system.
+class RealFileSystem : public FileSystem {
+public:
+  ErrorOr<Status> status(const Twine &Path) LLVM_OVERRIDE;
+  error_code openFileForRead(const Twine &Path,
+                             OwningPtr<File> &Result) LLVM_OVERRIDE;
+};
+
+ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
+  sys::fs::file_status RealStatus;
+  if (error_code EC = sys::fs::status(Path, RealStatus))
+    return EC;
+  Status Result(RealStatus);
+  Result.setName(Path.str());
+  Result.setExternalName(Path.str());
+  return Result;
+}
+
+error_code RealFileSystem::openFileForRead(const Twine &Name,
+                                           OwningPtr<File> &Result) {
+  int FD;
+  if (error_code EC = sys::fs::openFileForRead(Name, FD))
+    return EC;
+  Result.reset(new RealFile(FD));
+  return error_code::success();
+}
+
+IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
+  static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
+  return FS;
+}
+
+//===-----------------------------------------------------------------------===/
+// OverlayFileSystem implementation
+//===-----------------------------------------------------------------------===/
+OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
+  pushOverlay(BaseFS);
+}
+
+void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
+  FSList.push_back(FS);
+}
+
+ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
+  // FIXME: handle symlinks that cross file systems
+  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
+    ErrorOr<Status> Status = (*I)->status(Path);
+    if (Status || Status.getError() != errc::no_such_file_or_directory)
+      return Status;
+  }
+  return error_code(errc::no_such_file_or_directory, system_category());
+}
+
+error_code OverlayFileSystem::openFileForRead(const llvm::Twine &Path,
+                                              OwningPtr<File> &Result) {
+  // FIXME: handle symlinks that cross file systems
+  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
+    error_code EC = (*I)->openFileForRead(Path, Result);
+    if (!EC || EC != errc::no_such_file_or_directory)
+      return EC;
+  }
+  return error_code(errc::no_such_file_or_directory, system_category());
+}

Modified: cfe/trunk/lib/Frontend/ASTUnit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/ASTUnit.cpp?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/ASTUnit.cpp (original)
+++ cfe/trunk/lib/Frontend/ASTUnit.cpp Thu Feb 20 15:59:23 2014
@@ -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"
@@ -1418,7 +1418,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBuff
              REnd = PreprocessorOpts.remapped_file_end();
            !AnyFileChanged && R != REnd;
            ++R) {
-        llvm::sys::fs::file_status Status;
+        vfs::Status Status;
         if (FileMgr->getNoncachedStatValue(R->second, Status)) {
           // If we can't stat the file we're remapping to, assume that something
           // horrible happened.
@@ -1454,7 +1454,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBuff
         }
         
         // The file was not remapped; check whether it has changed on disk.
-        llvm::sys::fs::file_status Status;
+        vfs::Status Status;
         if (FileMgr->getNoncachedStatValue(F->first(), Status)) {
           // If we can't stat the file, assume that something horrible happened.
           AnyFileChanged = true;

Modified: cfe/trunk/lib/Frontend/CacheTokens.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CacheTokens.cpp?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CacheTokens.cpp (original)
+++ cfe/trunk/lib/Frontend/CacheTokens.cpp Thu Feb 20 15:59:23 2014
@@ -516,8 +516,8 @@ public:
   ~StatListener() {}
 
   LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                       int *FileDescriptor) {
-    LookupResult Result = statChained(Path, Data, isFile, FileDescriptor);
+                       vfs::File **F, vfs::FileSystem &FS) {
+    LookupResult Result = statChained(Path, Data, isFile, F, FS);
 
     if (Result == CacheMissing) // Failed 'stat'.
       PM.insert(PTHEntryKeyVariant(Path), PTHEntry());

Modified: cfe/trunk/lib/Frontend/CompilerInstance.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInstance.cpp?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInstance.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInstance.cpp Thu Feb 20 15:59:23 2014
@@ -79,6 +79,10 @@ void CompilerInstance::setTarget(TargetI
 
 void CompilerInstance::setFileManager(FileManager *Value) {
   FileMgr = Value;
+  if (Value)
+    VirtualFileSystem = Value->getVirtualFileSystem();
+  else
+    VirtualFileSystem.reset();
 }
 
 void CompilerInstance::setSourceManager(SourceManager *Value) {
@@ -197,7 +201,11 @@ CompilerInstance::createDiagnostics(Diag
 // File Manager
 
 void CompilerInstance::createFileManager() {
-  FileMgr = new FileManager(getFileSystemOpts());
+  if (!hasVirtualFileSystem()) {
+    // TODO: choose the virtual file system based on the CompilerInvocation.
+    setVirtualFileSystem(vfs::getRealFileSystem());
+  }
+  FileMgr = new FileManager(getFileSystemOpts(), VirtualFileSystem);
 }
 
 // Source Manager
@@ -867,6 +875,8 @@ static void compileModule(CompilerInstan
                                    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?

Modified: cfe/trunk/lib/Frontend/FrontendAction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/FrontendAction.cpp?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/FrontendAction.cpp (original)
+++ cfe/trunk/lib/Frontend/FrontendAction.cpp Thu Feb 20 15:59:23 2014
@@ -159,7 +159,6 @@ ASTConsumer* FrontendAction::CreateWrapp
   return new MultiplexConsumer(Consumers);
 }
 
-
 bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
                                      const FrontendInputFile &Input) {
   assert(!Instance && "Already processing a source file!");

Modified: cfe/trunk/lib/Lex/PTHLexer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PTHLexer.cpp?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/lib/Lex/PTHLexer.cpp (original)
+++ cfe/trunk/lib/Lex/PTHLexer.cpp Thu Feb 20 15:59:23 2014
@@ -675,13 +675,13 @@ public:
   ~PTHStatCache() {}
 
   LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                       int *FileDescriptor) {
+                       vfs::File **F, vfs::FileSystem &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, F, FS);
 
     const PTHStatData &D = *I;
 

Modified: cfe/trunk/lib/Serialization/ModuleManager.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ModuleManager.cpp?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ModuleManager.cpp (original)
+++ cfe/trunk/lib/Serialization/ModuleManager.cpp Thu Feb 20 15:59:23 2014
@@ -89,7 +89,7 @@ ModuleManager::addModule(StringRef FileN
     New->InputFilesValidationTimestamp = 0;
     if (New->Kind == MK_Module) {
       std::string TimestampFilename = New->getTimestampFilename();
-      llvm::sys::fs::file_status Status;
+      vfs::Status Status;
       // A cached stat value would be fine as well.
       if (!FileMgr.getNoncachedStatValue(TimestampFilename, Status))
         New->InputFilesValidationTimestamp =

Modified: cfe/trunk/unittests/Basic/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Basic/CMakeLists.txt?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/unittests/Basic/CMakeLists.txt (original)
+++ cfe/trunk/unittests/Basic/CMakeLists.txt Thu Feb 20 15:59:23 2014
@@ -6,6 +6,7 @@ add_clang_unittest(BasicTests
   CharInfoTest.cpp
   FileManagerTest.cpp
   SourceManagerTest.cpp
+  VirtualFileSystemTest.cpp
   )
 
 target_link_libraries(BasicTests

Modified: cfe/trunk/unittests/Basic/FileManagerTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Basic/FileManagerTest.cpp?rev=201818&r1=201817&r2=201818&view=diff
==============================================================================
--- cfe/trunk/unittests/Basic/FileManagerTest.cpp (original)
+++ cfe/trunk/unittests/Basic/FileManagerTest.cpp Thu Feb 20 15:59:23 2014
@@ -48,7 +48,7 @@ public:
 
   // Implement FileSystemStatCache::getStat().
   virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                               int *FileDescriptor) {
+                               vfs::File **F, vfs::FileSystem &FS) {
     if (StatCalls.count(Path) != 0) {
       Data = StatCalls[Path];
       return CacheExists;

Added: cfe/trunk/unittests/Basic/VirtualFileSystemTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Basic/VirtualFileSystemTest.cpp?rev=201818&view=auto
==============================================================================
--- cfe/trunk/unittests/Basic/VirtualFileSystemTest.cpp (added)
+++ cfe/trunk/unittests/Basic/VirtualFileSystemTest.cpp Thu Feb 20 15:59:23 2014
@@ -0,0 +1,216 @@
+//===- 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 "llvm/Support/Path.h"
+#include "gtest/gtest.h"
+#include <map>
+using namespace clang;
+using namespace llvm;
+using llvm::sys::fs::UniqueID;
+
+namespace {
+class DummyFileSystem : public vfs::FileSystem {
+  int FSID;   // used to produce UniqueIDs
+  int FileID; // used to produce UniqueIDs
+  std::map<std::string, vfs::Status> FilesAndDirs;
+
+  static int getNextFSID() {
+    static int Count = 0;
+    return Count++;
+  }
+
+public:
+  DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
+
+  ErrorOr<vfs::Status> status(const Twine &Path) {
+    std::map<std::string, vfs::Status>::iterator I =
+      FilesAndDirs.find(Path.str());
+    if (I == FilesAndDirs.end())
+      return error_code(errc::no_such_file_or_directory, posix_category());
+    return I->second;
+  }
+  error_code openFileForRead(const Twine &Path, OwningPtr<vfs::File> &Result) {
+    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 vfs::Status &Status) {
+    FilesAndDirs[Path] = Status;
+  }
+
+  void addRegularFile(StringRef Path, sys::fs::perms Perms=sys::fs::all_all) {
+    vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
+                  0, 0, 1024, sys::fs::file_type::regular_file, Perms);
+    addEntry(Path, S);
+  }
+
+  void addDirectory(StringRef Path, sys::fs::perms Perms=sys::fs::all_all) {
+    vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
+                  0, 0, 0, sys::fs::file_type::directory_file, Perms);
+    addEntry(Path, S);
+  }
+
+  void addSymlink(StringRef Path) {
+    vfs::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);
+  }
+};
+} // end anonymous namespace
+
+TEST(VirtualFileSystemTest, statusQueries) {
+  IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
+  ErrorOr<vfs::Status> Status((error_code()));
+
+  D->addRegularFile("/foo");
+  Status = D->status("/foo");
+  ASSERT_EQ(errc::success, Status.getError());
+  EXPECT_TRUE(Status->isStatusKnown());
+  EXPECT_FALSE(Status->isDirectory());
+  EXPECT_TRUE(Status->isRegularFile());
+  EXPECT_FALSE(Status->isSymlink());
+  EXPECT_FALSE(Status->isOther());
+  EXPECT_TRUE(Status->exists());
+
+  D->addDirectory("/bar");
+  Status = D->status("/bar");
+  ASSERT_EQ(errc::success, Status.getError());
+  EXPECT_TRUE(Status->isStatusKnown());
+  EXPECT_TRUE(Status->isDirectory());
+  EXPECT_FALSE(Status->isRegularFile());
+  EXPECT_FALSE(Status->isSymlink());
+  EXPECT_FALSE(Status->isOther());
+  EXPECT_TRUE(Status->exists());
+
+  D->addSymlink("/baz");
+  Status = D->status("/baz");
+  ASSERT_EQ(errc::success, Status.getError());
+  EXPECT_TRUE(Status->isStatusKnown());
+  EXPECT_FALSE(Status->isDirectory());
+  EXPECT_FALSE(Status->isRegularFile());
+  EXPECT_TRUE(Status->isSymlink());
+  EXPECT_FALSE(Status->isOther());
+  EXPECT_TRUE(Status->exists());
+
+  EXPECT_TRUE(Status->equivalent(*Status));
+  ErrorOr<vfs::Status> Status2 = D->status("/foo");
+  ASSERT_EQ(errc::success, Status2.getError());
+  EXPECT_FALSE(Status->equivalent(*Status2));
+}
+
+TEST(VirtualFileSystemTest, baseOnlyOverlay) {
+  IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
+  ErrorOr<vfs::Status> Status((error_code()));
+  EXPECT_FALSE(Status = D->status("/foo"));
+
+  IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
+  EXPECT_FALSE(Status = O->status("/foo"));
+
+  D->addRegularFile("/foo");
+  Status = D->status("/foo");
+  EXPECT_EQ(errc::success, Status.getError());
+
+  ErrorOr<vfs::Status> Status2((error_code()));
+  Status2 = O->status("/foo");
+  EXPECT_EQ(errc::success, Status2.getError());
+  EXPECT_TRUE(Status->equivalent(*Status2));
+}
+
+TEST(VirtualFileSystemTest, overlayFiles) {
+  IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
+  IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
+  IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
+  IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(Base));
+  O->pushOverlay(Middle);
+  O->pushOverlay(Top);
+
+  ErrorOr<vfs::Status> Status1((error_code())), Status2((error_code())),
+                       Status3((error_code())), StatusB((error_code())),
+                       StatusM((error_code())), StatusT((error_code()));
+
+  Base->addRegularFile("/foo");
+  StatusB = Base->status("/foo");
+  ASSERT_EQ(errc::success, StatusB.getError());
+  Status1 = O->status("/foo");
+  ASSERT_EQ(errc::success, Status1.getError());
+  Middle->addRegularFile("/foo");
+  StatusM = Middle->status("/foo");
+  ASSERT_EQ(errc::success, StatusM.getError());
+  Status2 = O->status("/foo");
+  ASSERT_EQ(errc::success, Status2.getError());
+  Top->addRegularFile("/foo");
+  StatusT = Top->status("/foo");
+  ASSERT_EQ(errc::success, StatusT.getError());
+  Status3 = O->status("/foo");
+  ASSERT_EQ(errc::success, Status3.getError());
+
+  EXPECT_TRUE(Status1->equivalent(*StatusB));
+  EXPECT_TRUE(Status2->equivalent(*StatusM));
+  EXPECT_TRUE(Status3->equivalent(*StatusT));
+
+  EXPECT_FALSE(Status1->equivalent(*Status2));
+  EXPECT_FALSE(Status2->equivalent(*Status3));
+  EXPECT_FALSE(Status1->equivalent(*Status3));
+}
+
+TEST(VirtualFileSystemTest, overlayDirsNonMerged) {
+  IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
+  IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
+  IntrusiveRefCntPtr<vfs::OverlayFileSystem>
+    O(new vfs::OverlayFileSystem(Lower));
+  O->pushOverlay(Upper);
+
+  Lower->addDirectory("/lower-only");
+  Upper->addDirectory("/upper-only");
+
+  // non-merged paths should be the same
+  ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
+  ASSERT_EQ(errc::success, Status1.getError());
+  ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
+  ASSERT_EQ(errc::success, Status2.getError());
+  EXPECT_TRUE(Status1->equivalent(*Status2));
+
+  Status1 = Upper->status("/upper-only");
+  ASSERT_EQ(errc::success, Status1.getError());
+  Status2 = O->status("/upper-only");
+  ASSERT_EQ(errc::success, Status2.getError());
+  EXPECT_TRUE(Status1->equivalent(*Status2));
+}
+
+TEST(VirtualFileSystemTest, mergedDirPermissions) {
+  // merged directories get the permissions of the upper dir
+  IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
+  IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
+  IntrusiveRefCntPtr<vfs::OverlayFileSystem>
+    O(new vfs::OverlayFileSystem(Lower));
+  O->pushOverlay(Upper);
+
+  ErrorOr<vfs::Status> Status((error_code()));
+  Lower->addDirectory("/both", sys::fs::owner_read);
+  Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
+  Status = O->status("/both");
+  ASSERT_EQ(errc::success, Status.getError());
+  EXPECT_EQ(0740, Status->getPermissions());
+
+  // permissions (as usual) are not recursively applied
+  Lower->addRegularFile("/both/foo", sys::fs::owner_read);
+  Upper->addRegularFile("/both/bar", sys::fs::owner_write);
+  Status = O->status("/both/foo");
+  ASSERT_EQ(errc::success, Status.getError());
+  EXPECT_EQ(0400, Status->getPermissions());
+  Status = O->status("/both/bar");
+  ASSERT_EQ(errc::success, Status.getError());
+  EXPECT_EQ(0200, Status->getPermissions());
+}





More information about the cfe-commits mailing list