This revision was automatically updated to reflect the committed changes.
Closed by commit rLLDB345783: [FileSystem] Extend file system and have it use 
the VFS. (authored by JDevlieghere, committed by ).
Herald added subscribers: teemperor, abidh.

Changed prior to commit:
  https://reviews.llvm.org/D53532?vs=171845&id=172027#toc

Repository:
  rLLDB LLDB

https://reviews.llvm.org/D53532

Files:
  include/lldb/Host/FileSystem.h
  include/lldb/Utility/FileSpec.h
  source/Core/Disassembler.cpp
  source/Core/Module.cpp
  source/Core/ModuleList.cpp
  source/Core/SourceManager.cpp
  source/Host/common/FileSystem.cpp
  source/Host/common/HostInfoBase.cpp
  source/Host/common/Symbols.cpp
  source/Host/posix/HostProcessPosix.cpp
  source/Initialization/SystemInitializerCommon.cpp
  source/Interpreter/OptionValueFileSpec.cpp
  source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
  source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp
  source/Plugins/Platform/Android/AdbClient.cpp
  source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
  source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
  source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
  source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
  source/Target/Platform.cpp
  unittests/Core/MangledTest.cpp
  unittests/Expression/ClangParserTest.cpp
  unittests/Host/FileSystemTest.cpp
  unittests/Host/HostInfoTest.cpp
  unittests/Host/SymbolsTest.cpp
  unittests/ObjectFile/ELF/TestObjectFileELF.cpp
  unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
  unittests/Symbol/TestClangASTContext.cpp
  unittests/Symbol/TestDWARFCallFrameInfo.cpp
  unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp
  unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp
  unittests/Target/ModuleCacheTest.cpp

Index: unittests/ObjectFile/ELF/TestObjectFileELF.cpp
===================================================================
--- unittests/ObjectFile/ELF/TestObjectFileELF.cpp
+++ unittests/ObjectFile/ELF/TestObjectFileELF.cpp
@@ -14,6 +14,7 @@
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
 #include "lldb/Core/Section.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/Support/Compression.h"
@@ -29,6 +30,7 @@
 class ObjectFileELFTest : public testing::Test {
 public:
   void SetUp() override {
+    FileSystem::Initialize();
     HostInfo::Initialize();
     ObjectFileELF::Initialize();
     SymbolVendorELF::Initialize();
@@ -38,6 +40,7 @@
     SymbolVendorELF::Terminate();
     ObjectFileELF::Terminate();
     HostInfo::Terminate();
+    FileSystem::Terminate();
   }
 
 protected:
Index: unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp
===================================================================
--- unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp
+++ unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp
@@ -22,6 +22,7 @@
 #include "lldb/Core/Address.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Symbol/ClangASTContext.h"
 #include "lldb/Symbol/CompileUnit.h"
@@ -49,6 +50,7 @@
     ::CoInitializeEx(nullptr, COINIT_MULTITHREADED);
 #endif
 
+    FileSystem::Initialize();
     HostInfo::Initialize();
     ObjectFilePECOFF::Initialize();
     SymbolFileDWARF::Initialize();
@@ -65,6 +67,7 @@
     SymbolFileDWARF::Terminate();
     ObjectFilePECOFF::Terminate();
     HostInfo::Terminate();
+    FileSystem::Terminate();
 
 #if defined(_MSC_VER)
     ::CoUninitialize();
Index: unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp
===================================================================
--- unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp
+++ unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp
@@ -22,6 +22,7 @@
 #include "lldb/Core/Address.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Symbol/ClangASTContext.h"
 #include "lldb/Symbol/CompileUnit.h"
@@ -38,21 +39,23 @@
 // Initialize and TearDown the plugin every time, so we get a brand new
 // AST every time so that modifications to the AST from each test don't
 // leak into the next test.
-    HostInfo::Initialize();
-    ObjectFilePECOFF::Initialize();
-    SymbolFileDWARF::Initialize();
-    ClangASTContext::Initialize();
-    SymbolFilePDB::Initialize();
+FileSystem::Initialize();
+HostInfo::Initialize();
+ObjectFilePECOFF::Initialize();
+SymbolFileDWARF::Initialize();
+ClangASTContext::Initialize();
+SymbolFilePDB::Initialize();
 
-    m_dwarf_test_exe = GetInputFilePath("test-dwarf.exe");
+m_dwarf_test_exe = GetInputFilePath("test-dwarf.exe");
   }
 
   void TearDown() override {
     SymbolFilePDB::Terminate();
     ClangASTContext::Initialize();
     SymbolFileDWARF::Terminate();
     ObjectFilePECOFF::Terminate();
     HostInfo::Terminate();
+    FileSystem::Terminate();
   }
 
 protected:
Index: unittests/Symbol/TestClangASTContext.cpp
===================================================================
--- unittests/Symbol/TestClangASTContext.cpp
+++ unittests/Symbol/TestClangASTContext.cpp
@@ -13,6 +13,7 @@
 
 #include "clang/AST/DeclCXX.h"
 
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Symbol/ClangASTContext.h"
 #include "lldb/Symbol/ClangUtil.h"
@@ -25,9 +26,15 @@
 
 class TestClangASTContext : public testing::Test {
 public:
-  static void SetUpTestCase() { HostInfo::Initialize(); }
+  static void SetUpTestCase() {
+    FileSystem::Initialize();
+    HostInfo::Initialize();
+  }
 
-  static void TearDownTestCase() { HostInfo::Terminate(); }
+  static void TearDownTestCase() {
+    HostInfo::Terminate();
+    FileSystem::Terminate();
+  }
 
   virtual void SetUp() override {
     std::string triple = HostInfo::GetTargetTriple();
@@ -357,7 +364,7 @@
   RecordDecl *empty_derived_non_empty_base_decl =
       ClangASTContext::GetAsRecordDecl(empty_derived);
   EXPECT_EQ(1u, ClangASTContext::GetNumBaseClasses(
-                   empty_derived_non_empty_base_cxx_decl, false));
+                    empty_derived_non_empty_base_cxx_decl, false));
   EXPECT_TRUE(
       ClangASTContext::RecordHasFields(empty_derived_non_empty_base_decl));
 
@@ -380,7 +387,7 @@
   RecordDecl *empty_derived_non_empty_vbase_decl =
       ClangASTContext::GetAsRecordDecl(empty_derived2);
   EXPECT_EQ(1u, ClangASTContext::GetNumBaseClasses(
-                   empty_derived_non_empty_vbase_cxx_decl, false));
+                    empty_derived_non_empty_vbase_cxx_decl, false));
   EXPECT_TRUE(
       ClangASTContext::RecordHasFields(empty_derived_non_empty_vbase_decl));
 }
@@ -420,7 +427,7 @@
                              clang::AutoTypeKeyword::Auto, false));
 
   CompilerType int_type(m_ast->getASTContext(), m_ast->getASTContext()->IntTy);
-  for(CompilerType t: { type, typedef_type, auto_type }) {
+  for (CompilerType t : {type, typedef_type, auto_type}) {
     SCOPED_TRACE(t.GetTypeName().AsCString());
 
     EXPECT_EQ(m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 0),
Index: unittests/Symbol/TestDWARFCallFrameInfo.cpp
===================================================================
--- unittests/Symbol/TestDWARFCallFrameInfo.cpp
+++ unittests/Symbol/TestDWARFCallFrameInfo.cpp
@@ -8,34 +8,40 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "gtest/gtest.h"
+
 #include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
 #include "Plugins/Process/Utility/RegisterContext_x86.h"
+#include "TestingSupport/TestUtilities.h"
+
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
 #include "lldb/Core/Section.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Symbol/DWARFCallFrameInfo.h"
 #include "lldb/Utility/StreamString.h"
-#include "TestingSupport/TestUtilities.h"
+
 #include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Program.h"
 #include "llvm/Support/raw_ostream.h"
-#include "gtest/gtest.h"
 
 using namespace lldb_private;
 using namespace lldb;
 
 class DWARFCallFrameInfoTest : public testing::Test {
 public:
   void SetUp() override {
+    FileSystem::Initialize();
     HostInfo::Initialize();
     ObjectFileELF::Initialize();
   }
 
   void TearDown() override {
     ObjectFileELF::Terminate();
     HostInfo::Terminate();
+    FileSystem::Terminate();
   }
 
 protected:
Index: unittests/Expression/ClangParserTest.cpp
===================================================================
--- unittests/Expression/ClangParserTest.cpp
+++ unittests/Expression/ClangParserTest.cpp
@@ -9,6 +9,7 @@
 
 #include "Plugins/ExpressionParser/Clang/ClangHost.h"
 #include "TestingSupport/TestUtilities.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/lldb-defines.h"
@@ -18,8 +19,14 @@
 
 namespace {
 struct ClangHostTest : public testing::Test {
-  static void SetUpTestCase() { HostInfo::Initialize(); }
-  static void TearDownTestCase() { HostInfo::Terminate(); }
+  static void SetUpTestCase() {
+    FileSystem::Initialize();
+    HostInfo::Initialize();
+  }
+  static void TearDownTestCase() {
+    HostInfo::Terminate();
+    FileSystem::Terminate();
+  }
 };
 } // namespace
 
Index: unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
===================================================================
--- unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -7,17 +7,19 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "Plugins/ScriptInterpreter/Python/lldb-python.h"
 #include "gtest/gtest.h"
 
 #include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h"
+#include "Plugins/ScriptInterpreter/Python/lldb-python.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 
 #include "PythonTestSuite.h"
 
 using namespace lldb_private;
 
 void PythonTestSuite::SetUp() {
+  FileSystem::Initialize();
   HostInfoBase::Initialize();
   // ScriptInterpreterPython::Initialize() depends on HostInfo being
   // initializedso it can compute the python directory etc.
Index: unittests/Core/MangledTest.cpp
===================================================================
--- unittests/Core/MangledTest.cpp
+++ unittests/Core/MangledTest.cpp
@@ -14,6 +14,7 @@
 #include "lldb/Core/Mangled.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Symbol/SymbolContext.h"
 
@@ -61,6 +62,7 @@
   }
 
 TEST(MangledTest, NameIndexes_FindFunctionSymbols) {
+  FileSystem::Initialize();
   HostInfo::Initialize();
   ObjectFileELF::Initialize();
   SymbolVendorELF::Initialize();
@@ -167,4 +169,5 @@
   SymbolVendorELF::Terminate();
   ObjectFileELF::Terminate();
   HostInfo::Terminate();
+  FileSystem::Terminate();
 }
Index: unittests/Target/ModuleCacheTest.cpp
===================================================================
--- unittests/Target/ModuleCacheTest.cpp
+++ unittests/Target/ModuleCacheTest.cpp
@@ -5,12 +5,13 @@
 #include "llvm/Support/Path.h"
 
 #include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "TestingSupport/TestUtilities.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Symbol/SymbolContext.h"
 #include "lldb/Target/ModuleCache.h"
-#include "TestingSupport/TestUtilities.h"
 
 using namespace lldb_private;
 using namespace lldb;
@@ -65,6 +66,7 @@
 }
 
 void ModuleCacheTest::SetUpTestCase() {
+  FileSystem::Initialize();
   HostInfo::Initialize();
   ObjectFileELF::Initialize();
 
@@ -75,6 +77,7 @@
 void ModuleCacheTest::TearDownTestCase() {
   ObjectFileELF::Terminate();
   HostInfo::Terminate();
+  FileSystem::Terminate();
 }
 
 static void VerifyDiskState(const FileSpec &cache_dir, const char *hostname) {
Index: unittests/Host/SymbolsTest.cpp
===================================================================
--- unittests/Host/SymbolsTest.cpp
+++ unittests/Host/SymbolsTest.cpp
@@ -9,20 +9,37 @@
 
 #include "gtest/gtest.h"
 
-#include "lldb/Host/Symbols.h"
 #include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/Symbols.h"
 
 using namespace lldb_private;
 
-TEST(SymbolsTest,
-     LocateExecutableSymbolFileForUnknownExecutableAndUnknownSymbolFile) {
+namespace {
+class SymbolsTest : public ::testing::Test {
+public:
+  void SetUp() override {
+    FileSystem::Initialize();
+    HostInfo::Initialize();
+  }
+  void TearDown() override {
+    HostInfo::Terminate();
+    FileSystem::Terminate();
+  }
+};
+} // namespace
+
+TEST_F(
+    SymbolsTest,
+    TerminateLocateExecutableSymbolFileForUnknownExecutableAndUnknownSymbolFile) {
   ModuleSpec module_spec;
   FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec);
   EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty());
 }
 
-TEST(SymbolsTest,
-     LocateExecutableSymbolFileForUnknownExecutableAndMissingSymbolFile) {
+TEST_F(SymbolsTest,
+       LocateExecutableSymbolFileForUnknownExecutableAndMissingSymbolFile) {
   ModuleSpec module_spec;
   // using a GUID here because the symbol file shouldn't actually exist on disk
   module_spec.GetSymbolFileSpec().SetFile(
Index: unittests/Host/FileSystemTest.cpp
===================================================================
--- unittests/Host/FileSystemTest.cpp
+++ unittests/Host/FileSystemTest.cpp
@@ -7,13 +7,154 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 #include "lldb/Host/FileSystem.h"
+#include "llvm/Support/Errc.h"
 
 extern const char *TestMainArgv0;
 
 using namespace lldb_private;
+using namespace llvm;
+using llvm::sys::fs::UniqueID;
+
+// Modified from llvm/unittests/Support/VirtualFileSystemTest.cpp
+namespace {
+struct DummyFile : public vfs::File {
+  vfs::Status S;
+  explicit DummyFile(vfs::Status S) : S(S) {}
+  llvm::ErrorOr<vfs::Status> status() override { return S; }
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
+            bool IsVolatile) override {
+    llvm_unreachable("unimplemented");
+  }
+  std::error_code close() override { return std::error_code(); }
+};
+
+class DummyFileSystem : public vfs::FileSystem {
+  int FSID;   // used to produce UniqueIDs
+  int FileID; // used to produce UniqueIDs
+  std::string cwd;
+  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) override {
+    std::map<std::string, vfs::Status>::iterator I =
+        FilesAndDirs.find(Path.str());
+    if (I == FilesAndDirs.end())
+      return make_error_code(llvm::errc::no_such_file_or_directory);
+    return I->second;
+  }
+  ErrorOr<std::unique_ptr<vfs::File>>
+  openFileForRead(const Twine &Path) override {
+    auto S = status(Path);
+    if (S)
+      return std::unique_ptr<vfs::File>(new DummyFile{*S});
+    return S.getError();
+  }
+  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
+    return cwd;
+  }
+  std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
+    cwd = Path.str();
+    return std::error_code();
+  }
+  // Map any symlink to "/symlink".
+  std::error_code getRealPath(const Twine &Path,
+                              SmallVectorImpl<char> &Output) const override {
+    auto I = FilesAndDirs.find(Path.str());
+    if (I == FilesAndDirs.end())
+      return make_error_code(llvm::errc::no_such_file_or_directory);
+    if (I->second.isSymlink()) {
+      Output.clear();
+      Twine("/symlink").toVector(Output);
+      return std::error_code();
+    }
+    Output.clear();
+    Path.toVector(Output);
+    return std::error_code();
+  }
+
+  struct DirIterImpl : public llvm::vfs::detail::DirIterImpl {
+    std::map<std::string, vfs::Status> &FilesAndDirs;
+    std::map<std::string, vfs::Status>::iterator I;
+    std::string Path;
+    bool isInPath(StringRef S) {
+      if (Path.size() < S.size() && S.find(Path) == 0) {
+        auto LastSep = S.find_last_of('/');
+        if (LastSep == Path.size() || LastSep == Path.size() - 1)
+          return true;
+      }
+      return false;
+    }
+    DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
+                const Twine &_Path)
+        : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
+          Path(_Path.str()) {
+      for (; I != FilesAndDirs.end(); ++I) {
+        if (isInPath(I->first)) {
+          CurrentEntry =
+              vfs::directory_entry(I->second.getName(), I->second.getType());
+          break;
+        }
+      }
+    }
+    std::error_code increment() override {
+      ++I;
+      for (; I != FilesAndDirs.end(); ++I) {
+        if (isInPath(I->first)) {
+          CurrentEntry =
+              vfs::directory_entry(I->second.getName(), I->second.getType());
+          break;
+        }
+      }
+      if (I == FilesAndDirs.end())
+        CurrentEntry = vfs::directory_entry();
+      return std::error_code();
+    }
+  };
+
+  vfs::directory_iterator dir_begin(const Twine &Dir,
+                                    std::error_code &EC) override {
+    return vfs::directory_iterator(
+        std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
+  }
+
+  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, UniqueID(FSID, FileID++),
+                  std::chrono::system_clock::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, UniqueID(FSID, FileID++),
+                  std::chrono::system_clock::now(), 0, 0, 0,
+                  sys::fs::file_type::directory_file, Perms);
+    addEntry(Path, S);
+  }
+
+  void addSymlink(StringRef Path) {
+    vfs::Status S(Path, UniqueID(FSID, FileID++),
+                  std::chrono::system_clock::now(), 0, 0, 0,
+                  sys::fs::file_type::symlink_file, sys::fs::all_all);
+    addEntry(Path, S);
+  }
+};
+} // namespace
 
 TEST(FileSystemTest, FileAndDirectoryComponents) {
   using namespace std::chrono;
@@ -26,7 +167,125 @@
 #endif
   FileSpec fs2(TestMainArgv0, resolve);
 
-  EXPECT_EQ(system_clock::time_point(), FileSystem::GetModificationTime(fs1));
+  FileSystem fs;
+
+  EXPECT_EQ(system_clock::time_point(), fs.GetModificationTime(fs1));
   EXPECT_LT(system_clock::time_point() + hours(24 * 365 * 20),
-            FileSystem::GetModificationTime(fs2));
+            fs.GetModificationTime(fs2));
+}
+
+static IntrusiveRefCntPtr<DummyFileSystem> GetSimpleDummyFS() {
+  IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
+  D->addRegularFile("/foo");
+  D->addDirectory("/bar");
+  D->addSymlink("/baz");
+  D->addRegularFile("/qux", ~sys::fs::perms::all_read);
+  D->setCurrentWorkingDirectory("/");
+  return D;
+}
+
+TEST(FileSystemTest, Exists) {
+  FileSystem fs(GetSimpleDummyFS());
+
+  EXPECT_TRUE(fs.Exists("/foo"));
+  EXPECT_TRUE(fs.Exists(FileSpec("/foo", false, FileSpec::Style::posix)));
+}
+
+TEST(FileSystemTest, Readable) {
+  FileSystem fs(GetSimpleDummyFS());
+
+  EXPECT_TRUE(fs.Readable("/foo"));
+  EXPECT_TRUE(fs.Readable(FileSpec("/foo", false, FileSpec::Style::posix)));
+
+  EXPECT_FALSE(fs.Readable("/qux"));
+  EXPECT_FALSE(fs.Readable(FileSpec("/qux", false, FileSpec::Style::posix)));
+}
+
+TEST(FileSystemTest, GetByteSize) {
+  FileSystem fs(GetSimpleDummyFS());
+
+  EXPECT_EQ((uint64_t)1024, fs.GetByteSize("/foo"));
+  EXPECT_EQ((uint64_t)1024,
+            fs.GetByteSize(FileSpec("/foo", false, FileSpec::Style::posix)));
+}
+
+TEST(FileSystemTest, GetPermissions) {
+  FileSystem fs(GetSimpleDummyFS());
+
+  EXPECT_EQ(sys::fs::all_all, fs.GetPermissions("/foo"));
+  EXPECT_EQ(sys::fs::all_all,
+            fs.GetPermissions(FileSpec("/foo", false, FileSpec::Style::posix)));
+}
+
+TEST(FileSystemTest, MakeAbsolute) {
+  FileSystem fs(GetSimpleDummyFS());
+
+  {
+    StringRef foo_relative = "foo";
+    SmallString<16> foo(foo_relative);
+    auto EC = fs.MakeAbsolute(foo);
+    EXPECT_FALSE(EC);
+    EXPECT_TRUE(foo.equals("/foo"));
+  }
+
+  {
+    FileSpec file_spec("foo", false);
+    auto EC = fs.MakeAbsolute(file_spec);
+    EXPECT_FALSE(EC);
+    EXPECT_EQ("/foo", file_spec.GetPath());
+  }
+}
+
+TEST(FileSystemTest, Resolve) {
+  FileSystem fs(GetSimpleDummyFS());
+
+  {
+    StringRef foo_relative = "foo";
+    SmallString<16> foo(foo_relative);
+    fs.Resolve(foo);
+    EXPECT_TRUE(foo.equals("/foo"));
+  }
+
+  {
+    FileSpec file_spec("foo", false);
+    fs.Resolve(file_spec);
+    EXPECT_EQ("/foo", file_spec.GetPath());
+  }
+
+  {
+    StringRef foo_relative = "bogus";
+    SmallString<16> foo(foo_relative);
+    fs.Resolve(foo);
+    EXPECT_TRUE(foo.equals("bogus"));
+  }
+
+  {
+    FileSpec file_spec("bogus", false);
+    fs.Resolve(file_spec);
+    EXPECT_EQ("bogus", file_spec.GetPath());
+  }
+}
+
+FileSystem::EnumerateDirectoryResult
+VFSCallback(void *baton, llvm::sys::fs::file_type file_type,
+            llvm::StringRef path) {
+  auto visited = static_cast<std::vector<std::string> *>(baton);
+  visited->push_back(path.str());
+  return FileSystem::eEnumerateDirectoryResultNext;
+}
+
+TEST(FileSystemTest, EnumerateDirectory) {
+  FileSystem fs(GetSimpleDummyFS());
+
+  std::vector<std::string> visited;
+
+  constexpr bool find_directories = true;
+  constexpr bool find_files = true;
+  constexpr bool find_other = true;
+
+  fs.EnumerateDirectory("/", find_directories, find_files, find_other,
+                        VFSCallback, &visited);
+
+  EXPECT_THAT(visited,
+              testing::UnorderedElementsAre("/foo", "/bar", "/baz", "/qux"));
 }
Index: unittests/Host/HostInfoTest.cpp
===================================================================
--- unittests/Host/HostInfoTest.cpp
+++ unittests/Host/HostInfoTest.cpp
@@ -8,20 +8,27 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/Host/HostInfo.h"
-#include "lldb/lldb-defines.h"
 #include "TestingSupport/TestUtilities.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/lldb-defines.h"
 #include "gtest/gtest.h"
 
 using namespace lldb_private;
 using namespace llvm;
 
 namespace {
-class HostInfoTest: public ::testing::Test {
-  public:
-    void SetUp() override { HostInfo::Initialize(); }
-    void TearDown() override { HostInfo::Terminate(); }
+class HostInfoTest : public ::testing::Test {
+public:
+  void SetUp() override {
+    FileSystem::Initialize();
+    HostInfo::Initialize();
+  }
+  void TearDown() override {
+    HostInfo::Terminate();
+    FileSystem::Terminate();
+  }
 };
-}
+} // namespace
 
 TEST_F(HostInfoTest, GetAugmentedArchSpec) {
   // Fully specified triple should not be changed.
Index: source/Core/Disassembler.cpp
===================================================================
--- source/Core/Disassembler.cpp
+++ source/Core/Disassembler.cpp
@@ -934,7 +934,7 @@
     out_stream->Printf("Instruction::TestEmulation:  Missing file_name.");
     return false;
   }
-  FILE *test_file = FileSystem::Fopen(file_name, "r");
+  FILE *test_file = FileSystem::Instance().Fopen(file_name, "r");
   if (!test_file) {
     out_stream->Printf(
         "Instruction::TestEmulation: Attempt to open test file failed.");
Index: source/Core/SourceManager.cpp
===================================================================
--- source/Core/SourceManager.cpp
+++ source/Core/SourceManager.cpp
@@ -370,14 +370,14 @@
 SourceManager::File::File(const FileSpec &file_spec,
                           lldb::DebuggerSP debugger_sp)
     : m_file_spec_orig(file_spec), m_file_spec(file_spec),
-      m_mod_time(FileSystem::GetModificationTime(file_spec)),
+      m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)),
       m_debugger_wp(debugger_sp) {
   CommonInitializer(file_spec, nullptr);
 }
 
 SourceManager::File::File(const FileSpec &file_spec, Target *target)
     : m_file_spec_orig(file_spec), m_file_spec(file_spec),
-      m_mod_time(FileSystem::GetModificationTime(file_spec)),
+      m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)),
       m_debugger_wp(target ? target->GetDebugger().shared_from_this()
                            : DebuggerSP()) {
   CommonInitializer(file_spec, target);
@@ -422,7 +422,7 @@
             SymbolContext sc;
             sc_list.GetContextAtIndex(0, sc);
             m_file_spec = sc.comp_unit;
-            m_mod_time = FileSystem::GetModificationTime(m_file_spec);
+            m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
           }
         }
       }
@@ -435,7 +435,7 @@
         if (target->GetSourcePathMap().FindFile(m_file_spec, new_file_spec) ||
             target->GetImages().FindSourceFile(m_file_spec, new_file_spec)) {
           m_file_spec = new_file_spec;
-          m_mod_time = FileSystem::GetModificationTime(m_file_spec);
+          m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
         }
       }
     }
@@ -515,7 +515,7 @@
   // TODO: use host API to sign up for file modifications to anything in our
   // source cache and only update when we determine a file has been updated.
   // For now we check each time we want to display info for the file.
-  auto curr_mod_time = FileSystem::GetModificationTime(m_file_spec);
+  auto curr_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
 
   if (curr_mod_time != llvm::sys::TimePoint<>() &&
       m_mod_time != curr_mod_time) {
Index: source/Core/Module.cpp
===================================================================
--- source/Core/Module.cpp
+++ source/Core/Module.cpp
@@ -171,10 +171,10 @@
   }
 
   if (module_spec.GetFileSpec())
-    m_mod_time = FileSystem::GetModificationTime(module_spec.GetFileSpec());
+    m_mod_time = FileSystem::Instance().GetModificationTime(module_spec.GetFileSpec());
   else if (matching_module_spec.GetFileSpec())
     m_mod_time =
-        FileSystem::GetModificationTime(matching_module_spec.GetFileSpec());
+        FileSystem::Instance().GetModificationTime(matching_module_spec.GetFileSpec());
 
   // Copy the architecture from the actual spec if we got one back, else use
   // the one that was specified
@@ -219,7 +219,7 @@
 Module::Module(const FileSpec &file_spec, const ArchSpec &arch,
                const ConstString *object_name, lldb::offset_t object_offset,
                const llvm::sys::TimePoint<> &object_mod_time)
-    : m_mod_time(FileSystem::GetModificationTime(file_spec)), m_arch(arch),
+    : m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)), m_arch(arch),
       m_file(file_spec), m_object_offset(object_offset),
       m_object_mod_time(object_mod_time), m_file_has_changed(false),
       m_first_file_changed_log(false) {
@@ -1062,7 +1062,7 @@
   // Container objects whose paths do not specify a file directly can call this
   // function to correct the file and object names.
   m_file = file;
-  m_mod_time = FileSystem::GetModificationTime(file);
+  m_mod_time = FileSystem::Instance().GetModificationTime(file);
   m_object_name = object_name;
 }
 
@@ -1125,7 +1125,7 @@
 bool Module::FileHasChanged() const {
   if (!m_file_has_changed)
     m_file_has_changed =
-        (FileSystem::GetModificationTime(m_file) != m_mod_time);
+        (FileSystem::Instance().GetModificationTime(m_file) != m_mod_time);
   return m_file_has_changed;
 }
 
Index: source/Core/ModuleList.cpp
===================================================================
--- source/Core/ModuleList.cpp
+++ source/Core/ModuleList.cpp
@@ -952,7 +952,7 @@
       // If we didn't have a UUID in mind when looking for the object file,
       // then we should make sure the modification time hasn't changed!
       if (platform_module_spec.GetUUIDPtr() == nullptr) {
-        auto file_spec_mod_time = FileSystem::GetModificationTime(
+        auto file_spec_mod_time = FileSystem::Instance().GetModificationTime(
             located_binary_modulespec.GetFileSpec());
         if (file_spec_mod_time != llvm::sys::TimePoint<>()) {
           if (file_spec_mod_time != module_sp->GetModificationTime()) {
Index: source/Initialization/SystemInitializerCommon.cpp
===================================================================
--- source/Initialization/SystemInitializerCommon.cpp
+++ source/Initialization/SystemInitializerCommon.cpp
@@ -15,6 +15,7 @@
 #include "Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h"
 #include "Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h"
 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Utility/Log.h"
@@ -62,6 +63,7 @@
   }
 #endif
 
+  FileSystem::Initialize();
   Log::Initialize();
   HostInfo::Initialize();
   static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
@@ -106,4 +108,5 @@
 
   HostInfo::Terminate();
   Log::DisableAllLogChannels();
+  FileSystem::Terminate();
 }
Index: source/Target/Platform.cpp
===================================================================
--- source/Target/Platform.cpp
+++ source/Target/Platform.cpp
@@ -603,7 +603,7 @@
 
     FileSpec src_resolved;
 
-    rc_baton->error = FileSystem::Readlink(src, src_resolved);
+    rc_baton->error = FileSystem::Instance().Readlink(src, src_resolved);
 
     if (rc_baton->error.Fail())
       return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out
@@ -733,7 +733,7 @@
     case fs::file_type::symlink_file: {
       llvm::sys::fs::remove(fixed_dst.GetPath());
       FileSpec src_resolved;
-      error = FileSystem::Readlink(src, src_resolved);
+      error = FileSystem::Instance().Readlink(src, src_resolved);
       if (error.Success())
         error = CreateSymlink(dst, src_resolved);
     } break;
Index: source/Interpreter/OptionValueFileSpec.cpp
===================================================================
--- source/Interpreter/OptionValueFileSpec.cpp
+++ source/Interpreter/OptionValueFileSpec.cpp
@@ -109,7 +109,7 @@
 
 const lldb::DataBufferSP &OptionValueFileSpec::GetFileContents() {
   if (m_current_value) {
-    const auto file_mod_time = FileSystem::GetModificationTime(m_current_value);
+    const auto file_mod_time = FileSystem::Instance().GetModificationTime(m_current_value);
     if (m_data_sp && m_data_mod_time == file_mod_time)
       return m_data_sp;
     m_data_sp = DataBufferLLVM::CreateFromPath(m_current_value.GetPath());
Index: source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
===================================================================
--- source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
+++ source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
@@ -455,7 +455,7 @@
 
 Status PlatformPOSIX::CreateSymlink(const FileSpec &src, const FileSpec &dst) {
   if (IsHost())
-    return FileSystem::Symlink(src, dst);
+    return FileSystem::Instance().Symlink(src, dst);
   else if (m_remote_platform_sp)
     return m_remote_platform_sp->CreateSymlink(src, dst);
   else
Index: source/Plugins/Platform/Android/AdbClient.cpp
===================================================================
--- source/Plugins/Platform/Android/AdbClient.cpp
+++ source/Plugins/Platform/Android/AdbClient.cpp
@@ -484,7 +484,7 @@
       return Status("Failed to send file chunk: %s", error.AsCString());
   }
   error = SendSyncRequest(
-      kDONE, llvm::sys::toTimeT(FileSystem::GetModificationTime(local_file)),
+      kDONE, llvm::sys::toTimeT(FileSystem::Instance().GetModificationTime(local_file)),
       nullptr);
   if (error.Fail())
     return error;
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -698,7 +698,7 @@
   packet.GetHexByteStringTerminatedBy(dst, ',');
   packet.GetChar(); // Skip ',' char
   packet.GetHexByteString(src);
-  Status error = FileSystem::Symlink(FileSpec{src, true}, FileSpec{dst, false});
+  Status error = FileSystem::Instance().Symlink(FileSpec{src, true}, FileSpec{dst, false});
   StreamString response;
   response.Printf("F%u,%u", error.GetError(), error.GetError());
   return SendPacketNoLock(response.GetString());
Index: source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
===================================================================
--- source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
+++ source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
@@ -115,7 +115,7 @@
     // No UUID, we must rely upon the cached module modification time and the
     // modification time of the file on disk
     if (module_sp->GetModificationTime() !=
-        FileSystem::GetModificationTime(module_sp->GetFileSpec()))
+        FileSystem::Instance().GetModificationTime(module_sp->GetFileSpec()))
       module_sp.reset();
   }
 
Index: source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp
===================================================================
--- source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp
+++ source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp
@@ -461,7 +461,7 @@
     return 0;
 
   const size_t initial_count = specs.GetSize();
-  llvm::sys::TimePoint<> file_mod_time = FileSystem::GetModificationTime(file);
+  llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file);
   Archive::shared_ptr archive_sp(
       Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset));
   bool set_archive_arch = false;
Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
===================================================================
--- source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -424,7 +424,7 @@
       FileSpec oso_file(oso_path, false);
       ConstString oso_object;
       if (oso_file.Exists()) {
-        auto oso_mod_time = FileSystem::GetModificationTime(oso_file);
+        auto oso_mod_time = FileSystem::Instance().GetModificationTime(oso_file);
         if (oso_mod_time != comp_unit_info->oso_mod_time) {
           obj_file->GetModule()->ReportError(
               "debug map object file '%s' has changed (actual time is "
Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
===================================================================
--- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -212,7 +212,7 @@
     return local_spec;
 
   FileSpec resolved_symlink;
-  const auto error = FileSystem::Readlink(local_spec, resolved_symlink);
+  const auto error = FileSystem::Instance().Readlink(local_spec, resolved_symlink);
   if (error.Success())
     return resolved_symlink;
 
Index: source/Host/common/FileSystem.cpp
===================================================================
--- source/Host/common/FileSystem.cpp
+++ source/Host/common/FileSystem.cpp
@@ -9,20 +9,171 @@
 
 #include "lldb/Host/FileSystem.h"
 
+#include "lldb/Utility/TildeExpressionResolver.h"
+
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
 
 #include <algorithm>
 #include <fstream>
 #include <vector>
 
 using namespace lldb;
 using namespace lldb_private;
+using namespace llvm;
+
+FileSystem &FileSystem::Instance() { return *InstanceImpl(); }
+
+void FileSystem::Initialize() {
+  assert(!InstanceImpl());
+  InstanceImpl().emplace();
+}
+
+void FileSystem::Initialize(IntrusiveRefCntPtr<vfs::FileSystem> fs) {
+  assert(!InstanceImpl());
+  InstanceImpl().emplace(fs);
+}
+
+void FileSystem::Terminate() {
+  assert(InstanceImpl());
+  InstanceImpl().reset();
+}
+
+Optional<FileSystem> &FileSystem::InstanceImpl() {
+  static Optional<FileSystem> g_fs;
+  return g_fs;
+}
+
+void FileSystem::SetFileSystem(IntrusiveRefCntPtr<vfs::FileSystem> fs) {
+  m_fs = fs;
+}
+
+sys::TimePoint<>
+FileSystem::GetModificationTime(const FileSpec &file_spec) const {
+  return GetModificationTime(file_spec.GetPath());
+}
+
+sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {
+  ErrorOr<vfs::Status> status = m_fs->status(path);
+  if (!status)
+    return sys::TimePoint<>();
+  return status->getLastModificationTime();
+}
+
+uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
+  return GetByteSize(file_spec.GetPath());
+}
+
+uint64_t FileSystem::GetByteSize(const Twine &path) const {
+  ErrorOr<vfs::Status> status = m_fs->status(path);
+  if (!status)
+    return 0;
+  return status->getSize();
+}
+
+uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
+  return GetPermissions(file_spec.GetPath());
+}
+
+uint32_t FileSystem::GetPermissions(const Twine &path) const {
+  ErrorOr<vfs::Status> status = m_fs->status(path);
+  if (!status)
+    return sys::fs::perms::perms_not_known;
+  return status->getPermissions();
+}
+
+bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
+
+bool FileSystem::Exists(const FileSpec &file_spec) const {
+  return Exists(file_spec.GetPath());
+}
+
+bool FileSystem::Readable(const Twine &path) const {
+  return GetPermissions(path) & sys::fs::perms::all_read;
+}
+
+bool FileSystem::Readable(const FileSpec &file_spec) const {
+  return Readable(file_spec.GetPath());
+}
+
+void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
+                                    bool find_files, bool find_other,
+                                    EnumerateDirectoryCallbackType callback,
+                                    void *callback_baton) {
+  std::error_code EC;
+  vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
+  vfs::recursive_directory_iterator End;
+  for (; Iter != End && !EC; Iter.increment(EC)) {
+    const auto &Item = *Iter;
+    ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
+    if (!Status)
+      break;
+    if (!find_files && Status->isRegularFile())
+      continue;
+    if (!find_directories && Status->isDirectory())
+      continue;
+    if (!find_other && Status->isOther())
+      continue;
+
+    auto Result = callback(callback_baton, Status->getType(), Item.path());
+    if (Result == eEnumerateDirectoryResultQuit)
+      return;
+    if (Result == eEnumerateDirectoryResultNext) {
+      // Default behavior is to recurse. Opt out if the callback doesn't want
+      // this behavior.
+      Iter.no_push();
+    }
+  }
+}
+
+std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
+  return m_fs->makeAbsolute(path);
+}
+
+std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
+  SmallString<128> path;
+  file_spec.GetPath(path, false);
+
+  auto EC = MakeAbsolute(path);
+  if (EC)
+    return EC;
+
+  FileSpec new_file_spec(path, false, file_spec.GetPathStyle());
+  file_spec = new_file_spec;
+  return {};
+}
+
+std::error_code FileSystem::GetRealPath(const Twine &path,
+                                        SmallVectorImpl<char> &output) const {
+  return m_fs->getRealPath(path, output);
+}
+
+void FileSystem::Resolve(SmallVectorImpl<char> &path) {
+  if (path.empty())
+    return;
+
+  // Resolve tilde.
+  SmallString<128> original_path(path.begin(), path.end());
+  StandardTildeExpressionResolver Resolver;
+  Resolver.ResolveFullPath(original_path, path);
+
+  // Try making the path absolute if it exists.
+  SmallString<128> absolute_path(path.begin(), path.end());
+  MakeAbsolute(path);
+  if (!Exists(path)) {
+    path.clear();
+    path.append(original_path.begin(), original_path.end());
+  }
+}
+
+void FileSystem::Resolve(FileSpec &file_spec) {
+  // Extract path from the FileSpec.
+  SmallString<128> path;
+  file_spec.GetPath(path);
+
+  // Resolve the path.
+  Resolve(path);
 
-llvm::sys::TimePoint<>
-FileSystem::GetModificationTime(const FileSpec &file_spec) {
-  llvm::sys::fs::file_status status;
-  std::error_code ec = llvm::sys::fs::status(file_spec.GetPath(), status);
-  if (ec)
-    return llvm::sys::TimePoint<>();
-  return status.getLastModificationTime();
+  // Update the FileSpec with the resolved path.
+  file_spec.SetPath(path);
 }
Index: source/Host/common/Symbols.cpp
===================================================================
--- source/Host/common/Symbols.cpp
+++ source/Host/common/Symbols.cpp
@@ -261,7 +261,7 @@
     // Add module directory.
     FileSpec module_file_spec = module_spec.GetFileSpec();
     // We keep the unresolved pathname if it fails.
-    FileSystem::ResolveSymbolicLink(module_file_spec, module_file_spec);
+    FileSystem::Instance().ResolveSymbolicLink(module_file_spec, module_file_spec);
 
     const ConstString &file_dir = module_file_spec.GetDirectory();
     debug_file_search_paths.AppendIfUnique(
Index: source/Host/common/HostInfoBase.cpp
===================================================================
--- source/Host/common/HostInfoBase.cpp
+++ source/Host/common/HostInfoBase.cpp
@@ -226,7 +226,7 @@
 
   // This is necessary because when running the testsuite the shlib might be a
   // symbolic link inside the Python resource dir.
-  FileSystem::ResolveSymbolicLink(lldb_file_spec, lldb_file_spec);
+  FileSystem::Instance().ResolveSymbolicLink(lldb_file_spec, lldb_file_spec);
 
   // Remove the filename so that this FileSpec only represents the directory.
   file_spec.GetDirectory() = lldb_file_spec.GetDirectory();
Index: source/Host/posix/HostProcessPosix.cpp
===================================================================
--- source/Host/posix/HostProcessPosix.cpp
+++ source/Host/posix/HostProcessPosix.cpp
@@ -62,7 +62,7 @@
     return error;
   }
 
-  error = FileSystem::Readlink(FileSpec{link_path, false}, file_spec);
+  error = FileSystem::Instance().Readlink(FileSpec{link_path, false}, file_spec);
   if (!error.Success())
     return error;
 
Index: include/lldb/Host/FileSystem.h
===================================================================
--- include/lldb/Host/FileSystem.h
+++ include/lldb/Host/FileSystem.h
@@ -12,7 +12,10 @@
 
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/Utility/Status.h"
+
+#include "llvm/ADT/Optional.h"
 #include "llvm/Support/Chrono.h"
+#include "llvm/Support/VirtualFileSystem.h"
 
 #include "lldb/lldb-types.h"
 
@@ -26,17 +29,101 @@
   static const char *DEV_NULL;
   static const char *PATH_CONVERSION_ERROR;
 
-  static Status Symlink(const FileSpec &src, const FileSpec &dst);
-  static Status Readlink(const FileSpec &src, FileSpec &dst);
+  FileSystem() : m_fs(llvm::vfs::getRealFileSystem()) {}
+  FileSystem(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) : m_fs(fs) {}
+
+  static FileSystem &Instance();
+
+  static void Initialize();
+  static void Initialize(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs);
+  static void Terminate();
+
+  Status Symlink(const FileSpec &src, const FileSpec &dst);
+  Status Readlink(const FileSpec &src, FileSpec &dst);
 
-  static Status ResolveSymbolicLink(const FileSpec &src, FileSpec &dst);
+  Status ResolveSymbolicLink(const FileSpec &src, FileSpec &dst);
 
   /// Wraps ::fopen in a platform-independent way. Once opened, FILEs can be
   /// manipulated and closed with the normal ::fread, ::fclose, etc. functions.
-  static FILE *Fopen(const char *path, const char *mode);
+  FILE *Fopen(const char *path, const char *mode);
 
-  static llvm::sys::TimePoint<> GetModificationTime(const FileSpec &file_spec);
+  /// Returns the modification time of the given file.
+  /// @{
+  llvm::sys::TimePoint<> GetModificationTime(const FileSpec &file_spec) const;
+  llvm::sys::TimePoint<> GetModificationTime(const llvm::Twine &path) const;
+  /// @}
+
+  /// Returns the on-disk size of the given file in bytes.
+  /// @{
+  uint64_t GetByteSize(const FileSpec &file_spec) const;
+  uint64_t GetByteSize(const llvm::Twine &path) const;
+  /// @}
+
+  /// Return the current permissions of the given file.
+  ///
+  /// Returns a bitmask for the current permissions of the file (zero or more
+  /// of the permission bits defined in File::Permissions).
+  /// @{
+  uint32_t GetPermissions(const FileSpec &file_spec) const;
+  uint32_t GetPermissions(const llvm::Twine &path) const;
+  /// @}
+
+  /// Returns whether the given file exists.
+  /// @{
+  bool Exists(const FileSpec &file_spec) const;
+  bool Exists(const llvm::Twine &path) const;
+  /// @}
+
+  /// Returns whether the given file is readable.
+  /// @{
+  bool Readable(const FileSpec &file_spec) const;
+  bool Readable(const llvm::Twine &path) const;
+  /// @}
+
+  /// Make the given file path absolute.
+  /// @{
+  std::error_code MakeAbsolute(llvm::SmallVectorImpl<char> &path) const;
+  std::error_code MakeAbsolute(FileSpec &file_spec) const;
+  /// @}
+
+  /// Resolve path to make it canonical.
+  /// @{
+  void Resolve(llvm::SmallVectorImpl<char> &path);
+  void Resolve(FileSpec &file_spec);
+  /// @}
+
+  enum EnumerateDirectoryResult {
+    /// Enumerate next entry in the current directory.
+    eEnumerateDirectoryResultNext,
+    /// Recurse into the current entry if it is a directory or symlink, or next
+    /// if not.
+    eEnumerateDirectoryResultEnter,
+    /// Stop directory enumerations at any level.
+    eEnumerateDirectoryResultQuit
+  };
+
+  typedef EnumerateDirectoryResult (*EnumerateDirectoryCallbackType)(
+      void *baton, llvm::sys::fs::file_type file_type, llvm::StringRef);
+
+  typedef std::function<EnumerateDirectoryResult(
+      llvm::sys::fs::file_type file_type, llvm::StringRef)>
+      DirectoryCallback;
+
+  void EnumerateDirectory(llvm::Twine path, bool find_directories,
+                          bool find_files, bool find_other,
+                          EnumerateDirectoryCallbackType callback,
+                          void *callback_baton);
+
+  std::error_code GetRealPath(const llvm::Twine &path,
+                              llvm::SmallVectorImpl<char> &output) const;
+
+protected:
+  void SetFileSystem(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs);
+
+private:
+  static llvm::Optional<FileSystem> &InstanceImpl();
+  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> m_fs;
 };
-}
+} // namespace lldb_private
 
 #endif
Index: include/lldb/Utility/FileSpec.h
===================================================================
--- include/lldb/Utility/FileSpec.h
+++ include/lldb/Utility/FileSpec.h
@@ -375,6 +375,9 @@
   //------------------------------------------------------------------
   bool IsAbsolute() const;
 
+  /// Temporary helper for FileSystem change.
+  void SetPath(llvm::StringRef p) { SetFile(p, false); }
+
   //------------------------------------------------------------------
   /// Extract the full path to the file.
   ///
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to