10b57cec5SDimitry Andric //===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements the VirtualFileSystem interface.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
140b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h"
150b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
160b57cec5SDimitry Andric #include "llvm/ADT/IntrusiveRefCntPtr.h"
170b57cec5SDimitry Andric #include "llvm/ADT/None.h"
180b57cec5SDimitry Andric #include "llvm/ADT/Optional.h"
190b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
200b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
210b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
220b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
230b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h"
240b57cec5SDimitry Andric #include "llvm/ADT/Twine.h"
250b57cec5SDimitry Andric #include "llvm/ADT/iterator_range.h"
260b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h"
270b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
280b57cec5SDimitry Andric #include "llvm/Support/Chrono.h"
290b57cec5SDimitry Andric #include "llvm/Support/Compiler.h"
300b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
310b57cec5SDimitry Andric #include "llvm/Support/Errc.h"
320b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
330b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h"
340b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
350b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
360b57cec5SDimitry Andric #include "llvm/Support/Path.h"
370b57cec5SDimitry Andric #include "llvm/Support/Process.h"
380b57cec5SDimitry Andric #include "llvm/Support/SMLoc.h"
390b57cec5SDimitry Andric #include "llvm/Support/SourceMgr.h"
400b57cec5SDimitry Andric #include "llvm/Support/YAMLParser.h"
410b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
420b57cec5SDimitry Andric #include <algorithm>
430b57cec5SDimitry Andric #include <atomic>
440b57cec5SDimitry Andric #include <cassert>
450b57cec5SDimitry Andric #include <cstdint>
460b57cec5SDimitry Andric #include <iterator>
470b57cec5SDimitry Andric #include <limits>
480b57cec5SDimitry Andric #include <map>
490b57cec5SDimitry Andric #include <memory>
500b57cec5SDimitry Andric #include <mutex>
510b57cec5SDimitry Andric #include <string>
520b57cec5SDimitry Andric #include <system_error>
530b57cec5SDimitry Andric #include <utility>
540b57cec5SDimitry Andric #include <vector>
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric using namespace llvm;
570b57cec5SDimitry Andric using namespace llvm::vfs;
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric using llvm::sys::fs::file_t;
600b57cec5SDimitry Andric using llvm::sys::fs::file_status;
610b57cec5SDimitry Andric using llvm::sys::fs::file_type;
620b57cec5SDimitry Andric using llvm::sys::fs::kInvalidFile;
630b57cec5SDimitry Andric using llvm::sys::fs::perms;
640b57cec5SDimitry Andric using llvm::sys::fs::UniqueID;
650b57cec5SDimitry Andric 
Status(const file_status & Status)660b57cec5SDimitry Andric Status::Status(const file_status &Status)
670b57cec5SDimitry Andric     : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
680b57cec5SDimitry Andric       User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
690b57cec5SDimitry Andric       Type(Status.type()), Perms(Status.permissions()) {}
700b57cec5SDimitry Andric 
Status(const Twine & Name,UniqueID UID,sys::TimePoint<> MTime,uint32_t User,uint32_t Group,uint64_t Size,file_type Type,perms Perms)710b57cec5SDimitry Andric Status::Status(const Twine &Name, UniqueID UID, sys::TimePoint<> MTime,
720b57cec5SDimitry Andric                uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
730b57cec5SDimitry Andric                perms Perms)
740b57cec5SDimitry Andric     : Name(Name.str()), UID(UID), MTime(MTime), User(User), Group(Group),
750b57cec5SDimitry Andric       Size(Size), Type(Type), Perms(Perms) {}
760b57cec5SDimitry Andric 
copyWithNewName(const Status & In,const Twine & NewName)770b57cec5SDimitry Andric Status Status::copyWithNewName(const Status &In, const Twine &NewName) {
780b57cec5SDimitry Andric   return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
790b57cec5SDimitry Andric                 In.getUser(), In.getGroup(), In.getSize(), In.getType(),
800b57cec5SDimitry Andric                 In.getPermissions());
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric 
copyWithNewName(const file_status & In,const Twine & NewName)830b57cec5SDimitry Andric Status Status::copyWithNewName(const file_status &In, const Twine &NewName) {
840b57cec5SDimitry Andric   return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
850b57cec5SDimitry Andric                 In.getUser(), In.getGroup(), In.getSize(), In.type(),
860b57cec5SDimitry Andric                 In.permissions());
870b57cec5SDimitry Andric }
880b57cec5SDimitry Andric 
equivalent(const Status & Other) const890b57cec5SDimitry Andric bool Status::equivalent(const Status &Other) const {
900b57cec5SDimitry Andric   assert(isStatusKnown() && Other.isStatusKnown());
910b57cec5SDimitry Andric   return getUniqueID() == Other.getUniqueID();
920b57cec5SDimitry Andric }
930b57cec5SDimitry Andric 
isDirectory() const940b57cec5SDimitry Andric bool Status::isDirectory() const { return Type == file_type::directory_file; }
950b57cec5SDimitry Andric 
isRegularFile() const960b57cec5SDimitry Andric bool Status::isRegularFile() const { return Type == file_type::regular_file; }
970b57cec5SDimitry Andric 
isOther() const980b57cec5SDimitry Andric bool Status::isOther() const {
990b57cec5SDimitry Andric   return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
1000b57cec5SDimitry Andric }
1010b57cec5SDimitry Andric 
isSymlink() const1020b57cec5SDimitry Andric bool Status::isSymlink() const { return Type == file_type::symlink_file; }
1030b57cec5SDimitry Andric 
isStatusKnown() const1040b57cec5SDimitry Andric bool Status::isStatusKnown() const { return Type != file_type::status_error; }
1050b57cec5SDimitry Andric 
exists() const1060b57cec5SDimitry Andric bool Status::exists() const {
1070b57cec5SDimitry Andric   return isStatusKnown() && Type != file_type::file_not_found;
1080b57cec5SDimitry Andric }
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric File::~File() = default;
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric FileSystem::~FileSystem() = default;
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>>
getBufferForFile(const llvm::Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)1150b57cec5SDimitry Andric FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
1160b57cec5SDimitry Andric                              bool RequiresNullTerminator, bool IsVolatile) {
1170b57cec5SDimitry Andric   auto F = openFileForRead(Name);
1180b57cec5SDimitry Andric   if (!F)
1190b57cec5SDimitry Andric     return F.getError();
1200b57cec5SDimitry Andric 
1210b57cec5SDimitry Andric   return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
1220b57cec5SDimitry Andric }
1230b57cec5SDimitry Andric 
makeAbsolute(SmallVectorImpl<char> & Path) const1240b57cec5SDimitry Andric std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1250b57cec5SDimitry Andric   if (llvm::sys::path::is_absolute(Path))
1260b57cec5SDimitry Andric     return {};
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric   auto WorkingDir = getCurrentWorkingDirectory();
1290b57cec5SDimitry Andric   if (!WorkingDir)
1300b57cec5SDimitry Andric     return WorkingDir.getError();
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
1330b57cec5SDimitry Andric   return {};
1340b57cec5SDimitry Andric }
1350b57cec5SDimitry Andric 
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const1360b57cec5SDimitry Andric std::error_code FileSystem::getRealPath(const Twine &Path,
1370b57cec5SDimitry Andric                                         SmallVectorImpl<char> &Output) const {
1380b57cec5SDimitry Andric   return errc::operation_not_permitted;
1390b57cec5SDimitry Andric }
1400b57cec5SDimitry Andric 
isLocal(const Twine & Path,bool & Result)1410b57cec5SDimitry Andric std::error_code FileSystem::isLocal(const Twine &Path, bool &Result) {
1420b57cec5SDimitry Andric   return errc::operation_not_permitted;
1430b57cec5SDimitry Andric }
1440b57cec5SDimitry Andric 
exists(const Twine & Path)1450b57cec5SDimitry Andric bool FileSystem::exists(const Twine &Path) {
1460b57cec5SDimitry Andric   auto Status = status(Path);
1470b57cec5SDimitry Andric   return Status && Status->exists();
1480b57cec5SDimitry Andric }
1490b57cec5SDimitry Andric 
1500b57cec5SDimitry Andric #ifndef NDEBUG
isTraversalComponent(StringRef Component)1510b57cec5SDimitry Andric static bool isTraversalComponent(StringRef Component) {
1520b57cec5SDimitry Andric   return Component.equals("..") || Component.equals(".");
1530b57cec5SDimitry Andric }
1540b57cec5SDimitry Andric 
pathHasTraversal(StringRef Path)1550b57cec5SDimitry Andric static bool pathHasTraversal(StringRef Path) {
1560b57cec5SDimitry Andric   using namespace llvm::sys;
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric   for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1590b57cec5SDimitry Andric     if (isTraversalComponent(Comp))
1600b57cec5SDimitry Andric       return true;
1610b57cec5SDimitry Andric   return false;
1620b57cec5SDimitry Andric }
1630b57cec5SDimitry Andric #endif
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
1660b57cec5SDimitry Andric // RealFileSystem implementation
1670b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
1680b57cec5SDimitry Andric 
1690b57cec5SDimitry Andric namespace {
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric /// Wrapper around a raw file descriptor.
1720b57cec5SDimitry Andric class RealFile : public File {
1730b57cec5SDimitry Andric   friend class RealFileSystem;
1740b57cec5SDimitry Andric 
1750b57cec5SDimitry Andric   file_t FD;
1760b57cec5SDimitry Andric   Status S;
1770b57cec5SDimitry Andric   std::string RealName;
1780b57cec5SDimitry Andric 
RealFile(file_t RawFD,StringRef NewName,StringRef NewRealPathName)1798bcb0991SDimitry Andric   RealFile(file_t RawFD, StringRef NewName, StringRef NewRealPathName)
1808bcb0991SDimitry Andric       : FD(RawFD), S(NewName, {}, {}, {}, {}, {},
1810b57cec5SDimitry Andric                      llvm::sys::fs::file_type::status_error, {}),
1820b57cec5SDimitry Andric         RealName(NewRealPathName.str()) {
1830b57cec5SDimitry Andric     assert(FD != kInvalidFile && "Invalid or inactive file descriptor");
1840b57cec5SDimitry Andric   }
1850b57cec5SDimitry Andric 
1860b57cec5SDimitry Andric public:
1870b57cec5SDimitry Andric   ~RealFile() override;
1880b57cec5SDimitry Andric 
1890b57cec5SDimitry Andric   ErrorOr<Status> status() override;
1900b57cec5SDimitry Andric   ErrorOr<std::string> getName() override;
1910b57cec5SDimitry Andric   ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
1920b57cec5SDimitry Andric                                                    int64_t FileSize,
1930b57cec5SDimitry Andric                                                    bool RequiresNullTerminator,
1940b57cec5SDimitry Andric                                                    bool IsVolatile) override;
1950b57cec5SDimitry Andric   std::error_code close() override;
1960b57cec5SDimitry Andric };
1970b57cec5SDimitry Andric 
1980b57cec5SDimitry Andric } // namespace
1990b57cec5SDimitry Andric 
~RealFile()2000b57cec5SDimitry Andric RealFile::~RealFile() { close(); }
2010b57cec5SDimitry Andric 
status()2020b57cec5SDimitry Andric ErrorOr<Status> RealFile::status() {
2030b57cec5SDimitry Andric   assert(FD != kInvalidFile && "cannot stat closed file");
2040b57cec5SDimitry Andric   if (!S.isStatusKnown()) {
2050b57cec5SDimitry Andric     file_status RealStatus;
2060b57cec5SDimitry Andric     if (std::error_code EC = sys::fs::status(FD, RealStatus))
2070b57cec5SDimitry Andric       return EC;
2080b57cec5SDimitry Andric     S = Status::copyWithNewName(RealStatus, S.getName());
2090b57cec5SDimitry Andric   }
2100b57cec5SDimitry Andric   return S;
2110b57cec5SDimitry Andric }
2120b57cec5SDimitry Andric 
getName()2130b57cec5SDimitry Andric ErrorOr<std::string> RealFile::getName() {
2140b57cec5SDimitry Andric   return RealName.empty() ? S.getName().str() : RealName;
2150b57cec5SDimitry Andric }
2160b57cec5SDimitry Andric 
2170b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>>
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)2180b57cec5SDimitry Andric RealFile::getBuffer(const Twine &Name, int64_t FileSize,
2190b57cec5SDimitry Andric                     bool RequiresNullTerminator, bool IsVolatile) {
2200b57cec5SDimitry Andric   assert(FD != kInvalidFile && "cannot get buffer for closed file");
2210b57cec5SDimitry Andric   return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
2220b57cec5SDimitry Andric                                    IsVolatile);
2230b57cec5SDimitry Andric }
2240b57cec5SDimitry Andric 
close()2250b57cec5SDimitry Andric std::error_code RealFile::close() {
2260b57cec5SDimitry Andric   std::error_code EC = sys::fs::closeFile(FD);
2270b57cec5SDimitry Andric   FD = kInvalidFile;
2280b57cec5SDimitry Andric   return EC;
2290b57cec5SDimitry Andric }
2300b57cec5SDimitry Andric 
2310b57cec5SDimitry Andric namespace {
2320b57cec5SDimitry Andric 
2330b57cec5SDimitry Andric /// A file system according to your operating system.
2340b57cec5SDimitry Andric /// This may be linked to the process's working directory, or maintain its own.
2350b57cec5SDimitry Andric ///
2360b57cec5SDimitry Andric /// Currently, its own working directory is emulated by storing the path and
2370b57cec5SDimitry Andric /// sending absolute paths to llvm::sys::fs:: functions.
2380b57cec5SDimitry Andric /// A more principled approach would be to push this down a level, modelling
2390b57cec5SDimitry Andric /// the working dir as an llvm::sys::fs::WorkingDir or similar.
2400b57cec5SDimitry Andric /// This would enable the use of openat()-style functions on some platforms.
2410b57cec5SDimitry Andric class RealFileSystem : public FileSystem {
2420b57cec5SDimitry Andric public:
RealFileSystem(bool LinkCWDToProcess)2430b57cec5SDimitry Andric   explicit RealFileSystem(bool LinkCWDToProcess) {
2440b57cec5SDimitry Andric     if (!LinkCWDToProcess) {
2450b57cec5SDimitry Andric       SmallString<128> PWD, RealPWD;
2460b57cec5SDimitry Andric       if (llvm::sys::fs::current_path(PWD))
2470b57cec5SDimitry Andric         return; // Awful, but nothing to do here.
2480b57cec5SDimitry Andric       if (llvm::sys::fs::real_path(PWD, RealPWD))
2490b57cec5SDimitry Andric         WD = {PWD, PWD};
2500b57cec5SDimitry Andric       else
2510b57cec5SDimitry Andric         WD = {PWD, RealPWD};
2520b57cec5SDimitry Andric     }
2530b57cec5SDimitry Andric   }
2540b57cec5SDimitry Andric 
2550b57cec5SDimitry Andric   ErrorOr<Status> status(const Twine &Path) override;
2560b57cec5SDimitry Andric   ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
2570b57cec5SDimitry Andric   directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
2580b57cec5SDimitry Andric 
2590b57cec5SDimitry Andric   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
2600b57cec5SDimitry Andric   std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
2610b57cec5SDimitry Andric   std::error_code isLocal(const Twine &Path, bool &Result) override;
2620b57cec5SDimitry Andric   std::error_code getRealPath(const Twine &Path,
2630b57cec5SDimitry Andric                               SmallVectorImpl<char> &Output) const override;
2640b57cec5SDimitry Andric 
2650b57cec5SDimitry Andric private:
2660b57cec5SDimitry Andric   // If this FS has its own working dir, use it to make Path absolute.
2670b57cec5SDimitry Andric   // The returned twine is safe to use as long as both Storage and Path live.
adjustPath(const Twine & Path,SmallVectorImpl<char> & Storage) const2680b57cec5SDimitry Andric   Twine adjustPath(const Twine &Path, SmallVectorImpl<char> &Storage) const {
2690b57cec5SDimitry Andric     if (!WD)
2700b57cec5SDimitry Andric       return Path;
2710b57cec5SDimitry Andric     Path.toVector(Storage);
2720b57cec5SDimitry Andric     sys::fs::make_absolute(WD->Resolved, Storage);
2730b57cec5SDimitry Andric     return Storage;
2740b57cec5SDimitry Andric   }
2750b57cec5SDimitry Andric 
2760b57cec5SDimitry Andric   struct WorkingDirectory {
2770b57cec5SDimitry Andric     // The current working directory, without symlinks resolved. (echo $PWD).
2780b57cec5SDimitry Andric     SmallString<128> Specified;
2790b57cec5SDimitry Andric     // The current working directory, with links resolved. (readlink .).
2800b57cec5SDimitry Andric     SmallString<128> Resolved;
2810b57cec5SDimitry Andric   };
2820b57cec5SDimitry Andric   Optional<WorkingDirectory> WD;
2830b57cec5SDimitry Andric };
2840b57cec5SDimitry Andric 
2850b57cec5SDimitry Andric } // namespace
2860b57cec5SDimitry Andric 
status(const Twine & Path)2870b57cec5SDimitry Andric ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
2880b57cec5SDimitry Andric   SmallString<256> Storage;
2890b57cec5SDimitry Andric   sys::fs::file_status RealStatus;
2900b57cec5SDimitry Andric   if (std::error_code EC =
2910b57cec5SDimitry Andric           sys::fs::status(adjustPath(Path, Storage), RealStatus))
2920b57cec5SDimitry Andric     return EC;
2930b57cec5SDimitry Andric   return Status::copyWithNewName(RealStatus, Path);
2940b57cec5SDimitry Andric }
2950b57cec5SDimitry Andric 
2960b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Name)2970b57cec5SDimitry Andric RealFileSystem::openFileForRead(const Twine &Name) {
2980b57cec5SDimitry Andric   SmallString<256> RealName, Storage;
2990b57cec5SDimitry Andric   Expected<file_t> FDOrErr = sys::fs::openNativeFileForRead(
3000b57cec5SDimitry Andric       adjustPath(Name, Storage), sys::fs::OF_None, &RealName);
3010b57cec5SDimitry Andric   if (!FDOrErr)
3020b57cec5SDimitry Andric     return errorToErrorCode(FDOrErr.takeError());
3030b57cec5SDimitry Andric   return std::unique_ptr<File>(
3040b57cec5SDimitry Andric       new RealFile(*FDOrErr, Name.str(), RealName.str()));
3050b57cec5SDimitry Andric }
3060b57cec5SDimitry Andric 
getCurrentWorkingDirectory() const3070b57cec5SDimitry Andric llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
3080b57cec5SDimitry Andric   if (WD)
3095ffd83dbSDimitry Andric     return std::string(WD->Specified.str());
3100b57cec5SDimitry Andric 
3110b57cec5SDimitry Andric   SmallString<128> Dir;
3120b57cec5SDimitry Andric   if (std::error_code EC = llvm::sys::fs::current_path(Dir))
3130b57cec5SDimitry Andric     return EC;
3145ffd83dbSDimitry Andric   return std::string(Dir.str());
3150b57cec5SDimitry Andric }
3160b57cec5SDimitry Andric 
setCurrentWorkingDirectory(const Twine & Path)3170b57cec5SDimitry Andric std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
3180b57cec5SDimitry Andric   if (!WD)
3190b57cec5SDimitry Andric     return llvm::sys::fs::set_current_path(Path);
3200b57cec5SDimitry Andric 
3210b57cec5SDimitry Andric   SmallString<128> Absolute, Resolved, Storage;
3220b57cec5SDimitry Andric   adjustPath(Path, Storage).toVector(Absolute);
3230b57cec5SDimitry Andric   bool IsDir;
3240b57cec5SDimitry Andric   if (auto Err = llvm::sys::fs::is_directory(Absolute, IsDir))
3250b57cec5SDimitry Andric     return Err;
3260b57cec5SDimitry Andric   if (!IsDir)
3270b57cec5SDimitry Andric     return std::make_error_code(std::errc::not_a_directory);
3280b57cec5SDimitry Andric   if (auto Err = llvm::sys::fs::real_path(Absolute, Resolved))
3290b57cec5SDimitry Andric     return Err;
3300b57cec5SDimitry Andric   WD = {Absolute, Resolved};
3310b57cec5SDimitry Andric   return std::error_code();
3320b57cec5SDimitry Andric }
3330b57cec5SDimitry Andric 
isLocal(const Twine & Path,bool & Result)3340b57cec5SDimitry Andric std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) {
3350b57cec5SDimitry Andric   SmallString<256> Storage;
3360b57cec5SDimitry Andric   return llvm::sys::fs::is_local(adjustPath(Path, Storage), Result);
3370b57cec5SDimitry Andric }
3380b57cec5SDimitry Andric 
3390b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const3400b57cec5SDimitry Andric RealFileSystem::getRealPath(const Twine &Path,
3410b57cec5SDimitry Andric                             SmallVectorImpl<char> &Output) const {
3420b57cec5SDimitry Andric   SmallString<256> Storage;
3430b57cec5SDimitry Andric   return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output);
3440b57cec5SDimitry Andric }
3450b57cec5SDimitry Andric 
getRealFileSystem()3460b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
3470b57cec5SDimitry Andric   static IntrusiveRefCntPtr<FileSystem> FS(new RealFileSystem(true));
3480b57cec5SDimitry Andric   return FS;
3490b57cec5SDimitry Andric }
3500b57cec5SDimitry Andric 
createPhysicalFileSystem()3510b57cec5SDimitry Andric std::unique_ptr<FileSystem> vfs::createPhysicalFileSystem() {
3528bcb0991SDimitry Andric   return std::make_unique<RealFileSystem>(false);
3530b57cec5SDimitry Andric }
3540b57cec5SDimitry Andric 
3550b57cec5SDimitry Andric namespace {
3560b57cec5SDimitry Andric 
3570b57cec5SDimitry Andric class RealFSDirIter : public llvm::vfs::detail::DirIterImpl {
3580b57cec5SDimitry Andric   llvm::sys::fs::directory_iterator Iter;
3590b57cec5SDimitry Andric 
3600b57cec5SDimitry Andric public:
RealFSDirIter(const Twine & Path,std::error_code & EC)3610b57cec5SDimitry Andric   RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
3620b57cec5SDimitry Andric     if (Iter != llvm::sys::fs::directory_iterator())
3630b57cec5SDimitry Andric       CurrentEntry = directory_entry(Iter->path(), Iter->type());
3640b57cec5SDimitry Andric   }
3650b57cec5SDimitry Andric 
increment()3660b57cec5SDimitry Andric   std::error_code increment() override {
3670b57cec5SDimitry Andric     std::error_code EC;
3680b57cec5SDimitry Andric     Iter.increment(EC);
3690b57cec5SDimitry Andric     CurrentEntry = (Iter == llvm::sys::fs::directory_iterator())
3700b57cec5SDimitry Andric                        ? directory_entry()
3710b57cec5SDimitry Andric                        : directory_entry(Iter->path(), Iter->type());
3720b57cec5SDimitry Andric     return EC;
3730b57cec5SDimitry Andric   }
3740b57cec5SDimitry Andric };
3750b57cec5SDimitry Andric 
3760b57cec5SDimitry Andric } // namespace
3770b57cec5SDimitry Andric 
dir_begin(const Twine & Dir,std::error_code & EC)3780b57cec5SDimitry Andric directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
3790b57cec5SDimitry Andric                                              std::error_code &EC) {
3800b57cec5SDimitry Andric   SmallString<128> Storage;
3810b57cec5SDimitry Andric   return directory_iterator(
3820b57cec5SDimitry Andric       std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC));
3830b57cec5SDimitry Andric }
3840b57cec5SDimitry Andric 
3850b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
3860b57cec5SDimitry Andric // OverlayFileSystem implementation
3870b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
3880b57cec5SDimitry Andric 
OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS)3890b57cec5SDimitry Andric OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
3900b57cec5SDimitry Andric   FSList.push_back(std::move(BaseFS));
3910b57cec5SDimitry Andric }
3920b57cec5SDimitry Andric 
pushOverlay(IntrusiveRefCntPtr<FileSystem> FS)3930b57cec5SDimitry Andric void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
3940b57cec5SDimitry Andric   FSList.push_back(FS);
3950b57cec5SDimitry Andric   // Synchronize added file systems by duplicating the working directory from
3960b57cec5SDimitry Andric   // the first one in the list.
3970b57cec5SDimitry Andric   FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
3980b57cec5SDimitry Andric }
3990b57cec5SDimitry Andric 
status(const Twine & Path)4000b57cec5SDimitry Andric ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
4010b57cec5SDimitry Andric   // FIXME: handle symlinks that cross file systems
4020b57cec5SDimitry Andric   for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
4030b57cec5SDimitry Andric     ErrorOr<Status> Status = (*I)->status(Path);
4040b57cec5SDimitry Andric     if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
4050b57cec5SDimitry Andric       return Status;
4060b57cec5SDimitry Andric   }
4070b57cec5SDimitry Andric   return make_error_code(llvm::errc::no_such_file_or_directory);
4080b57cec5SDimitry Andric }
4090b57cec5SDimitry Andric 
4100b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const llvm::Twine & Path)4110b57cec5SDimitry Andric OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
4120b57cec5SDimitry Andric   // FIXME: handle symlinks that cross file systems
4130b57cec5SDimitry Andric   for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
4140b57cec5SDimitry Andric     auto Result = (*I)->openFileForRead(Path);
4150b57cec5SDimitry Andric     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
4160b57cec5SDimitry Andric       return Result;
4170b57cec5SDimitry Andric   }
4180b57cec5SDimitry Andric   return make_error_code(llvm::errc::no_such_file_or_directory);
4190b57cec5SDimitry Andric }
4200b57cec5SDimitry Andric 
4210b57cec5SDimitry Andric llvm::ErrorOr<std::string>
getCurrentWorkingDirectory() const4220b57cec5SDimitry Andric OverlayFileSystem::getCurrentWorkingDirectory() const {
4230b57cec5SDimitry Andric   // All file systems are synchronized, just take the first working directory.
4240b57cec5SDimitry Andric   return FSList.front()->getCurrentWorkingDirectory();
4250b57cec5SDimitry Andric }
4260b57cec5SDimitry Andric 
4270b57cec5SDimitry Andric std::error_code
setCurrentWorkingDirectory(const Twine & Path)4280b57cec5SDimitry Andric OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
4290b57cec5SDimitry Andric   for (auto &FS : FSList)
4300b57cec5SDimitry Andric     if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
4310b57cec5SDimitry Andric       return EC;
4320b57cec5SDimitry Andric   return {};
4330b57cec5SDimitry Andric }
4340b57cec5SDimitry Andric 
isLocal(const Twine & Path,bool & Result)4350b57cec5SDimitry Andric std::error_code OverlayFileSystem::isLocal(const Twine &Path, bool &Result) {
4360b57cec5SDimitry Andric   for (auto &FS : FSList)
4370b57cec5SDimitry Andric     if (FS->exists(Path))
4380b57cec5SDimitry Andric       return FS->isLocal(Path, Result);
4390b57cec5SDimitry Andric   return errc::no_such_file_or_directory;
4400b57cec5SDimitry Andric }
4410b57cec5SDimitry Andric 
4420b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const4430b57cec5SDimitry Andric OverlayFileSystem::getRealPath(const Twine &Path,
4440b57cec5SDimitry Andric                                SmallVectorImpl<char> &Output) const {
4450b57cec5SDimitry Andric   for (auto &FS : FSList)
4460b57cec5SDimitry Andric     if (FS->exists(Path))
4470b57cec5SDimitry Andric       return FS->getRealPath(Path, Output);
4480b57cec5SDimitry Andric   return errc::no_such_file_or_directory;
4490b57cec5SDimitry Andric }
4500b57cec5SDimitry Andric 
4510b57cec5SDimitry Andric llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;
4520b57cec5SDimitry Andric 
4530b57cec5SDimitry Andric namespace {
4540b57cec5SDimitry Andric 
455*5f7ddb14SDimitry Andric /// Combines and deduplicates directory entries across multiple file systems.
456*5f7ddb14SDimitry Andric class CombiningDirIterImpl : public llvm::vfs::detail::DirIterImpl {
457*5f7ddb14SDimitry Andric   using FileSystemPtr = llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>;
458*5f7ddb14SDimitry Andric 
459*5f7ddb14SDimitry Andric   /// File systems to check for entries in. Processed in reverse order.
460*5f7ddb14SDimitry Andric   SmallVector<FileSystemPtr, 8> FSList;
461*5f7ddb14SDimitry Andric   /// The directory iterator for the current filesystem.
4620b57cec5SDimitry Andric   directory_iterator CurrentDirIter;
463*5f7ddb14SDimitry Andric   /// The path of the directory to iterate the entries of.
464*5f7ddb14SDimitry Andric   std::string DirPath;
465*5f7ddb14SDimitry Andric   /// The set of names already returned as entries.
4660b57cec5SDimitry Andric   llvm::StringSet<> SeenNames;
4670b57cec5SDimitry Andric 
468*5f7ddb14SDimitry Andric   /// Sets \c CurrentDirIter to an iterator of \c DirPath in the next file
469*5f7ddb14SDimitry Andric   /// system in the list, or leaves it as is (at its end position) if we've
470*5f7ddb14SDimitry Andric   /// already gone through them all.
incrementFS()4710b57cec5SDimitry Andric   std::error_code incrementFS() {
472*5f7ddb14SDimitry Andric     while (!FSList.empty()) {
4730b57cec5SDimitry Andric       std::error_code EC;
474*5f7ddb14SDimitry Andric       CurrentDirIter = FSList.back()->dir_begin(DirPath, EC);
475*5f7ddb14SDimitry Andric       FSList.pop_back();
4760b57cec5SDimitry Andric       if (EC && EC != errc::no_such_file_or_directory)
4770b57cec5SDimitry Andric         return EC;
4780b57cec5SDimitry Andric       if (CurrentDirIter != directory_iterator())
4790b57cec5SDimitry Andric         break; // found
4800b57cec5SDimitry Andric     }
4810b57cec5SDimitry Andric     return {};
4820b57cec5SDimitry Andric   }
4830b57cec5SDimitry Andric 
incrementDirIter(bool IsFirstTime)4840b57cec5SDimitry Andric   std::error_code incrementDirIter(bool IsFirstTime) {
4850b57cec5SDimitry Andric     assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
4860b57cec5SDimitry Andric            "incrementing past end");
4870b57cec5SDimitry Andric     std::error_code EC;
4880b57cec5SDimitry Andric     if (!IsFirstTime)
4890b57cec5SDimitry Andric       CurrentDirIter.increment(EC);
4900b57cec5SDimitry Andric     if (!EC && CurrentDirIter == directory_iterator())
4910b57cec5SDimitry Andric       EC = incrementFS();
4920b57cec5SDimitry Andric     return EC;
4930b57cec5SDimitry Andric   }
4940b57cec5SDimitry Andric 
incrementImpl(bool IsFirstTime)4950b57cec5SDimitry Andric   std::error_code incrementImpl(bool IsFirstTime) {
4960b57cec5SDimitry Andric     while (true) {
4970b57cec5SDimitry Andric       std::error_code EC = incrementDirIter(IsFirstTime);
4980b57cec5SDimitry Andric       if (EC || CurrentDirIter == directory_iterator()) {
4990b57cec5SDimitry Andric         CurrentEntry = directory_entry();
5000b57cec5SDimitry Andric         return EC;
5010b57cec5SDimitry Andric       }
5020b57cec5SDimitry Andric       CurrentEntry = *CurrentDirIter;
5030b57cec5SDimitry Andric       StringRef Name = llvm::sys::path::filename(CurrentEntry.path());
5040b57cec5SDimitry Andric       if (SeenNames.insert(Name).second)
5050b57cec5SDimitry Andric         return EC; // name not seen before
5060b57cec5SDimitry Andric     }
5070b57cec5SDimitry Andric     llvm_unreachable("returned above");
5080b57cec5SDimitry Andric   }
5090b57cec5SDimitry Andric 
5100b57cec5SDimitry Andric public:
CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems,std::string Dir,std::error_code & EC)511*5f7ddb14SDimitry Andric   CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems, std::string Dir,
5120b57cec5SDimitry Andric                        std::error_code &EC)
513*5f7ddb14SDimitry Andric       : FSList(FileSystems.begin(), FileSystems.end()),
514*5f7ddb14SDimitry Andric         DirPath(std::move(Dir)) {
515*5f7ddb14SDimitry Andric     if (!FSList.empty()) {
516*5f7ddb14SDimitry Andric       CurrentDirIter = FSList.back()->dir_begin(DirPath, EC);
517*5f7ddb14SDimitry Andric       FSList.pop_back();
518*5f7ddb14SDimitry Andric       if (!EC || EC == errc::no_such_file_or_directory)
519*5f7ddb14SDimitry Andric         EC = incrementImpl(true);
520*5f7ddb14SDimitry Andric     }
521*5f7ddb14SDimitry Andric   }
522*5f7ddb14SDimitry Andric 
CombiningDirIterImpl(directory_iterator FirstIter,FileSystemPtr Fallback,std::string FallbackDir,std::error_code & EC)523*5f7ddb14SDimitry Andric   CombiningDirIterImpl(directory_iterator FirstIter, FileSystemPtr Fallback,
524*5f7ddb14SDimitry Andric                        std::string FallbackDir, std::error_code &EC)
525*5f7ddb14SDimitry Andric       : FSList({Fallback}), CurrentDirIter(FirstIter),
526*5f7ddb14SDimitry Andric         DirPath(std::move(FallbackDir)) {
527*5f7ddb14SDimitry Andric     if (!EC || EC == errc::no_such_file_or_directory)
5280b57cec5SDimitry Andric       EC = incrementImpl(true);
5290b57cec5SDimitry Andric   }
5300b57cec5SDimitry Andric 
increment()5310b57cec5SDimitry Andric   std::error_code increment() override { return incrementImpl(false); }
5320b57cec5SDimitry Andric };
5330b57cec5SDimitry Andric 
5340b57cec5SDimitry Andric } // namespace
5350b57cec5SDimitry Andric 
dir_begin(const Twine & Dir,std::error_code & EC)5360b57cec5SDimitry Andric directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
5370b57cec5SDimitry Andric                                                 std::error_code &EC) {
5380b57cec5SDimitry Andric   return directory_iterator(
539*5f7ddb14SDimitry Andric       std::make_shared<CombiningDirIterImpl>(FSList, Dir.str(), EC));
5400b57cec5SDimitry Andric }
5410b57cec5SDimitry Andric 
anchor()5420b57cec5SDimitry Andric void ProxyFileSystem::anchor() {}
5430b57cec5SDimitry Andric 
5440b57cec5SDimitry Andric namespace llvm {
5450b57cec5SDimitry Andric namespace vfs {
5460b57cec5SDimitry Andric 
5470b57cec5SDimitry Andric namespace detail {
5480b57cec5SDimitry Andric 
5490b57cec5SDimitry Andric enum InMemoryNodeKind { IME_File, IME_Directory, IME_HardLink };
5500b57cec5SDimitry Andric 
5510b57cec5SDimitry Andric /// The in memory file system is a tree of Nodes. Every node can either be a
5520b57cec5SDimitry Andric /// file , hardlink or a directory.
5530b57cec5SDimitry Andric class InMemoryNode {
5540b57cec5SDimitry Andric   InMemoryNodeKind Kind;
5550b57cec5SDimitry Andric   std::string FileName;
5560b57cec5SDimitry Andric 
5570b57cec5SDimitry Andric public:
InMemoryNode(llvm::StringRef FileName,InMemoryNodeKind Kind)5580b57cec5SDimitry Andric   InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind)
5595ffd83dbSDimitry Andric       : Kind(Kind), FileName(std::string(llvm::sys::path::filename(FileName))) {
5605ffd83dbSDimitry Andric   }
5610b57cec5SDimitry Andric   virtual ~InMemoryNode() = default;
5620b57cec5SDimitry Andric 
5630b57cec5SDimitry Andric   /// Get the filename of this node (the name without the directory part).
getFileName() const5640b57cec5SDimitry Andric   StringRef getFileName() const { return FileName; }
getKind() const5650b57cec5SDimitry Andric   InMemoryNodeKind getKind() const { return Kind; }
5660b57cec5SDimitry Andric   virtual std::string toString(unsigned Indent) const = 0;
5670b57cec5SDimitry Andric };
5680b57cec5SDimitry Andric 
5690b57cec5SDimitry Andric class InMemoryFile : public InMemoryNode {
5700b57cec5SDimitry Andric   Status Stat;
5710b57cec5SDimitry Andric   std::unique_ptr<llvm::MemoryBuffer> Buffer;
5720b57cec5SDimitry Andric 
5730b57cec5SDimitry Andric public:
InMemoryFile(Status Stat,std::unique_ptr<llvm::MemoryBuffer> Buffer)5740b57cec5SDimitry Andric   InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
5750b57cec5SDimitry Andric       : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)),
5760b57cec5SDimitry Andric         Buffer(std::move(Buffer)) {}
5770b57cec5SDimitry Andric 
5780b57cec5SDimitry Andric   /// Return the \p Status for this node. \p RequestedName should be the name
5790b57cec5SDimitry Andric   /// through which the caller referred to this node. It will override
5800b57cec5SDimitry Andric   /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
getStatus(const Twine & RequestedName) const5810b57cec5SDimitry Andric   Status getStatus(const Twine &RequestedName) const {
5820b57cec5SDimitry Andric     return Status::copyWithNewName(Stat, RequestedName);
5830b57cec5SDimitry Andric   }
getBuffer() const5840b57cec5SDimitry Andric   llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); }
5850b57cec5SDimitry Andric 
toString(unsigned Indent) const5860b57cec5SDimitry Andric   std::string toString(unsigned Indent) const override {
5870b57cec5SDimitry Andric     return (std::string(Indent, ' ') + Stat.getName() + "\n").str();
5880b57cec5SDimitry Andric   }
5890b57cec5SDimitry Andric 
classof(const InMemoryNode * N)5900b57cec5SDimitry Andric   static bool classof(const InMemoryNode *N) {
5910b57cec5SDimitry Andric     return N->getKind() == IME_File;
5920b57cec5SDimitry Andric   }
5930b57cec5SDimitry Andric };
5940b57cec5SDimitry Andric 
5950b57cec5SDimitry Andric namespace {
5960b57cec5SDimitry Andric 
5970b57cec5SDimitry Andric class InMemoryHardLink : public InMemoryNode {
5980b57cec5SDimitry Andric   const InMemoryFile &ResolvedFile;
5990b57cec5SDimitry Andric 
6000b57cec5SDimitry Andric public:
InMemoryHardLink(StringRef Path,const InMemoryFile & ResolvedFile)6010b57cec5SDimitry Andric   InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile)
6020b57cec5SDimitry Andric       : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {}
getResolvedFile() const6030b57cec5SDimitry Andric   const InMemoryFile &getResolvedFile() const { return ResolvedFile; }
6040b57cec5SDimitry Andric 
toString(unsigned Indent) const6050b57cec5SDimitry Andric   std::string toString(unsigned Indent) const override {
6060b57cec5SDimitry Andric     return std::string(Indent, ' ') + "HardLink to -> " +
6070b57cec5SDimitry Andric            ResolvedFile.toString(0);
6080b57cec5SDimitry Andric   }
6090b57cec5SDimitry Andric 
classof(const InMemoryNode * N)6100b57cec5SDimitry Andric   static bool classof(const InMemoryNode *N) {
6110b57cec5SDimitry Andric     return N->getKind() == IME_HardLink;
6120b57cec5SDimitry Andric   }
6130b57cec5SDimitry Andric };
6140b57cec5SDimitry Andric 
6150b57cec5SDimitry Andric /// Adapt a InMemoryFile for VFS' File interface.  The goal is to make
6160b57cec5SDimitry Andric /// \p InMemoryFileAdaptor mimic as much as possible the behavior of
6170b57cec5SDimitry Andric /// \p RealFile.
6180b57cec5SDimitry Andric class InMemoryFileAdaptor : public File {
6190b57cec5SDimitry Andric   const InMemoryFile &Node;
6200b57cec5SDimitry Andric   /// The name to use when returning a Status for this file.
6210b57cec5SDimitry Andric   std::string RequestedName;
6220b57cec5SDimitry Andric 
6230b57cec5SDimitry Andric public:
InMemoryFileAdaptor(const InMemoryFile & Node,std::string RequestedName)6240b57cec5SDimitry Andric   explicit InMemoryFileAdaptor(const InMemoryFile &Node,
6250b57cec5SDimitry Andric                                std::string RequestedName)
6260b57cec5SDimitry Andric       : Node(Node), RequestedName(std::move(RequestedName)) {}
6270b57cec5SDimitry Andric 
status()6280b57cec5SDimitry Andric   llvm::ErrorOr<Status> status() override {
6290b57cec5SDimitry Andric     return Node.getStatus(RequestedName);
6300b57cec5SDimitry Andric   }
6310b57cec5SDimitry Andric 
6320b57cec5SDimitry Andric   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)6330b57cec5SDimitry Andric   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
6340b57cec5SDimitry Andric             bool IsVolatile) override {
6350b57cec5SDimitry Andric     llvm::MemoryBuffer *Buf = Node.getBuffer();
6360b57cec5SDimitry Andric     return llvm::MemoryBuffer::getMemBuffer(
6370b57cec5SDimitry Andric         Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
6380b57cec5SDimitry Andric   }
6390b57cec5SDimitry Andric 
close()6400b57cec5SDimitry Andric   std::error_code close() override { return {}; }
6410b57cec5SDimitry Andric };
6420b57cec5SDimitry Andric } // namespace
6430b57cec5SDimitry Andric 
6440b57cec5SDimitry Andric class InMemoryDirectory : public InMemoryNode {
6450b57cec5SDimitry Andric   Status Stat;
6460b57cec5SDimitry Andric   llvm::StringMap<std::unique_ptr<InMemoryNode>> Entries;
6470b57cec5SDimitry Andric 
6480b57cec5SDimitry Andric public:
InMemoryDirectory(Status Stat)6490b57cec5SDimitry Andric   InMemoryDirectory(Status Stat)
6500b57cec5SDimitry Andric       : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {}
6510b57cec5SDimitry Andric 
6520b57cec5SDimitry Andric   /// Return the \p Status for this node. \p RequestedName should be the name
6530b57cec5SDimitry Andric   /// through which the caller referred to this node. It will override
6540b57cec5SDimitry Andric   /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
getStatus(const Twine & RequestedName) const6550b57cec5SDimitry Andric   Status getStatus(const Twine &RequestedName) const {
6560b57cec5SDimitry Andric     return Status::copyWithNewName(Stat, RequestedName);
6570b57cec5SDimitry Andric   }
getChild(StringRef Name)6580b57cec5SDimitry Andric   InMemoryNode *getChild(StringRef Name) {
6590b57cec5SDimitry Andric     auto I = Entries.find(Name);
6600b57cec5SDimitry Andric     if (I != Entries.end())
6610b57cec5SDimitry Andric       return I->second.get();
6620b57cec5SDimitry Andric     return nullptr;
6630b57cec5SDimitry Andric   }
6640b57cec5SDimitry Andric 
addChild(StringRef Name,std::unique_ptr<InMemoryNode> Child)6650b57cec5SDimitry Andric   InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
6660b57cec5SDimitry Andric     return Entries.insert(make_pair(Name, std::move(Child)))
6670b57cec5SDimitry Andric         .first->second.get();
6680b57cec5SDimitry Andric   }
6690b57cec5SDimitry Andric 
6700b57cec5SDimitry Andric   using const_iterator = decltype(Entries)::const_iterator;
6710b57cec5SDimitry Andric 
begin() const6720b57cec5SDimitry Andric   const_iterator begin() const { return Entries.begin(); }
end() const6730b57cec5SDimitry Andric   const_iterator end() const { return Entries.end(); }
6740b57cec5SDimitry Andric 
toString(unsigned Indent) const6750b57cec5SDimitry Andric   std::string toString(unsigned Indent) const override {
6760b57cec5SDimitry Andric     std::string Result =
6770b57cec5SDimitry Andric         (std::string(Indent, ' ') + Stat.getName() + "\n").str();
6780b57cec5SDimitry Andric     for (const auto &Entry : Entries)
6790b57cec5SDimitry Andric       Result += Entry.second->toString(Indent + 2);
6800b57cec5SDimitry Andric     return Result;
6810b57cec5SDimitry Andric   }
6820b57cec5SDimitry Andric 
classof(const InMemoryNode * N)6830b57cec5SDimitry Andric   static bool classof(const InMemoryNode *N) {
6840b57cec5SDimitry Andric     return N->getKind() == IME_Directory;
6850b57cec5SDimitry Andric   }
6860b57cec5SDimitry Andric };
6870b57cec5SDimitry Andric 
6880b57cec5SDimitry Andric namespace {
getNodeStatus(const InMemoryNode * Node,const Twine & RequestedName)6890b57cec5SDimitry Andric Status getNodeStatus(const InMemoryNode *Node, const Twine &RequestedName) {
6900b57cec5SDimitry Andric   if (auto Dir = dyn_cast<detail::InMemoryDirectory>(Node))
6910b57cec5SDimitry Andric     return Dir->getStatus(RequestedName);
6920b57cec5SDimitry Andric   if (auto File = dyn_cast<detail::InMemoryFile>(Node))
6930b57cec5SDimitry Andric     return File->getStatus(RequestedName);
6940b57cec5SDimitry Andric   if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node))
6950b57cec5SDimitry Andric     return Link->getResolvedFile().getStatus(RequestedName);
6960b57cec5SDimitry Andric   llvm_unreachable("Unknown node type");
6970b57cec5SDimitry Andric }
6980b57cec5SDimitry Andric } // namespace
6990b57cec5SDimitry Andric } // namespace detail
7000b57cec5SDimitry Andric 
InMemoryFileSystem(bool UseNormalizedPaths)7010b57cec5SDimitry Andric InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
7020b57cec5SDimitry Andric     : Root(new detail::InMemoryDirectory(
7030b57cec5SDimitry Andric           Status("", getNextVirtualUniqueID(), llvm::sys::TimePoint<>(), 0, 0,
7040b57cec5SDimitry Andric                  0, llvm::sys::fs::file_type::directory_file,
7050b57cec5SDimitry Andric                  llvm::sys::fs::perms::all_all))),
7060b57cec5SDimitry Andric       UseNormalizedPaths(UseNormalizedPaths) {}
7070b57cec5SDimitry Andric 
7080b57cec5SDimitry Andric InMemoryFileSystem::~InMemoryFileSystem() = default;
7090b57cec5SDimitry Andric 
toString() const7100b57cec5SDimitry Andric std::string InMemoryFileSystem::toString() const {
7110b57cec5SDimitry Andric   return Root->toString(/*Indent=*/0);
7120b57cec5SDimitry Andric }
7130b57cec5SDimitry Andric 
addFile(const Twine & P,time_t ModificationTime,std::unique_ptr<llvm::MemoryBuffer> Buffer,Optional<uint32_t> User,Optional<uint32_t> Group,Optional<llvm::sys::fs::file_type> Type,Optional<llvm::sys::fs::perms> Perms,const detail::InMemoryFile * HardLinkTarget)7140b57cec5SDimitry Andric bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
7150b57cec5SDimitry Andric                                  std::unique_ptr<llvm::MemoryBuffer> Buffer,
7160b57cec5SDimitry Andric                                  Optional<uint32_t> User,
7170b57cec5SDimitry Andric                                  Optional<uint32_t> Group,
7180b57cec5SDimitry Andric                                  Optional<llvm::sys::fs::file_type> Type,
7190b57cec5SDimitry Andric                                  Optional<llvm::sys::fs::perms> Perms,
7200b57cec5SDimitry Andric                                  const detail::InMemoryFile *HardLinkTarget) {
7210b57cec5SDimitry Andric   SmallString<128> Path;
7220b57cec5SDimitry Andric   P.toVector(Path);
7230b57cec5SDimitry Andric 
7240b57cec5SDimitry Andric   // Fix up relative paths. This just prepends the current working directory.
7250b57cec5SDimitry Andric   std::error_code EC = makeAbsolute(Path);
7260b57cec5SDimitry Andric   assert(!EC);
7270b57cec5SDimitry Andric   (void)EC;
7280b57cec5SDimitry Andric 
7290b57cec5SDimitry Andric   if (useNormalizedPaths())
7300b57cec5SDimitry Andric     llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
7310b57cec5SDimitry Andric 
7320b57cec5SDimitry Andric   if (Path.empty())
7330b57cec5SDimitry Andric     return false;
7340b57cec5SDimitry Andric 
7350b57cec5SDimitry Andric   detail::InMemoryDirectory *Dir = Root.get();
7360b57cec5SDimitry Andric   auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path);
7370b57cec5SDimitry Andric   const auto ResolvedUser = User.getValueOr(0);
7380b57cec5SDimitry Andric   const auto ResolvedGroup = Group.getValueOr(0);
7390b57cec5SDimitry Andric   const auto ResolvedType = Type.getValueOr(sys::fs::file_type::regular_file);
7400b57cec5SDimitry Andric   const auto ResolvedPerms = Perms.getValueOr(sys::fs::all_all);
7410b57cec5SDimitry Andric   assert(!(HardLinkTarget && Buffer) && "HardLink cannot have a buffer");
7420b57cec5SDimitry Andric   // Any intermediate directories we create should be accessible by
7430b57cec5SDimitry Andric   // the owner, even if Perms says otherwise for the final path.
7440b57cec5SDimitry Andric   const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all;
7450b57cec5SDimitry Andric   while (true) {
7460b57cec5SDimitry Andric     StringRef Name = *I;
7470b57cec5SDimitry Andric     detail::InMemoryNode *Node = Dir->getChild(Name);
7480b57cec5SDimitry Andric     ++I;
7490b57cec5SDimitry Andric     if (!Node) {
7500b57cec5SDimitry Andric       if (I == E) {
7510b57cec5SDimitry Andric         // End of the path.
7520b57cec5SDimitry Andric         std::unique_ptr<detail::InMemoryNode> Child;
7530b57cec5SDimitry Andric         if (HardLinkTarget)
7540b57cec5SDimitry Andric           Child.reset(new detail::InMemoryHardLink(P.str(), *HardLinkTarget));
7550b57cec5SDimitry Andric         else {
7560b57cec5SDimitry Andric           // Create a new file or directory.
7570b57cec5SDimitry Andric           Status Stat(P.str(), getNextVirtualUniqueID(),
7580b57cec5SDimitry Andric                       llvm::sys::toTimePoint(ModificationTime), ResolvedUser,
7590b57cec5SDimitry Andric                       ResolvedGroup, Buffer->getBufferSize(), ResolvedType,
7600b57cec5SDimitry Andric                       ResolvedPerms);
7610b57cec5SDimitry Andric           if (ResolvedType == sys::fs::file_type::directory_file) {
7620b57cec5SDimitry Andric             Child.reset(new detail::InMemoryDirectory(std::move(Stat)));
7630b57cec5SDimitry Andric           } else {
7640b57cec5SDimitry Andric             Child.reset(
7650b57cec5SDimitry Andric                 new detail::InMemoryFile(std::move(Stat), std::move(Buffer)));
7660b57cec5SDimitry Andric           }
7670b57cec5SDimitry Andric         }
7680b57cec5SDimitry Andric         Dir->addChild(Name, std::move(Child));
7690b57cec5SDimitry Andric         return true;
7700b57cec5SDimitry Andric       }
7710b57cec5SDimitry Andric 
7720b57cec5SDimitry Andric       // Create a new directory. Use the path up to here.
7730b57cec5SDimitry Andric       Status Stat(
7740b57cec5SDimitry Andric           StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
7750b57cec5SDimitry Andric           getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime),
7760b57cec5SDimitry Andric           ResolvedUser, ResolvedGroup, 0, sys::fs::file_type::directory_file,
7770b57cec5SDimitry Andric           NewDirectoryPerms);
7780b57cec5SDimitry Andric       Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
7798bcb0991SDimitry Andric           Name, std::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
7800b57cec5SDimitry Andric       continue;
7810b57cec5SDimitry Andric     }
7820b57cec5SDimitry Andric 
7830b57cec5SDimitry Andric     if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) {
7840b57cec5SDimitry Andric       Dir = NewDir;
7850b57cec5SDimitry Andric     } else {
7860b57cec5SDimitry Andric       assert((isa<detail::InMemoryFile>(Node) ||
7870b57cec5SDimitry Andric               isa<detail::InMemoryHardLink>(Node)) &&
7880b57cec5SDimitry Andric              "Must be either file, hardlink or directory!");
7890b57cec5SDimitry Andric 
7900b57cec5SDimitry Andric       // Trying to insert a directory in place of a file.
7910b57cec5SDimitry Andric       if (I != E)
7920b57cec5SDimitry Andric         return false;
7930b57cec5SDimitry Andric 
7940b57cec5SDimitry Andric       // Return false only if the new file is different from the existing one.
7950b57cec5SDimitry Andric       if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) {
7960b57cec5SDimitry Andric         return Link->getResolvedFile().getBuffer()->getBuffer() ==
7970b57cec5SDimitry Andric                Buffer->getBuffer();
7980b57cec5SDimitry Andric       }
7990b57cec5SDimitry Andric       return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
8000b57cec5SDimitry Andric              Buffer->getBuffer();
8010b57cec5SDimitry Andric     }
8020b57cec5SDimitry Andric   }
8030b57cec5SDimitry Andric }
8040b57cec5SDimitry Andric 
addFile(const Twine & P,time_t ModificationTime,std::unique_ptr<llvm::MemoryBuffer> Buffer,Optional<uint32_t> User,Optional<uint32_t> Group,Optional<llvm::sys::fs::file_type> Type,Optional<llvm::sys::fs::perms> Perms)8050b57cec5SDimitry Andric bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
8060b57cec5SDimitry Andric                                  std::unique_ptr<llvm::MemoryBuffer> Buffer,
8070b57cec5SDimitry Andric                                  Optional<uint32_t> User,
8080b57cec5SDimitry Andric                                  Optional<uint32_t> Group,
8090b57cec5SDimitry Andric                                  Optional<llvm::sys::fs::file_type> Type,
8100b57cec5SDimitry Andric                                  Optional<llvm::sys::fs::perms> Perms) {
8110b57cec5SDimitry Andric   return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type,
8120b57cec5SDimitry Andric                  Perms, /*HardLinkTarget=*/nullptr);
8130b57cec5SDimitry Andric }
8140b57cec5SDimitry Andric 
addFileNoOwn(const Twine & P,time_t ModificationTime,const llvm::MemoryBufferRef & Buffer,Optional<uint32_t> User,Optional<uint32_t> Group,Optional<llvm::sys::fs::file_type> Type,Optional<llvm::sys::fs::perms> Perms)8150b57cec5SDimitry Andric bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
816af732203SDimitry Andric                                       const llvm::MemoryBufferRef &Buffer,
8170b57cec5SDimitry Andric                                       Optional<uint32_t> User,
8180b57cec5SDimitry Andric                                       Optional<uint32_t> Group,
8190b57cec5SDimitry Andric                                       Optional<llvm::sys::fs::file_type> Type,
8200b57cec5SDimitry Andric                                       Optional<llvm::sys::fs::perms> Perms) {
821af732203SDimitry Andric   return addFile(P, ModificationTime, llvm::MemoryBuffer::getMemBuffer(Buffer),
8220b57cec5SDimitry Andric                  std::move(User), std::move(Group), std::move(Type),
8230b57cec5SDimitry Andric                  std::move(Perms));
8240b57cec5SDimitry Andric }
8250b57cec5SDimitry Andric 
8260b57cec5SDimitry Andric static ErrorOr<const detail::InMemoryNode *>
lookupInMemoryNode(const InMemoryFileSystem & FS,detail::InMemoryDirectory * Dir,const Twine & P)8270b57cec5SDimitry Andric lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir,
8280b57cec5SDimitry Andric                    const Twine &P) {
8290b57cec5SDimitry Andric   SmallString<128> Path;
8300b57cec5SDimitry Andric   P.toVector(Path);
8310b57cec5SDimitry Andric 
8320b57cec5SDimitry Andric   // Fix up relative paths. This just prepends the current working directory.
8330b57cec5SDimitry Andric   std::error_code EC = FS.makeAbsolute(Path);
8340b57cec5SDimitry Andric   assert(!EC);
8350b57cec5SDimitry Andric   (void)EC;
8360b57cec5SDimitry Andric 
8370b57cec5SDimitry Andric   if (FS.useNormalizedPaths())
8380b57cec5SDimitry Andric     llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
8390b57cec5SDimitry Andric 
8400b57cec5SDimitry Andric   if (Path.empty())
8410b57cec5SDimitry Andric     return Dir;
8420b57cec5SDimitry Andric 
8430b57cec5SDimitry Andric   auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
8440b57cec5SDimitry Andric   while (true) {
8450b57cec5SDimitry Andric     detail::InMemoryNode *Node = Dir->getChild(*I);
8460b57cec5SDimitry Andric     ++I;
8470b57cec5SDimitry Andric     if (!Node)
8480b57cec5SDimitry Andric       return errc::no_such_file_or_directory;
8490b57cec5SDimitry Andric 
8500b57cec5SDimitry Andric     // Return the file if it's at the end of the path.
8510b57cec5SDimitry Andric     if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
8520b57cec5SDimitry Andric       if (I == E)
8530b57cec5SDimitry Andric         return File;
8540b57cec5SDimitry Andric       return errc::no_such_file_or_directory;
8550b57cec5SDimitry Andric     }
8560b57cec5SDimitry Andric 
8570b57cec5SDimitry Andric     // If Node is HardLink then return the resolved file.
8580b57cec5SDimitry Andric     if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) {
8590b57cec5SDimitry Andric       if (I == E)
8600b57cec5SDimitry Andric         return &File->getResolvedFile();
8610b57cec5SDimitry Andric       return errc::no_such_file_or_directory;
8620b57cec5SDimitry Andric     }
8630b57cec5SDimitry Andric     // Traverse directories.
8640b57cec5SDimitry Andric     Dir = cast<detail::InMemoryDirectory>(Node);
8650b57cec5SDimitry Andric     if (I == E)
8660b57cec5SDimitry Andric       return Dir;
8670b57cec5SDimitry Andric   }
8680b57cec5SDimitry Andric }
8690b57cec5SDimitry Andric 
addHardLink(const Twine & FromPath,const Twine & ToPath)8700b57cec5SDimitry Andric bool InMemoryFileSystem::addHardLink(const Twine &FromPath,
8710b57cec5SDimitry Andric                                      const Twine &ToPath) {
8720b57cec5SDimitry Andric   auto FromNode = lookupInMemoryNode(*this, Root.get(), FromPath);
8730b57cec5SDimitry Andric   auto ToNode = lookupInMemoryNode(*this, Root.get(), ToPath);
8740b57cec5SDimitry Andric   // FromPath must not have been added before. ToPath must have been added
8750b57cec5SDimitry Andric   // before. Resolved ToPath must be a File.
8760b57cec5SDimitry Andric   if (!ToNode || FromNode || !isa<detail::InMemoryFile>(*ToNode))
8770b57cec5SDimitry Andric     return false;
8780b57cec5SDimitry Andric   return this->addFile(FromPath, 0, nullptr, None, None, None, None,
8790b57cec5SDimitry Andric                        cast<detail::InMemoryFile>(*ToNode));
8800b57cec5SDimitry Andric }
8810b57cec5SDimitry Andric 
status(const Twine & Path)8820b57cec5SDimitry Andric llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
8830b57cec5SDimitry Andric   auto Node = lookupInMemoryNode(*this, Root.get(), Path);
8840b57cec5SDimitry Andric   if (Node)
8850b57cec5SDimitry Andric     return detail::getNodeStatus(*Node, Path);
8860b57cec5SDimitry Andric   return Node.getError();
8870b57cec5SDimitry Andric }
8880b57cec5SDimitry Andric 
8890b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Path)8900b57cec5SDimitry Andric InMemoryFileSystem::openFileForRead(const Twine &Path) {
8910b57cec5SDimitry Andric   auto Node = lookupInMemoryNode(*this, Root.get(), Path);
8920b57cec5SDimitry Andric   if (!Node)
8930b57cec5SDimitry Andric     return Node.getError();
8940b57cec5SDimitry Andric 
8950b57cec5SDimitry Andric   // When we have a file provide a heap-allocated wrapper for the memory buffer
8960b57cec5SDimitry Andric   // to match the ownership semantics for File.
8970b57cec5SDimitry Andric   if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
8980b57cec5SDimitry Andric     return std::unique_ptr<File>(
8990b57cec5SDimitry Andric         new detail::InMemoryFileAdaptor(*F, Path.str()));
9000b57cec5SDimitry Andric 
9010b57cec5SDimitry Andric   // FIXME: errc::not_a_file?
9020b57cec5SDimitry Andric   return make_error_code(llvm::errc::invalid_argument);
9030b57cec5SDimitry Andric }
9040b57cec5SDimitry Andric 
9050b57cec5SDimitry Andric namespace {
9060b57cec5SDimitry Andric 
9070b57cec5SDimitry Andric /// Adaptor from InMemoryDir::iterator to directory_iterator.
9080b57cec5SDimitry Andric class InMemoryDirIterator : public llvm::vfs::detail::DirIterImpl {
9090b57cec5SDimitry Andric   detail::InMemoryDirectory::const_iterator I;
9100b57cec5SDimitry Andric   detail::InMemoryDirectory::const_iterator E;
9110b57cec5SDimitry Andric   std::string RequestedDirName;
9120b57cec5SDimitry Andric 
setCurrentEntry()9130b57cec5SDimitry Andric   void setCurrentEntry() {
9140b57cec5SDimitry Andric     if (I != E) {
9150b57cec5SDimitry Andric       SmallString<256> Path(RequestedDirName);
9160b57cec5SDimitry Andric       llvm::sys::path::append(Path, I->second->getFileName());
917480093f4SDimitry Andric       sys::fs::file_type Type = sys::fs::file_type::type_unknown;
9180b57cec5SDimitry Andric       switch (I->second->getKind()) {
9190b57cec5SDimitry Andric       case detail::IME_File:
9200b57cec5SDimitry Andric       case detail::IME_HardLink:
9210b57cec5SDimitry Andric         Type = sys::fs::file_type::regular_file;
9220b57cec5SDimitry Andric         break;
9230b57cec5SDimitry Andric       case detail::IME_Directory:
9240b57cec5SDimitry Andric         Type = sys::fs::file_type::directory_file;
9250b57cec5SDimitry Andric         break;
9260b57cec5SDimitry Andric       }
9275ffd83dbSDimitry Andric       CurrentEntry = directory_entry(std::string(Path.str()), Type);
9280b57cec5SDimitry Andric     } else {
9290b57cec5SDimitry Andric       // When we're at the end, make CurrentEntry invalid and DirIterImpl will
9300b57cec5SDimitry Andric       // do the rest.
9310b57cec5SDimitry Andric       CurrentEntry = directory_entry();
9320b57cec5SDimitry Andric     }
9330b57cec5SDimitry Andric   }
9340b57cec5SDimitry Andric 
9350b57cec5SDimitry Andric public:
9360b57cec5SDimitry Andric   InMemoryDirIterator() = default;
9370b57cec5SDimitry Andric 
InMemoryDirIterator(const detail::InMemoryDirectory & Dir,std::string RequestedDirName)9380b57cec5SDimitry Andric   explicit InMemoryDirIterator(const detail::InMemoryDirectory &Dir,
9390b57cec5SDimitry Andric                                std::string RequestedDirName)
9400b57cec5SDimitry Andric       : I(Dir.begin()), E(Dir.end()),
9410b57cec5SDimitry Andric         RequestedDirName(std::move(RequestedDirName)) {
9420b57cec5SDimitry Andric     setCurrentEntry();
9430b57cec5SDimitry Andric   }
9440b57cec5SDimitry Andric 
increment()9450b57cec5SDimitry Andric   std::error_code increment() override {
9460b57cec5SDimitry Andric     ++I;
9470b57cec5SDimitry Andric     setCurrentEntry();
9480b57cec5SDimitry Andric     return {};
9490b57cec5SDimitry Andric   }
9500b57cec5SDimitry Andric };
9510b57cec5SDimitry Andric 
9520b57cec5SDimitry Andric } // namespace
9530b57cec5SDimitry Andric 
dir_begin(const Twine & Dir,std::error_code & EC)9540b57cec5SDimitry Andric directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
9550b57cec5SDimitry Andric                                                  std::error_code &EC) {
9560b57cec5SDimitry Andric   auto Node = lookupInMemoryNode(*this, Root.get(), Dir);
9570b57cec5SDimitry Andric   if (!Node) {
9580b57cec5SDimitry Andric     EC = Node.getError();
9590b57cec5SDimitry Andric     return directory_iterator(std::make_shared<InMemoryDirIterator>());
9600b57cec5SDimitry Andric   }
9610b57cec5SDimitry Andric 
9620b57cec5SDimitry Andric   if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
9630b57cec5SDimitry Andric     return directory_iterator(
9640b57cec5SDimitry Andric         std::make_shared<InMemoryDirIterator>(*DirNode, Dir.str()));
9650b57cec5SDimitry Andric 
9660b57cec5SDimitry Andric   EC = make_error_code(llvm::errc::not_a_directory);
9670b57cec5SDimitry Andric   return directory_iterator(std::make_shared<InMemoryDirIterator>());
9680b57cec5SDimitry Andric }
9690b57cec5SDimitry Andric 
setCurrentWorkingDirectory(const Twine & P)9700b57cec5SDimitry Andric std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) {
9710b57cec5SDimitry Andric   SmallString<128> Path;
9720b57cec5SDimitry Andric   P.toVector(Path);
9730b57cec5SDimitry Andric 
9740b57cec5SDimitry Andric   // Fix up relative paths. This just prepends the current working directory.
9750b57cec5SDimitry Andric   std::error_code EC = makeAbsolute(Path);
9760b57cec5SDimitry Andric   assert(!EC);
9770b57cec5SDimitry Andric   (void)EC;
9780b57cec5SDimitry Andric 
9790b57cec5SDimitry Andric   if (useNormalizedPaths())
9800b57cec5SDimitry Andric     llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
9810b57cec5SDimitry Andric 
9820b57cec5SDimitry Andric   if (!Path.empty())
9835ffd83dbSDimitry Andric     WorkingDirectory = std::string(Path.str());
9840b57cec5SDimitry Andric   return {};
9850b57cec5SDimitry Andric }
9860b57cec5SDimitry Andric 
9870b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const9880b57cec5SDimitry Andric InMemoryFileSystem::getRealPath(const Twine &Path,
9890b57cec5SDimitry Andric                                 SmallVectorImpl<char> &Output) const {
9900b57cec5SDimitry Andric   auto CWD = getCurrentWorkingDirectory();
9910b57cec5SDimitry Andric   if (!CWD || CWD->empty())
9920b57cec5SDimitry Andric     return errc::operation_not_permitted;
9930b57cec5SDimitry Andric   Path.toVector(Output);
9940b57cec5SDimitry Andric   if (auto EC = makeAbsolute(Output))
9950b57cec5SDimitry Andric     return EC;
9960b57cec5SDimitry Andric   llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true);
9970b57cec5SDimitry Andric   return {};
9980b57cec5SDimitry Andric }
9990b57cec5SDimitry Andric 
isLocal(const Twine & Path,bool & Result)10000b57cec5SDimitry Andric std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
10010b57cec5SDimitry Andric   Result = false;
10020b57cec5SDimitry Andric   return {};
10030b57cec5SDimitry Andric }
10040b57cec5SDimitry Andric 
10050b57cec5SDimitry Andric } // namespace vfs
10060b57cec5SDimitry Andric } // namespace llvm
10070b57cec5SDimitry Andric 
10080b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
10090b57cec5SDimitry Andric // RedirectingFileSystem implementation
10100b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
10110b57cec5SDimitry Andric 
10125ffd83dbSDimitry Andric namespace {
10135ffd83dbSDimitry Andric 
getExistingStyle(llvm::StringRef Path)1014*5f7ddb14SDimitry Andric static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) {
1015*5f7ddb14SDimitry Andric   // Detect the path style in use by checking the first separator.
10165ffd83dbSDimitry Andric   llvm::sys::path::Style style = llvm::sys::path::Style::native;
10175ffd83dbSDimitry Andric   const size_t n = Path.find_first_of("/\\");
10185ffd83dbSDimitry Andric   if (n != static_cast<size_t>(-1))
10195ffd83dbSDimitry Andric     style = (Path[n] == '/') ? llvm::sys::path::Style::posix
10205ffd83dbSDimitry Andric                              : llvm::sys::path::Style::windows;
1021*5f7ddb14SDimitry Andric   return style;
1022*5f7ddb14SDimitry Andric }
1023*5f7ddb14SDimitry Andric 
1024*5f7ddb14SDimitry Andric /// Removes leading "./" as well as path components like ".." and ".".
canonicalize(llvm::StringRef Path)1025*5f7ddb14SDimitry Andric static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {
1026*5f7ddb14SDimitry Andric   // First detect the path style in use by checking the first separator.
1027*5f7ddb14SDimitry Andric   llvm::sys::path::Style style = getExistingStyle(Path);
10285ffd83dbSDimitry Andric 
10295ffd83dbSDimitry Andric   // Now remove the dots.  Explicitly specifying the path style prevents the
10305ffd83dbSDimitry Andric   // direction of the slashes from changing.
10315ffd83dbSDimitry Andric   llvm::SmallString<256> result =
10325ffd83dbSDimitry Andric       llvm::sys::path::remove_leading_dotslash(Path, style);
10335ffd83dbSDimitry Andric   llvm::sys::path::remove_dots(result, /*remove_dot_dot=*/true, style);
10345ffd83dbSDimitry Andric   return result;
10355ffd83dbSDimitry Andric }
10365ffd83dbSDimitry Andric 
10375ffd83dbSDimitry Andric } // anonymous namespace
10385ffd83dbSDimitry Andric 
10395ffd83dbSDimitry Andric 
RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)10408bcb0991SDimitry Andric RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
10418bcb0991SDimitry Andric     : ExternalFS(std::move(FS)) {
10428bcb0991SDimitry Andric   if (ExternalFS)
10438bcb0991SDimitry Andric     if (auto ExternalWorkingDirectory =
10448bcb0991SDimitry Andric             ExternalFS->getCurrentWorkingDirectory()) {
10458bcb0991SDimitry Andric       WorkingDirectory = *ExternalWorkingDirectory;
10468bcb0991SDimitry Andric     }
10478bcb0991SDimitry Andric }
10488bcb0991SDimitry Andric 
1049*5f7ddb14SDimitry Andric /// Directory iterator implementation for \c RedirectingFileSystem's
1050*5f7ddb14SDimitry Andric /// directory entries.
1051*5f7ddb14SDimitry Andric class llvm::vfs::RedirectingFSDirIterImpl
10520b57cec5SDimitry Andric     : public llvm::vfs::detail::DirIterImpl {
10530b57cec5SDimitry Andric   std::string Dir;
1054*5f7ddb14SDimitry Andric   RedirectingFileSystem::DirectoryEntry::iterator Current, End;
10550b57cec5SDimitry Andric 
incrementImpl(bool IsFirstTime)1056*5f7ddb14SDimitry Andric   std::error_code incrementImpl(bool IsFirstTime) {
1057*5f7ddb14SDimitry Andric     assert((IsFirstTime || Current != End) && "cannot iterate past end");
1058*5f7ddb14SDimitry Andric     if (!IsFirstTime)
1059*5f7ddb14SDimitry Andric       ++Current;
1060*5f7ddb14SDimitry Andric     if (Current != End) {
1061*5f7ddb14SDimitry Andric       SmallString<128> PathStr(Dir);
1062*5f7ddb14SDimitry Andric       llvm::sys::path::append(PathStr, (*Current)->getName());
1063*5f7ddb14SDimitry Andric       sys::fs::file_type Type = sys::fs::file_type::type_unknown;
1064*5f7ddb14SDimitry Andric       switch ((*Current)->getKind()) {
1065*5f7ddb14SDimitry Andric       case RedirectingFileSystem::EK_Directory:
1066*5f7ddb14SDimitry Andric         LLVM_FALLTHROUGH;
1067*5f7ddb14SDimitry Andric       case RedirectingFileSystem::EK_DirectoryRemap:
1068*5f7ddb14SDimitry Andric         Type = sys::fs::file_type::directory_file;
1069*5f7ddb14SDimitry Andric         break;
1070*5f7ddb14SDimitry Andric       case RedirectingFileSystem::EK_File:
1071*5f7ddb14SDimitry Andric         Type = sys::fs::file_type::regular_file;
1072*5f7ddb14SDimitry Andric         break;
1073*5f7ddb14SDimitry Andric       }
1074*5f7ddb14SDimitry Andric       CurrentEntry = directory_entry(std::string(PathStr.str()), Type);
1075*5f7ddb14SDimitry Andric     } else {
1076*5f7ddb14SDimitry Andric       CurrentEntry = directory_entry();
1077*5f7ddb14SDimitry Andric     }
1078*5f7ddb14SDimitry Andric     return {};
1079*5f7ddb14SDimitry Andric   };
10800b57cec5SDimitry Andric 
10810b57cec5SDimitry Andric public:
RedirectingFSDirIterImpl(const Twine & Path,RedirectingFileSystem::DirectoryEntry::iterator Begin,RedirectingFileSystem::DirectoryEntry::iterator End,std::error_code & EC)1082*5f7ddb14SDimitry Andric   RedirectingFSDirIterImpl(
1083*5f7ddb14SDimitry Andric       const Twine &Path, RedirectingFileSystem::DirectoryEntry::iterator Begin,
1084*5f7ddb14SDimitry Andric       RedirectingFileSystem::DirectoryEntry::iterator End, std::error_code &EC)
1085*5f7ddb14SDimitry Andric       : Dir(Path.str()), Current(Begin), End(End) {
1086*5f7ddb14SDimitry Andric     EC = incrementImpl(/*IsFirstTime=*/true);
1087*5f7ddb14SDimitry Andric   }
10880b57cec5SDimitry Andric 
increment()1089*5f7ddb14SDimitry Andric   std::error_code increment() override {
1090*5f7ddb14SDimitry Andric     return incrementImpl(/*IsFirstTime=*/false);
1091*5f7ddb14SDimitry Andric   }
1092*5f7ddb14SDimitry Andric };
1093*5f7ddb14SDimitry Andric 
1094*5f7ddb14SDimitry Andric /// Directory iterator implementation for \c RedirectingFileSystem's
1095*5f7ddb14SDimitry Andric /// directory remap entries that maps the paths reported by the external
1096*5f7ddb14SDimitry Andric /// file system's directory iterator back to the virtual directory's path.
1097*5f7ddb14SDimitry Andric class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl {
1098*5f7ddb14SDimitry Andric   std::string Dir;
1099*5f7ddb14SDimitry Andric   llvm::sys::path::Style DirStyle;
1100*5f7ddb14SDimitry Andric   llvm::vfs::directory_iterator ExternalIter;
1101*5f7ddb14SDimitry Andric 
1102*5f7ddb14SDimitry Andric public:
RedirectingFSDirRemapIterImpl(std::string DirPath,llvm::vfs::directory_iterator ExtIter)1103*5f7ddb14SDimitry Andric   RedirectingFSDirRemapIterImpl(std::string DirPath,
1104*5f7ddb14SDimitry Andric                                 llvm::vfs::directory_iterator ExtIter)
1105*5f7ddb14SDimitry Andric       : Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)),
1106*5f7ddb14SDimitry Andric         ExternalIter(ExtIter) {
1107*5f7ddb14SDimitry Andric     if (ExternalIter != llvm::vfs::directory_iterator())
1108*5f7ddb14SDimitry Andric       setCurrentEntry();
1109*5f7ddb14SDimitry Andric   }
1110*5f7ddb14SDimitry Andric 
setCurrentEntry()1111*5f7ddb14SDimitry Andric   void setCurrentEntry() {
1112*5f7ddb14SDimitry Andric     StringRef ExternalPath = ExternalIter->path();
1113*5f7ddb14SDimitry Andric     llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath);
1114*5f7ddb14SDimitry Andric     StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle);
1115*5f7ddb14SDimitry Andric 
1116*5f7ddb14SDimitry Andric     SmallString<128> NewPath(Dir);
1117*5f7ddb14SDimitry Andric     llvm::sys::path::append(NewPath, DirStyle, File);
1118*5f7ddb14SDimitry Andric 
1119*5f7ddb14SDimitry Andric     CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type());
1120*5f7ddb14SDimitry Andric   }
1121*5f7ddb14SDimitry Andric 
increment()1122*5f7ddb14SDimitry Andric   std::error_code increment() override {
1123*5f7ddb14SDimitry Andric     std::error_code EC;
1124*5f7ddb14SDimitry Andric     ExternalIter.increment(EC);
1125*5f7ddb14SDimitry Andric     if (!EC && ExternalIter != llvm::vfs::directory_iterator())
1126*5f7ddb14SDimitry Andric       setCurrentEntry();
1127*5f7ddb14SDimitry Andric     else
1128*5f7ddb14SDimitry Andric       CurrentEntry = directory_entry();
1129*5f7ddb14SDimitry Andric     return EC;
1130*5f7ddb14SDimitry Andric   }
11310b57cec5SDimitry Andric };
11320b57cec5SDimitry Andric 
11330b57cec5SDimitry Andric llvm::ErrorOr<std::string>
getCurrentWorkingDirectory() const11340b57cec5SDimitry Andric RedirectingFileSystem::getCurrentWorkingDirectory() const {
11358bcb0991SDimitry Andric   return WorkingDirectory;
11360b57cec5SDimitry Andric }
11370b57cec5SDimitry Andric 
11380b57cec5SDimitry Andric std::error_code
setCurrentWorkingDirectory(const Twine & Path)11390b57cec5SDimitry Andric RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
11408bcb0991SDimitry Andric   // Don't change the working directory if the path doesn't exist.
11418bcb0991SDimitry Andric   if (!exists(Path))
11428bcb0991SDimitry Andric     return errc::no_such_file_or_directory;
11438bcb0991SDimitry Andric 
11448bcb0991SDimitry Andric   SmallString<128> AbsolutePath;
11458bcb0991SDimitry Andric   Path.toVector(AbsolutePath);
11468bcb0991SDimitry Andric   if (std::error_code EC = makeAbsolute(AbsolutePath))
11478bcb0991SDimitry Andric     return EC;
11485ffd83dbSDimitry Andric   WorkingDirectory = std::string(AbsolutePath.str());
11498bcb0991SDimitry Andric   return {};
11500b57cec5SDimitry Andric }
11510b57cec5SDimitry Andric 
isLocal(const Twine & Path_,bool & Result)1152af732203SDimitry Andric std::error_code RedirectingFileSystem::isLocal(const Twine &Path_,
11530b57cec5SDimitry Andric                                                bool &Result) {
1154af732203SDimitry Andric   SmallString<256> Path;
1155af732203SDimitry Andric   Path_.toVector(Path);
1156af732203SDimitry Andric 
1157af732203SDimitry Andric   if (std::error_code EC = makeCanonical(Path))
1158af732203SDimitry Andric     return {};
1159af732203SDimitry Andric 
11600b57cec5SDimitry Andric   return ExternalFS->isLocal(Path, Result);
11610b57cec5SDimitry Andric }
11620b57cec5SDimitry Andric 
makeAbsolute(SmallVectorImpl<char> & Path) const1163480093f4SDimitry Andric std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1164480093f4SDimitry Andric   if (llvm::sys::path::is_absolute(Path, llvm::sys::path::Style::posix) ||
1165480093f4SDimitry Andric       llvm::sys::path::is_absolute(Path, llvm::sys::path::Style::windows))
1166480093f4SDimitry Andric     return {};
1167480093f4SDimitry Andric 
1168480093f4SDimitry Andric   auto WorkingDir = getCurrentWorkingDirectory();
1169480093f4SDimitry Andric   if (!WorkingDir)
1170480093f4SDimitry Andric     return WorkingDir.getError();
1171480093f4SDimitry Andric 
11725ffd83dbSDimitry Andric   // We can't use sys::fs::make_absolute because that assumes the path style
11735ffd83dbSDimitry Andric   // is native and there is no way to override that.  Since we know WorkingDir
11745ffd83dbSDimitry Andric   // is absolute, we can use it to determine which style we actually have and
11755ffd83dbSDimitry Andric   // append Path ourselves.
11765ffd83dbSDimitry Andric   sys::path::Style style = sys::path::Style::windows;
11775ffd83dbSDimitry Andric   if (sys::path::is_absolute(WorkingDir.get(), sys::path::Style::posix)) {
11785ffd83dbSDimitry Andric     style = sys::path::Style::posix;
11795ffd83dbSDimitry Andric   }
11805ffd83dbSDimitry Andric 
11815ffd83dbSDimitry Andric   std::string Result = WorkingDir.get();
11825ffd83dbSDimitry Andric   StringRef Dir(Result);
11835ffd83dbSDimitry Andric   if (!Dir.endswith(sys::path::get_separator(style))) {
11845ffd83dbSDimitry Andric     Result += sys::path::get_separator(style);
11855ffd83dbSDimitry Andric   }
11865ffd83dbSDimitry Andric   Result.append(Path.data(), Path.size());
11875ffd83dbSDimitry Andric   Path.assign(Result.begin(), Result.end());
11885ffd83dbSDimitry Andric 
1189480093f4SDimitry Andric   return {};
1190480093f4SDimitry Andric }
1191480093f4SDimitry Andric 
dir_begin(const Twine & Dir,std::error_code & EC)11920b57cec5SDimitry Andric directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
11930b57cec5SDimitry Andric                                                     std::error_code &EC) {
1194af732203SDimitry Andric   SmallString<256> Path;
1195af732203SDimitry Andric   Dir.toVector(Path);
1196af732203SDimitry Andric 
1197af732203SDimitry Andric   EC = makeCanonical(Path);
1198af732203SDimitry Andric   if (EC)
1199af732203SDimitry Andric     return {};
1200af732203SDimitry Andric 
1201*5f7ddb14SDimitry Andric   ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
1202*5f7ddb14SDimitry Andric   if (!Result) {
1203*5f7ddb14SDimitry Andric     EC = Result.getError();
1204*5f7ddb14SDimitry Andric     if (shouldFallBackToExternalFS(EC))
1205af732203SDimitry Andric       return ExternalFS->dir_begin(Path, EC);
12060b57cec5SDimitry Andric     return {};
12070b57cec5SDimitry Andric   }
1208*5f7ddb14SDimitry Andric 
1209*5f7ddb14SDimitry Andric   // Use status to make sure the path exists and refers to a directory.
1210*5f7ddb14SDimitry Andric   ErrorOr<Status> S = status(Path, *Result);
12110b57cec5SDimitry Andric   if (!S) {
1212*5f7ddb14SDimitry Andric     if (shouldFallBackToExternalFS(S.getError(), Result->E))
1213*5f7ddb14SDimitry Andric       return ExternalFS->dir_begin(Dir, EC);
12140b57cec5SDimitry Andric     EC = S.getError();
12150b57cec5SDimitry Andric     return {};
12160b57cec5SDimitry Andric   }
12170b57cec5SDimitry Andric   if (!S->isDirectory()) {
12180b57cec5SDimitry Andric     EC = std::error_code(static_cast<int>(errc::not_a_directory),
12190b57cec5SDimitry Andric                          std::system_category());
12200b57cec5SDimitry Andric     return {};
12210b57cec5SDimitry Andric   }
12220b57cec5SDimitry Andric 
1223*5f7ddb14SDimitry Andric   // Create the appropriate directory iterator based on whether we found a
1224*5f7ddb14SDimitry Andric   // DirectoryRemapEntry or DirectoryEntry.
1225*5f7ddb14SDimitry Andric   directory_iterator DirIter;
1226*5f7ddb14SDimitry Andric   if (auto ExtRedirect = Result->getExternalRedirect()) {
1227*5f7ddb14SDimitry Andric     auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
1228*5f7ddb14SDimitry Andric     DirIter = ExternalFS->dir_begin(*ExtRedirect, EC);
1229*5f7ddb14SDimitry Andric 
1230*5f7ddb14SDimitry Andric     if (!RE->useExternalName(UseExternalNames)) {
1231*5f7ddb14SDimitry Andric       // Update the paths in the results to use the virtual directory's path.
1232*5f7ddb14SDimitry Andric       DirIter =
1233*5f7ddb14SDimitry Andric           directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>(
1234*5f7ddb14SDimitry Andric               std::string(Path), DirIter));
1235*5f7ddb14SDimitry Andric     }
1236*5f7ddb14SDimitry Andric   } else {
1237*5f7ddb14SDimitry Andric     auto DE = cast<DirectoryEntry>(Result->E);
1238*5f7ddb14SDimitry Andric     DirIter = directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
1239*5f7ddb14SDimitry Andric         Path, DE->contents_begin(), DE->contents_end(), EC));
1240*5f7ddb14SDimitry Andric   }
1241*5f7ddb14SDimitry Andric 
1242*5f7ddb14SDimitry Andric   if (!shouldUseExternalFS())
1243*5f7ddb14SDimitry Andric     return DirIter;
1244*5f7ddb14SDimitry Andric   return directory_iterator(std::make_shared<CombiningDirIterImpl>(
1245*5f7ddb14SDimitry Andric       DirIter, ExternalFS, std::string(Path), EC));
12460b57cec5SDimitry Andric }
12470b57cec5SDimitry Andric 
setExternalContentsPrefixDir(StringRef PrefixDir)12480b57cec5SDimitry Andric void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) {
12490b57cec5SDimitry Andric   ExternalContentsPrefixDir = PrefixDir.str();
12500b57cec5SDimitry Andric }
12510b57cec5SDimitry Andric 
getExternalContentsPrefixDir() const12520b57cec5SDimitry Andric StringRef RedirectingFileSystem::getExternalContentsPrefixDir() const {
12530b57cec5SDimitry Andric   return ExternalContentsPrefixDir;
12540b57cec5SDimitry Andric }
12550b57cec5SDimitry Andric 
setFallthrough(bool Fallthrough)1256af732203SDimitry Andric void RedirectingFileSystem::setFallthrough(bool Fallthrough) {
1257af732203SDimitry Andric   IsFallthrough = Fallthrough;
1258af732203SDimitry Andric }
1259af732203SDimitry Andric 
getRoots() const1260af732203SDimitry Andric std::vector<StringRef> RedirectingFileSystem::getRoots() const {
1261af732203SDimitry Andric   std::vector<StringRef> R;
1262af732203SDimitry Andric   for (const auto &Root : Roots)
1263af732203SDimitry Andric     R.push_back(Root->getName());
1264af732203SDimitry Andric   return R;
1265af732203SDimitry Andric }
1266af732203SDimitry Andric 
dump(raw_ostream & OS) const12678bcb0991SDimitry Andric void RedirectingFileSystem::dump(raw_ostream &OS) const {
12680b57cec5SDimitry Andric   for (const auto &Root : Roots)
12698bcb0991SDimitry Andric     dumpEntry(OS, Root.get());
12700b57cec5SDimitry Andric }
12710b57cec5SDimitry Andric 
dumpEntry(raw_ostream & OS,RedirectingFileSystem::Entry * E,int NumSpaces) const12728bcb0991SDimitry Andric void RedirectingFileSystem::dumpEntry(raw_ostream &OS,
12738bcb0991SDimitry Andric                                       RedirectingFileSystem::Entry *E,
12740b57cec5SDimitry Andric                                       int NumSpaces) const {
12750b57cec5SDimitry Andric   StringRef Name = E->getName();
12760b57cec5SDimitry Andric   for (int i = 0, e = NumSpaces; i < e; ++i)
12778bcb0991SDimitry Andric     OS << " ";
12788bcb0991SDimitry Andric   OS << "'" << Name.str().c_str() << "'"
12790b57cec5SDimitry Andric      << "\n";
12800b57cec5SDimitry Andric 
12810b57cec5SDimitry Andric   if (E->getKind() == RedirectingFileSystem::EK_Directory) {
1282*5f7ddb14SDimitry Andric     auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(E);
12830b57cec5SDimitry Andric     assert(DE && "Should be a directory");
12840b57cec5SDimitry Andric 
12850b57cec5SDimitry Andric     for (std::unique_ptr<Entry> &SubEntry :
12860b57cec5SDimitry Andric          llvm::make_range(DE->contents_begin(), DE->contents_end()))
12878bcb0991SDimitry Andric       dumpEntry(OS, SubEntry.get(), NumSpaces + 2);
12880b57cec5SDimitry Andric   }
12890b57cec5SDimitry Andric }
12908bcb0991SDimitry Andric 
12918bcb0991SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
dump() const12928bcb0991SDimitry Andric LLVM_DUMP_METHOD void RedirectingFileSystem::dump() const { dump(dbgs()); }
12930b57cec5SDimitry Andric #endif
12940b57cec5SDimitry Andric 
12950b57cec5SDimitry Andric /// A helper class to hold the common YAML parsing state.
12960b57cec5SDimitry Andric class llvm::vfs::RedirectingFileSystemParser {
12970b57cec5SDimitry Andric   yaml::Stream &Stream;
12980b57cec5SDimitry Andric 
error(yaml::Node * N,const Twine & Msg)12990b57cec5SDimitry Andric   void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); }
13000b57cec5SDimitry Andric 
13010b57cec5SDimitry Andric   // false on error
parseScalarString(yaml::Node * N,StringRef & Result,SmallVectorImpl<char> & Storage)13020b57cec5SDimitry Andric   bool parseScalarString(yaml::Node *N, StringRef &Result,
13030b57cec5SDimitry Andric                          SmallVectorImpl<char> &Storage) {
13040b57cec5SDimitry Andric     const auto *S = dyn_cast<yaml::ScalarNode>(N);
13050b57cec5SDimitry Andric 
13060b57cec5SDimitry Andric     if (!S) {
13070b57cec5SDimitry Andric       error(N, "expected string");
13080b57cec5SDimitry Andric       return false;
13090b57cec5SDimitry Andric     }
13100b57cec5SDimitry Andric     Result = S->getValue(Storage);
13110b57cec5SDimitry Andric     return true;
13120b57cec5SDimitry Andric   }
13130b57cec5SDimitry Andric 
13140b57cec5SDimitry Andric   // false on error
parseScalarBool(yaml::Node * N,bool & Result)13150b57cec5SDimitry Andric   bool parseScalarBool(yaml::Node *N, bool &Result) {
13160b57cec5SDimitry Andric     SmallString<5> Storage;
13170b57cec5SDimitry Andric     StringRef Value;
13180b57cec5SDimitry Andric     if (!parseScalarString(N, Value, Storage))
13190b57cec5SDimitry Andric       return false;
13200b57cec5SDimitry Andric 
1321*5f7ddb14SDimitry Andric     if (Value.equals_insensitive("true") || Value.equals_insensitive("on") ||
1322*5f7ddb14SDimitry Andric         Value.equals_insensitive("yes") || Value == "1") {
13230b57cec5SDimitry Andric       Result = true;
13240b57cec5SDimitry Andric       return true;
1325*5f7ddb14SDimitry Andric     } else if (Value.equals_insensitive("false") ||
1326*5f7ddb14SDimitry Andric                Value.equals_insensitive("off") ||
1327*5f7ddb14SDimitry Andric                Value.equals_insensitive("no") || Value == "0") {
13280b57cec5SDimitry Andric       Result = false;
13290b57cec5SDimitry Andric       return true;
13300b57cec5SDimitry Andric     }
13310b57cec5SDimitry Andric 
13320b57cec5SDimitry Andric     error(N, "expected boolean value");
13330b57cec5SDimitry Andric     return false;
13340b57cec5SDimitry Andric   }
13350b57cec5SDimitry Andric 
13360b57cec5SDimitry Andric   struct KeyStatus {
13370b57cec5SDimitry Andric     bool Required;
13380b57cec5SDimitry Andric     bool Seen = false;
13390b57cec5SDimitry Andric 
KeyStatusllvm::vfs::RedirectingFileSystemParser::KeyStatus13400b57cec5SDimitry Andric     KeyStatus(bool Required = false) : Required(Required) {}
13410b57cec5SDimitry Andric   };
13420b57cec5SDimitry Andric 
13430b57cec5SDimitry Andric   using KeyStatusPair = std::pair<StringRef, KeyStatus>;
13440b57cec5SDimitry Andric 
13450b57cec5SDimitry Andric   // false on error
checkDuplicateOrUnknownKey(yaml::Node * KeyNode,StringRef Key,DenseMap<StringRef,KeyStatus> & Keys)13460b57cec5SDimitry Andric   bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
13470b57cec5SDimitry Andric                                   DenseMap<StringRef, KeyStatus> &Keys) {
13480b57cec5SDimitry Andric     if (!Keys.count(Key)) {
13490b57cec5SDimitry Andric       error(KeyNode, "unknown key");
13500b57cec5SDimitry Andric       return false;
13510b57cec5SDimitry Andric     }
13520b57cec5SDimitry Andric     KeyStatus &S = Keys[Key];
13530b57cec5SDimitry Andric     if (S.Seen) {
13540b57cec5SDimitry Andric       error(KeyNode, Twine("duplicate key '") + Key + "'");
13550b57cec5SDimitry Andric       return false;
13560b57cec5SDimitry Andric     }
13570b57cec5SDimitry Andric     S.Seen = true;
13580b57cec5SDimitry Andric     return true;
13590b57cec5SDimitry Andric   }
13600b57cec5SDimitry Andric 
13610b57cec5SDimitry Andric   // false on error
checkMissingKeys(yaml::Node * Obj,DenseMap<StringRef,KeyStatus> & Keys)13620b57cec5SDimitry Andric   bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
13630b57cec5SDimitry Andric     for (const auto &I : Keys) {
13640b57cec5SDimitry Andric       if (I.second.Required && !I.second.Seen) {
13650b57cec5SDimitry Andric         error(Obj, Twine("missing key '") + I.first + "'");
13660b57cec5SDimitry Andric         return false;
13670b57cec5SDimitry Andric       }
13680b57cec5SDimitry Andric     }
13690b57cec5SDimitry Andric     return true;
13700b57cec5SDimitry Andric   }
13710b57cec5SDimitry Andric 
1372af732203SDimitry Andric public:
1373af732203SDimitry Andric   static RedirectingFileSystem::Entry *
lookupOrCreateEntry(RedirectingFileSystem * FS,StringRef Name,RedirectingFileSystem::Entry * ParentEntry=nullptr)13740b57cec5SDimitry Andric   lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,
13750b57cec5SDimitry Andric                       RedirectingFileSystem::Entry *ParentEntry = nullptr) {
13760b57cec5SDimitry Andric     if (!ParentEntry) { // Look for a existent root
13770b57cec5SDimitry Andric       for (const auto &Root : FS->Roots) {
13780b57cec5SDimitry Andric         if (Name.equals(Root->getName())) {
13790b57cec5SDimitry Andric           ParentEntry = Root.get();
13800b57cec5SDimitry Andric           return ParentEntry;
13810b57cec5SDimitry Andric         }
13820b57cec5SDimitry Andric       }
13830b57cec5SDimitry Andric     } else { // Advance to the next component
1384*5f7ddb14SDimitry Andric       auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
13850b57cec5SDimitry Andric       for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :
13860b57cec5SDimitry Andric            llvm::make_range(DE->contents_begin(), DE->contents_end())) {
13870b57cec5SDimitry Andric         auto *DirContent =
1388*5f7ddb14SDimitry Andric             dyn_cast<RedirectingFileSystem::DirectoryEntry>(Content.get());
13890b57cec5SDimitry Andric         if (DirContent && Name.equals(Content->getName()))
13900b57cec5SDimitry Andric           return DirContent;
13910b57cec5SDimitry Andric       }
13920b57cec5SDimitry Andric     }
13930b57cec5SDimitry Andric 
13940b57cec5SDimitry Andric     // ... or create a new one
13950b57cec5SDimitry Andric     std::unique_ptr<RedirectingFileSystem::Entry> E =
1396*5f7ddb14SDimitry Andric         std::make_unique<RedirectingFileSystem::DirectoryEntry>(
13970b57cec5SDimitry Andric             Name, Status("", getNextVirtualUniqueID(),
13980b57cec5SDimitry Andric                          std::chrono::system_clock::now(), 0, 0, 0,
13990b57cec5SDimitry Andric                          file_type::directory_file, sys::fs::all_all));
14000b57cec5SDimitry Andric 
14010b57cec5SDimitry Andric     if (!ParentEntry) { // Add a new root to the overlay
14020b57cec5SDimitry Andric       FS->Roots.push_back(std::move(E));
14030b57cec5SDimitry Andric       ParentEntry = FS->Roots.back().get();
14040b57cec5SDimitry Andric       return ParentEntry;
14050b57cec5SDimitry Andric     }
14060b57cec5SDimitry Andric 
1407*5f7ddb14SDimitry Andric     auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
14080b57cec5SDimitry Andric     DE->addContent(std::move(E));
14090b57cec5SDimitry Andric     return DE->getLastContent();
14100b57cec5SDimitry Andric   }
14110b57cec5SDimitry Andric 
1412af732203SDimitry Andric private:
uniqueOverlayTree(RedirectingFileSystem * FS,RedirectingFileSystem::Entry * SrcE,RedirectingFileSystem::Entry * NewParentE=nullptr)14130b57cec5SDimitry Andric   void uniqueOverlayTree(RedirectingFileSystem *FS,
14140b57cec5SDimitry Andric                          RedirectingFileSystem::Entry *SrcE,
14150b57cec5SDimitry Andric                          RedirectingFileSystem::Entry *NewParentE = nullptr) {
14160b57cec5SDimitry Andric     StringRef Name = SrcE->getName();
14170b57cec5SDimitry Andric     switch (SrcE->getKind()) {
14180b57cec5SDimitry Andric     case RedirectingFileSystem::EK_Directory: {
1419*5f7ddb14SDimitry Andric       auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
14200b57cec5SDimitry Andric       // Empty directories could be present in the YAML as a way to
14210b57cec5SDimitry Andric       // describe a file for a current directory after some of its subdir
14220b57cec5SDimitry Andric       // is parsed. This only leads to redundant walks, ignore it.
14230b57cec5SDimitry Andric       if (!Name.empty())
14240b57cec5SDimitry Andric         NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
14250b57cec5SDimitry Andric       for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
14260b57cec5SDimitry Andric            llvm::make_range(DE->contents_begin(), DE->contents_end()))
14270b57cec5SDimitry Andric         uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
14280b57cec5SDimitry Andric       break;
14290b57cec5SDimitry Andric     }
1430*5f7ddb14SDimitry Andric     case RedirectingFileSystem::EK_DirectoryRemap: {
1431*5f7ddb14SDimitry Andric       assert(NewParentE && "Parent entry must exist");
1432*5f7ddb14SDimitry Andric       auto *DR = cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
1433*5f7ddb14SDimitry Andric       auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1434*5f7ddb14SDimitry Andric       DE->addContent(
1435*5f7ddb14SDimitry Andric           std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
1436*5f7ddb14SDimitry Andric               Name, DR->getExternalContentsPath(), DR->getUseName()));
1437*5f7ddb14SDimitry Andric       break;
1438*5f7ddb14SDimitry Andric     }
14390b57cec5SDimitry Andric     case RedirectingFileSystem::EK_File: {
14400b57cec5SDimitry Andric       assert(NewParentE && "Parent entry must exist");
1441*5f7ddb14SDimitry Andric       auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);
1442*5f7ddb14SDimitry Andric       auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1443*5f7ddb14SDimitry Andric       DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>(
14440b57cec5SDimitry Andric           Name, FE->getExternalContentsPath(), FE->getUseName()));
14450b57cec5SDimitry Andric       break;
14460b57cec5SDimitry Andric     }
14470b57cec5SDimitry Andric     }
14480b57cec5SDimitry Andric   }
14490b57cec5SDimitry Andric 
14500b57cec5SDimitry Andric   std::unique_ptr<RedirectingFileSystem::Entry>
parseEntry(yaml::Node * N,RedirectingFileSystem * FS,bool IsRootEntry)14510b57cec5SDimitry Andric   parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) {
14520b57cec5SDimitry Andric     auto *M = dyn_cast<yaml::MappingNode>(N);
14530b57cec5SDimitry Andric     if (!M) {
14540b57cec5SDimitry Andric       error(N, "expected mapping node for file or directory entry");
14550b57cec5SDimitry Andric       return nullptr;
14560b57cec5SDimitry Andric     }
14570b57cec5SDimitry Andric 
14580b57cec5SDimitry Andric     KeyStatusPair Fields[] = {
14590b57cec5SDimitry Andric         KeyStatusPair("name", true),
14600b57cec5SDimitry Andric         KeyStatusPair("type", true),
14610b57cec5SDimitry Andric         KeyStatusPair("contents", false),
14620b57cec5SDimitry Andric         KeyStatusPair("external-contents", false),
14630b57cec5SDimitry Andric         KeyStatusPair("use-external-name", false),
14640b57cec5SDimitry Andric     };
14650b57cec5SDimitry Andric 
14660b57cec5SDimitry Andric     DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
14670b57cec5SDimitry Andric 
1468*5f7ddb14SDimitry Andric     enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;
14690b57cec5SDimitry Andric     std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
14700b57cec5SDimitry Andric         EntryArrayContents;
14715ffd83dbSDimitry Andric     SmallString<256> ExternalContentsPath;
14725ffd83dbSDimitry Andric     SmallString<256> Name;
14730b57cec5SDimitry Andric     yaml::Node *NameValueNode = nullptr;
1474*5f7ddb14SDimitry Andric     auto UseExternalName = RedirectingFileSystem::NK_NotSet;
14750b57cec5SDimitry Andric     RedirectingFileSystem::EntryKind Kind;
14760b57cec5SDimitry Andric 
14770b57cec5SDimitry Andric     for (auto &I : *M) {
14780b57cec5SDimitry Andric       StringRef Key;
14790b57cec5SDimitry Andric       // Reuse the buffer for key and value, since we don't look at key after
14800b57cec5SDimitry Andric       // parsing value.
14810b57cec5SDimitry Andric       SmallString<256> Buffer;
14820b57cec5SDimitry Andric       if (!parseScalarString(I.getKey(), Key, Buffer))
14830b57cec5SDimitry Andric         return nullptr;
14840b57cec5SDimitry Andric 
14850b57cec5SDimitry Andric       if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
14860b57cec5SDimitry Andric         return nullptr;
14870b57cec5SDimitry Andric 
14880b57cec5SDimitry Andric       StringRef Value;
14890b57cec5SDimitry Andric       if (Key == "name") {
14900b57cec5SDimitry Andric         if (!parseScalarString(I.getValue(), Value, Buffer))
14910b57cec5SDimitry Andric           return nullptr;
14920b57cec5SDimitry Andric 
14930b57cec5SDimitry Andric         NameValueNode = I.getValue();
14940b57cec5SDimitry Andric         // Guarantee that old YAML files containing paths with ".." and "."
14950b57cec5SDimitry Andric         // are properly canonicalized before read into the VFS.
14965ffd83dbSDimitry Andric         Name = canonicalize(Value).str();
14970b57cec5SDimitry Andric       } else if (Key == "type") {
14980b57cec5SDimitry Andric         if (!parseScalarString(I.getValue(), Value, Buffer))
14990b57cec5SDimitry Andric           return nullptr;
15000b57cec5SDimitry Andric         if (Value == "file")
15010b57cec5SDimitry Andric           Kind = RedirectingFileSystem::EK_File;
15020b57cec5SDimitry Andric         else if (Value == "directory")
15030b57cec5SDimitry Andric           Kind = RedirectingFileSystem::EK_Directory;
1504*5f7ddb14SDimitry Andric         else if (Value == "directory-remap")
1505*5f7ddb14SDimitry Andric           Kind = RedirectingFileSystem::EK_DirectoryRemap;
15060b57cec5SDimitry Andric         else {
15070b57cec5SDimitry Andric           error(I.getValue(), "unknown value for 'type'");
15080b57cec5SDimitry Andric           return nullptr;
15090b57cec5SDimitry Andric         }
15100b57cec5SDimitry Andric       } else if (Key == "contents") {
1511*5f7ddb14SDimitry Andric         if (ContentsField != CF_NotSet) {
15120b57cec5SDimitry Andric           error(I.getKey(),
15130b57cec5SDimitry Andric                 "entry already has 'contents' or 'external-contents'");
15140b57cec5SDimitry Andric           return nullptr;
15150b57cec5SDimitry Andric         }
1516*5f7ddb14SDimitry Andric         ContentsField = CF_List;
15170b57cec5SDimitry Andric         auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
15180b57cec5SDimitry Andric         if (!Contents) {
15190b57cec5SDimitry Andric           // FIXME: this is only for directories, what about files?
15200b57cec5SDimitry Andric           error(I.getValue(), "expected array");
15210b57cec5SDimitry Andric           return nullptr;
15220b57cec5SDimitry Andric         }
15230b57cec5SDimitry Andric 
15240b57cec5SDimitry Andric         for (auto &I : *Contents) {
15250b57cec5SDimitry Andric           if (std::unique_ptr<RedirectingFileSystem::Entry> E =
15260b57cec5SDimitry Andric                   parseEntry(&I, FS, /*IsRootEntry*/ false))
15270b57cec5SDimitry Andric             EntryArrayContents.push_back(std::move(E));
15280b57cec5SDimitry Andric           else
15290b57cec5SDimitry Andric             return nullptr;
15300b57cec5SDimitry Andric         }
15310b57cec5SDimitry Andric       } else if (Key == "external-contents") {
1532*5f7ddb14SDimitry Andric         if (ContentsField != CF_NotSet) {
15330b57cec5SDimitry Andric           error(I.getKey(),
15340b57cec5SDimitry Andric                 "entry already has 'contents' or 'external-contents'");
15350b57cec5SDimitry Andric           return nullptr;
15360b57cec5SDimitry Andric         }
1537*5f7ddb14SDimitry Andric         ContentsField = CF_External;
15380b57cec5SDimitry Andric         if (!parseScalarString(I.getValue(), Value, Buffer))
15390b57cec5SDimitry Andric           return nullptr;
15400b57cec5SDimitry Andric 
15410b57cec5SDimitry Andric         SmallString<256> FullPath;
15420b57cec5SDimitry Andric         if (FS->IsRelativeOverlay) {
15430b57cec5SDimitry Andric           FullPath = FS->getExternalContentsPrefixDir();
15440b57cec5SDimitry Andric           assert(!FullPath.empty() &&
15450b57cec5SDimitry Andric                  "External contents prefix directory must exist");
15460b57cec5SDimitry Andric           llvm::sys::path::append(FullPath, Value);
15470b57cec5SDimitry Andric         } else {
15480b57cec5SDimitry Andric           FullPath = Value;
15490b57cec5SDimitry Andric         }
15500b57cec5SDimitry Andric 
15510b57cec5SDimitry Andric         // Guarantee that old YAML files containing paths with ".." and "."
15520b57cec5SDimitry Andric         // are properly canonicalized before read into the VFS.
15535ffd83dbSDimitry Andric         FullPath = canonicalize(FullPath);
15540b57cec5SDimitry Andric         ExternalContentsPath = FullPath.str();
15550b57cec5SDimitry Andric       } else if (Key == "use-external-name") {
15560b57cec5SDimitry Andric         bool Val;
15570b57cec5SDimitry Andric         if (!parseScalarBool(I.getValue(), Val))
15580b57cec5SDimitry Andric           return nullptr;
1559*5f7ddb14SDimitry Andric         UseExternalName = Val ? RedirectingFileSystem::NK_External
1560*5f7ddb14SDimitry Andric                               : RedirectingFileSystem::NK_Virtual;
15610b57cec5SDimitry Andric       } else {
15620b57cec5SDimitry Andric         llvm_unreachable("key missing from Keys");
15630b57cec5SDimitry Andric       }
15640b57cec5SDimitry Andric     }
15650b57cec5SDimitry Andric 
15660b57cec5SDimitry Andric     if (Stream.failed())
15670b57cec5SDimitry Andric       return nullptr;
15680b57cec5SDimitry Andric 
15690b57cec5SDimitry Andric     // check for missing keys
1570*5f7ddb14SDimitry Andric     if (ContentsField == CF_NotSet) {
15710b57cec5SDimitry Andric       error(N, "missing key 'contents' or 'external-contents'");
15720b57cec5SDimitry Andric       return nullptr;
15730b57cec5SDimitry Andric     }
15740b57cec5SDimitry Andric     if (!checkMissingKeys(N, Keys))
15750b57cec5SDimitry Andric       return nullptr;
15760b57cec5SDimitry Andric 
15770b57cec5SDimitry Andric     // check invalid configuration
15780b57cec5SDimitry Andric     if (Kind == RedirectingFileSystem::EK_Directory &&
1579*5f7ddb14SDimitry Andric         UseExternalName != RedirectingFileSystem::NK_NotSet) {
1580*5f7ddb14SDimitry Andric       error(N, "'use-external-name' is not supported for 'directory' entries");
1581*5f7ddb14SDimitry Andric       return nullptr;
1582*5f7ddb14SDimitry Andric     }
1583*5f7ddb14SDimitry Andric 
1584*5f7ddb14SDimitry Andric     if (Kind == RedirectingFileSystem::EK_DirectoryRemap &&
1585*5f7ddb14SDimitry Andric         ContentsField == CF_List) {
1586*5f7ddb14SDimitry Andric       error(N, "'contents' is not supported for 'directory-remap' entries");
15870b57cec5SDimitry Andric       return nullptr;
15880b57cec5SDimitry Andric     }
15890b57cec5SDimitry Andric 
1590480093f4SDimitry Andric     sys::path::Style path_style = sys::path::Style::native;
1591480093f4SDimitry Andric     if (IsRootEntry) {
1592480093f4SDimitry Andric       // VFS root entries may be in either Posix or Windows style.  Figure out
1593480093f4SDimitry Andric       // which style we have, and use it consistently.
1594480093f4SDimitry Andric       if (sys::path::is_absolute(Name, sys::path::Style::posix)) {
1595480093f4SDimitry Andric         path_style = sys::path::Style::posix;
1596480093f4SDimitry Andric       } else if (sys::path::is_absolute(Name, sys::path::Style::windows)) {
1597480093f4SDimitry Andric         path_style = sys::path::Style::windows;
1598480093f4SDimitry Andric       } else {
15990b57cec5SDimitry Andric         assert(NameValueNode && "Name presence should be checked earlier");
16000b57cec5SDimitry Andric         error(NameValueNode,
16010b57cec5SDimitry Andric               "entry with relative path at the root level is not discoverable");
16020b57cec5SDimitry Andric         return nullptr;
16030b57cec5SDimitry Andric       }
1604480093f4SDimitry Andric     }
16050b57cec5SDimitry Andric 
16060b57cec5SDimitry Andric     // Remove trailing slash(es), being careful not to remove the root path
1607*5f7ddb14SDimitry Andric     StringRef Trimmed = Name;
1608480093f4SDimitry Andric     size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();
16090b57cec5SDimitry Andric     while (Trimmed.size() > RootPathLen &&
1610480093f4SDimitry Andric            sys::path::is_separator(Trimmed.back(), path_style))
16110b57cec5SDimitry Andric       Trimmed = Trimmed.slice(0, Trimmed.size() - 1);
1612480093f4SDimitry Andric 
16130b57cec5SDimitry Andric     // Get the last component
1614480093f4SDimitry Andric     StringRef LastComponent = sys::path::filename(Trimmed, path_style);
16150b57cec5SDimitry Andric 
16160b57cec5SDimitry Andric     std::unique_ptr<RedirectingFileSystem::Entry> Result;
16170b57cec5SDimitry Andric     switch (Kind) {
16180b57cec5SDimitry Andric     case RedirectingFileSystem::EK_File:
1619*5f7ddb14SDimitry Andric       Result = std::make_unique<RedirectingFileSystem::FileEntry>(
1620*5f7ddb14SDimitry Andric           LastComponent, std::move(ExternalContentsPath), UseExternalName);
1621*5f7ddb14SDimitry Andric       break;
1622*5f7ddb14SDimitry Andric     case RedirectingFileSystem::EK_DirectoryRemap:
1623*5f7ddb14SDimitry Andric       Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
16240b57cec5SDimitry Andric           LastComponent, std::move(ExternalContentsPath), UseExternalName);
16250b57cec5SDimitry Andric       break;
16260b57cec5SDimitry Andric     case RedirectingFileSystem::EK_Directory:
1627*5f7ddb14SDimitry Andric       Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
16280b57cec5SDimitry Andric           LastComponent, std::move(EntryArrayContents),
1629*5f7ddb14SDimitry Andric           Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1630*5f7ddb14SDimitry Andric                  0, 0, 0, file_type::directory_file, sys::fs::all_all));
16310b57cec5SDimitry Andric       break;
16320b57cec5SDimitry Andric     }
16330b57cec5SDimitry Andric 
1634480093f4SDimitry Andric     StringRef Parent = sys::path::parent_path(Trimmed, path_style);
16350b57cec5SDimitry Andric     if (Parent.empty())
16360b57cec5SDimitry Andric       return Result;
16370b57cec5SDimitry Andric 
16380b57cec5SDimitry Andric     // if 'name' contains multiple components, create implicit directory entries
1639480093f4SDimitry Andric     for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style),
16400b57cec5SDimitry Andric                                      E = sys::path::rend(Parent);
16410b57cec5SDimitry Andric          I != E; ++I) {
16420b57cec5SDimitry Andric       std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;
16430b57cec5SDimitry Andric       Entries.push_back(std::move(Result));
1644*5f7ddb14SDimitry Andric       Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
16450b57cec5SDimitry Andric           *I, std::move(Entries),
1646*5f7ddb14SDimitry Andric           Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1647*5f7ddb14SDimitry Andric                  0, 0, 0, file_type::directory_file, sys::fs::all_all));
16480b57cec5SDimitry Andric     }
16490b57cec5SDimitry Andric     return Result;
16500b57cec5SDimitry Andric   }
16510b57cec5SDimitry Andric 
16520b57cec5SDimitry Andric public:
RedirectingFileSystemParser(yaml::Stream & S)16530b57cec5SDimitry Andric   RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
16540b57cec5SDimitry Andric 
16550b57cec5SDimitry Andric   // false on error
parse(yaml::Node * Root,RedirectingFileSystem * FS)16560b57cec5SDimitry Andric   bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
16570b57cec5SDimitry Andric     auto *Top = dyn_cast<yaml::MappingNode>(Root);
16580b57cec5SDimitry Andric     if (!Top) {
16590b57cec5SDimitry Andric       error(Root, "expected mapping node");
16600b57cec5SDimitry Andric       return false;
16610b57cec5SDimitry Andric     }
16620b57cec5SDimitry Andric 
16630b57cec5SDimitry Andric     KeyStatusPair Fields[] = {
16640b57cec5SDimitry Andric         KeyStatusPair("version", true),
16650b57cec5SDimitry Andric         KeyStatusPair("case-sensitive", false),
16660b57cec5SDimitry Andric         KeyStatusPair("use-external-names", false),
16670b57cec5SDimitry Andric         KeyStatusPair("overlay-relative", false),
16680b57cec5SDimitry Andric         KeyStatusPair("fallthrough", false),
16690b57cec5SDimitry Andric         KeyStatusPair("roots", true),
16700b57cec5SDimitry Andric     };
16710b57cec5SDimitry Andric 
16720b57cec5SDimitry Andric     DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
16730b57cec5SDimitry Andric     std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries;
16740b57cec5SDimitry Andric 
16750b57cec5SDimitry Andric     // Parse configuration and 'roots'
16760b57cec5SDimitry Andric     for (auto &I : *Top) {
16770b57cec5SDimitry Andric       SmallString<10> KeyBuffer;
16780b57cec5SDimitry Andric       StringRef Key;
16790b57cec5SDimitry Andric       if (!parseScalarString(I.getKey(), Key, KeyBuffer))
16800b57cec5SDimitry Andric         return false;
16810b57cec5SDimitry Andric 
16820b57cec5SDimitry Andric       if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
16830b57cec5SDimitry Andric         return false;
16840b57cec5SDimitry Andric 
16850b57cec5SDimitry Andric       if (Key == "roots") {
16860b57cec5SDimitry Andric         auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue());
16870b57cec5SDimitry Andric         if (!Roots) {
16880b57cec5SDimitry Andric           error(I.getValue(), "expected array");
16890b57cec5SDimitry Andric           return false;
16900b57cec5SDimitry Andric         }
16910b57cec5SDimitry Andric 
16920b57cec5SDimitry Andric         for (auto &I : *Roots) {
16930b57cec5SDimitry Andric           if (std::unique_ptr<RedirectingFileSystem::Entry> E =
16940b57cec5SDimitry Andric                   parseEntry(&I, FS, /*IsRootEntry*/ true))
16950b57cec5SDimitry Andric             RootEntries.push_back(std::move(E));
16960b57cec5SDimitry Andric           else
16970b57cec5SDimitry Andric             return false;
16980b57cec5SDimitry Andric         }
16990b57cec5SDimitry Andric       } else if (Key == "version") {
17000b57cec5SDimitry Andric         StringRef VersionString;
17010b57cec5SDimitry Andric         SmallString<4> Storage;
17020b57cec5SDimitry Andric         if (!parseScalarString(I.getValue(), VersionString, Storage))
17030b57cec5SDimitry Andric           return false;
17040b57cec5SDimitry Andric         int Version;
17050b57cec5SDimitry Andric         if (VersionString.getAsInteger<int>(10, Version)) {
17060b57cec5SDimitry Andric           error(I.getValue(), "expected integer");
17070b57cec5SDimitry Andric           return false;
17080b57cec5SDimitry Andric         }
17090b57cec5SDimitry Andric         if (Version < 0) {
17100b57cec5SDimitry Andric           error(I.getValue(), "invalid version number");
17110b57cec5SDimitry Andric           return false;
17120b57cec5SDimitry Andric         }
17130b57cec5SDimitry Andric         if (Version != 0) {
17140b57cec5SDimitry Andric           error(I.getValue(), "version mismatch, expected 0");
17150b57cec5SDimitry Andric           return false;
17160b57cec5SDimitry Andric         }
17170b57cec5SDimitry Andric       } else if (Key == "case-sensitive") {
17180b57cec5SDimitry Andric         if (!parseScalarBool(I.getValue(), FS->CaseSensitive))
17190b57cec5SDimitry Andric           return false;
17200b57cec5SDimitry Andric       } else if (Key == "overlay-relative") {
17210b57cec5SDimitry Andric         if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay))
17220b57cec5SDimitry Andric           return false;
17230b57cec5SDimitry Andric       } else if (Key == "use-external-names") {
17240b57cec5SDimitry Andric         if (!parseScalarBool(I.getValue(), FS->UseExternalNames))
17250b57cec5SDimitry Andric           return false;
17260b57cec5SDimitry Andric       } else if (Key == "fallthrough") {
17270b57cec5SDimitry Andric         if (!parseScalarBool(I.getValue(), FS->IsFallthrough))
17280b57cec5SDimitry Andric           return false;
17290b57cec5SDimitry Andric       } else {
17300b57cec5SDimitry Andric         llvm_unreachable("key missing from Keys");
17310b57cec5SDimitry Andric       }
17320b57cec5SDimitry Andric     }
17330b57cec5SDimitry Andric 
17340b57cec5SDimitry Andric     if (Stream.failed())
17350b57cec5SDimitry Andric       return false;
17360b57cec5SDimitry Andric 
17370b57cec5SDimitry Andric     if (!checkMissingKeys(Top, Keys))
17380b57cec5SDimitry Andric       return false;
17390b57cec5SDimitry Andric 
17400b57cec5SDimitry Andric     // Now that we sucessefully parsed the YAML file, canonicalize the internal
17410b57cec5SDimitry Andric     // representation to a proper directory tree so that we can search faster
17420b57cec5SDimitry Andric     // inside the VFS.
17430b57cec5SDimitry Andric     for (auto &E : RootEntries)
17440b57cec5SDimitry Andric       uniqueOverlayTree(FS, E.get());
17450b57cec5SDimitry Andric 
17460b57cec5SDimitry Andric     return true;
17470b57cec5SDimitry Andric   }
17480b57cec5SDimitry Andric };
17490b57cec5SDimitry Andric 
1750af732203SDimitry Andric std::unique_ptr<RedirectingFileSystem>
create(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)17510b57cec5SDimitry Andric RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
17520b57cec5SDimitry Andric                               SourceMgr::DiagHandlerTy DiagHandler,
17530b57cec5SDimitry Andric                               StringRef YAMLFilePath, void *DiagContext,
17540b57cec5SDimitry Andric                               IntrusiveRefCntPtr<FileSystem> ExternalFS) {
17550b57cec5SDimitry Andric   SourceMgr SM;
17560b57cec5SDimitry Andric   yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
17570b57cec5SDimitry Andric 
17580b57cec5SDimitry Andric   SM.setDiagHandler(DiagHandler, DiagContext);
17590b57cec5SDimitry Andric   yaml::document_iterator DI = Stream.begin();
17600b57cec5SDimitry Andric   yaml::Node *Root = DI->getRoot();
17610b57cec5SDimitry Andric   if (DI == Stream.end() || !Root) {
17620b57cec5SDimitry Andric     SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
17630b57cec5SDimitry Andric     return nullptr;
17640b57cec5SDimitry Andric   }
17650b57cec5SDimitry Andric 
17660b57cec5SDimitry Andric   RedirectingFileSystemParser P(Stream);
17670b57cec5SDimitry Andric 
17680b57cec5SDimitry Andric   std::unique_ptr<RedirectingFileSystem> FS(
17698bcb0991SDimitry Andric       new RedirectingFileSystem(ExternalFS));
17700b57cec5SDimitry Andric 
17710b57cec5SDimitry Andric   if (!YAMLFilePath.empty()) {
17720b57cec5SDimitry Andric     // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
17730b57cec5SDimitry Andric     // to each 'external-contents' path.
17740b57cec5SDimitry Andric     //
17750b57cec5SDimitry Andric     // Example:
17760b57cec5SDimitry Andric     //    -ivfsoverlay dummy.cache/vfs/vfs.yaml
17770b57cec5SDimitry Andric     // yields:
17780b57cec5SDimitry Andric     //  FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs
17790b57cec5SDimitry Andric     //
17800b57cec5SDimitry Andric     SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
17810b57cec5SDimitry Andric     std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir);
17820b57cec5SDimitry Andric     assert(!EC && "Overlay dir final path must be absolute");
17830b57cec5SDimitry Andric     (void)EC;
17840b57cec5SDimitry Andric     FS->setExternalContentsPrefixDir(OverlayAbsDir);
17850b57cec5SDimitry Andric   }
17860b57cec5SDimitry Andric 
17870b57cec5SDimitry Andric   if (!P.parse(Root, FS.get()))
17880b57cec5SDimitry Andric     return nullptr;
17890b57cec5SDimitry Andric 
1790af732203SDimitry Andric   return FS;
17910b57cec5SDimitry Andric }
17920b57cec5SDimitry Andric 
create(ArrayRef<std::pair<std::string,std::string>> RemappedFiles,bool UseExternalNames,FileSystem & ExternalFS)1793af732203SDimitry Andric std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
1794af732203SDimitry Andric     ArrayRef<std::pair<std::string, std::string>> RemappedFiles,
1795af732203SDimitry Andric     bool UseExternalNames, FileSystem &ExternalFS) {
1796af732203SDimitry Andric   std::unique_ptr<RedirectingFileSystem> FS(
1797af732203SDimitry Andric       new RedirectingFileSystem(&ExternalFS));
1798af732203SDimitry Andric   FS->UseExternalNames = UseExternalNames;
17990b57cec5SDimitry Andric 
1800af732203SDimitry Andric   StringMap<RedirectingFileSystem::Entry *> Entries;
1801af732203SDimitry Andric 
1802af732203SDimitry Andric   for (auto &Mapping : llvm::reverse(RemappedFiles)) {
1803af732203SDimitry Andric     SmallString<128> From = StringRef(Mapping.first);
1804af732203SDimitry Andric     SmallString<128> To = StringRef(Mapping.second);
1805af732203SDimitry Andric     {
1806af732203SDimitry Andric       auto EC = ExternalFS.makeAbsolute(From);
1807af732203SDimitry Andric       (void)EC;
1808af732203SDimitry Andric       assert(!EC && "Could not make absolute path");
1809af732203SDimitry Andric     }
1810af732203SDimitry Andric 
1811af732203SDimitry Andric     // Check if we've already mapped this file. The first one we see (in the
1812af732203SDimitry Andric     // reverse iteration) wins.
1813af732203SDimitry Andric     RedirectingFileSystem::Entry *&ToEntry = Entries[From];
1814af732203SDimitry Andric     if (ToEntry)
1815af732203SDimitry Andric       continue;
1816af732203SDimitry Andric 
1817af732203SDimitry Andric     // Add parent directories.
1818af732203SDimitry Andric     RedirectingFileSystem::Entry *Parent = nullptr;
1819af732203SDimitry Andric     StringRef FromDirectory = llvm::sys::path::parent_path(From);
1820af732203SDimitry Andric     for (auto I = llvm::sys::path::begin(FromDirectory),
1821af732203SDimitry Andric               E = llvm::sys::path::end(FromDirectory);
1822af732203SDimitry Andric          I != E; ++I) {
1823af732203SDimitry Andric       Parent = RedirectingFileSystemParser::lookupOrCreateEntry(FS.get(), *I,
1824af732203SDimitry Andric                                                                 Parent);
1825af732203SDimitry Andric     }
1826af732203SDimitry Andric     assert(Parent && "File without a directory?");
1827af732203SDimitry Andric     {
1828af732203SDimitry Andric       auto EC = ExternalFS.makeAbsolute(To);
1829af732203SDimitry Andric       (void)EC;
1830af732203SDimitry Andric       assert(!EC && "Could not make absolute path");
1831af732203SDimitry Andric     }
1832af732203SDimitry Andric 
1833af732203SDimitry Andric     // Add the file.
1834*5f7ddb14SDimitry Andric     auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
1835af732203SDimitry Andric         llvm::sys::path::filename(From), To,
1836*5f7ddb14SDimitry Andric         UseExternalNames ? RedirectingFileSystem::NK_External
1837*5f7ddb14SDimitry Andric                          : RedirectingFileSystem::NK_Virtual);
1838af732203SDimitry Andric     ToEntry = NewFile.get();
1839*5f7ddb14SDimitry Andric     cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(
1840af732203SDimitry Andric         std::move(NewFile));
1841af732203SDimitry Andric   }
1842af732203SDimitry Andric 
1843af732203SDimitry Andric   return FS;
1844af732203SDimitry Andric }
1845af732203SDimitry Andric 
LookupResult(Entry * E,sys::path::const_iterator Start,sys::path::const_iterator End)1846*5f7ddb14SDimitry Andric RedirectingFileSystem::LookupResult::LookupResult(
1847*5f7ddb14SDimitry Andric     Entry *E, sys::path::const_iterator Start, sys::path::const_iterator End)
1848*5f7ddb14SDimitry Andric     : E(E) {
1849*5f7ddb14SDimitry Andric   assert(E != nullptr);
1850*5f7ddb14SDimitry Andric   // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
1851*5f7ddb14SDimitry Andric   // path of the directory it maps to in the external file system plus any
1852*5f7ddb14SDimitry Andric   // remaining path components in the provided iterator.
1853*5f7ddb14SDimitry Andric   if (auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(E)) {
1854*5f7ddb14SDimitry Andric     SmallString<256> Redirect(DRE->getExternalContentsPath());
1855*5f7ddb14SDimitry Andric     sys::path::append(Redirect, Start, End,
1856*5f7ddb14SDimitry Andric                       getExistingStyle(DRE->getExternalContentsPath()));
1857*5f7ddb14SDimitry Andric     ExternalRedirect = std::string(Redirect);
1858*5f7ddb14SDimitry Andric   }
1859*5f7ddb14SDimitry Andric }
1860*5f7ddb14SDimitry Andric 
shouldFallBackToExternalFS(std::error_code EC,RedirectingFileSystem::Entry * E) const1861*5f7ddb14SDimitry Andric bool RedirectingFileSystem::shouldFallBackToExternalFS(
1862*5f7ddb14SDimitry Andric     std::error_code EC, RedirectingFileSystem::Entry *E) const {
1863*5f7ddb14SDimitry Andric   if (E && !isa<RedirectingFileSystem::DirectoryRemapEntry>(E))
1864*5f7ddb14SDimitry Andric     return false;
1865*5f7ddb14SDimitry Andric   return shouldUseExternalFS() && EC == llvm::errc::no_such_file_or_directory;
1866*5f7ddb14SDimitry Andric }
1867*5f7ddb14SDimitry Andric 
1868af732203SDimitry Andric std::error_code
makeCanonical(SmallVectorImpl<char> & Path) const1869af732203SDimitry Andric RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const {
18700b57cec5SDimitry Andric   if (std::error_code EC = makeAbsolute(Path))
18710b57cec5SDimitry Andric     return EC;
18720b57cec5SDimitry Andric 
1873af732203SDimitry Andric   llvm::SmallString<256> CanonicalPath =
1874af732203SDimitry Andric       canonicalize(StringRef(Path.data(), Path.size()));
1875af732203SDimitry Andric   if (CanonicalPath.empty())
18760b57cec5SDimitry Andric     return make_error_code(llvm::errc::invalid_argument);
18770b57cec5SDimitry Andric 
1878af732203SDimitry Andric   Path.assign(CanonicalPath.begin(), CanonicalPath.end());
1879af732203SDimitry Andric   return {};
1880af732203SDimitry Andric }
1881af732203SDimitry Andric 
1882*5f7ddb14SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult>
lookupPath(StringRef Path) const1883af732203SDimitry Andric RedirectingFileSystem::lookupPath(StringRef Path) const {
18840b57cec5SDimitry Andric   sys::path::const_iterator Start = sys::path::begin(Path);
18850b57cec5SDimitry Andric   sys::path::const_iterator End = sys::path::end(Path);
18860b57cec5SDimitry Andric   for (const auto &Root : Roots) {
1887*5f7ddb14SDimitry Andric     ErrorOr<RedirectingFileSystem::LookupResult> Result =
1888*5f7ddb14SDimitry Andric         lookupPathImpl(Start, End, Root.get());
18890b57cec5SDimitry Andric     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
18900b57cec5SDimitry Andric       return Result;
18910b57cec5SDimitry Andric   }
18920b57cec5SDimitry Andric   return make_error_code(llvm::errc::no_such_file_or_directory);
18930b57cec5SDimitry Andric }
18940b57cec5SDimitry Andric 
1895*5f7ddb14SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult>
lookupPathImpl(sys::path::const_iterator Start,sys::path::const_iterator End,RedirectingFileSystem::Entry * From) const1896*5f7ddb14SDimitry Andric RedirectingFileSystem::lookupPathImpl(
1897*5f7ddb14SDimitry Andric     sys::path::const_iterator Start, sys::path::const_iterator End,
18980b57cec5SDimitry Andric     RedirectingFileSystem::Entry *From) const {
18990b57cec5SDimitry Andric   assert(!isTraversalComponent(*Start) &&
19000b57cec5SDimitry Andric          !isTraversalComponent(From->getName()) &&
19010b57cec5SDimitry Andric          "Paths should not contain traversal components");
19020b57cec5SDimitry Andric 
19030b57cec5SDimitry Andric   StringRef FromName = From->getName();
19040b57cec5SDimitry Andric 
19050b57cec5SDimitry Andric   // Forward the search to the next component in case this is an empty one.
19060b57cec5SDimitry Andric   if (!FromName.empty()) {
1907480093f4SDimitry Andric     if (!pathComponentMatches(*Start, FromName))
19080b57cec5SDimitry Andric       return make_error_code(llvm::errc::no_such_file_or_directory);
19090b57cec5SDimitry Andric 
19100b57cec5SDimitry Andric     ++Start;
19110b57cec5SDimitry Andric 
19120b57cec5SDimitry Andric     if (Start == End) {
19130b57cec5SDimitry Andric       // Match!
1914*5f7ddb14SDimitry Andric       return LookupResult(From, Start, End);
19150b57cec5SDimitry Andric     }
19160b57cec5SDimitry Andric   }
19170b57cec5SDimitry Andric 
1918*5f7ddb14SDimitry Andric   if (isa<RedirectingFileSystem::FileEntry>(From))
19190b57cec5SDimitry Andric     return make_error_code(llvm::errc::not_a_directory);
19200b57cec5SDimitry Andric 
1921*5f7ddb14SDimitry Andric   if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From))
1922*5f7ddb14SDimitry Andric     return LookupResult(From, Start, End);
1923*5f7ddb14SDimitry Andric 
1924*5f7ddb14SDimitry Andric   auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From);
19250b57cec5SDimitry Andric   for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
19260b57cec5SDimitry Andric        llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1927*5f7ddb14SDimitry Andric     ErrorOr<RedirectingFileSystem::LookupResult> Result =
1928*5f7ddb14SDimitry Andric         lookupPathImpl(Start, End, DirEntry.get());
19290b57cec5SDimitry Andric     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
19300b57cec5SDimitry Andric       return Result;
19310b57cec5SDimitry Andric   }
1932480093f4SDimitry Andric 
19330b57cec5SDimitry Andric   return make_error_code(llvm::errc::no_such_file_or_directory);
19340b57cec5SDimitry Andric }
19350b57cec5SDimitry Andric 
getRedirectedFileStatus(const Twine & Path,bool UseExternalNames,Status ExternalStatus)19360b57cec5SDimitry Andric static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
19370b57cec5SDimitry Andric                                       Status ExternalStatus) {
19380b57cec5SDimitry Andric   Status S = ExternalStatus;
19390b57cec5SDimitry Andric   if (!UseExternalNames)
19400b57cec5SDimitry Andric     S = Status::copyWithNewName(S, Path);
19410b57cec5SDimitry Andric   S.IsVFSMapped = true;
19420b57cec5SDimitry Andric   return S;
19430b57cec5SDimitry Andric }
19440b57cec5SDimitry Andric 
status(const Twine & Path,const RedirectingFileSystem::LookupResult & Result)1945*5f7ddb14SDimitry Andric ErrorOr<Status> RedirectingFileSystem::status(
1946*5f7ddb14SDimitry Andric     const Twine &Path, const RedirectingFileSystem::LookupResult &Result) {
1947*5f7ddb14SDimitry Andric   if (Optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
1948*5f7ddb14SDimitry Andric     ErrorOr<Status> S = ExternalFS->status(*ExtRedirect);
1949*5f7ddb14SDimitry Andric     if (!S)
19500b57cec5SDimitry Andric       return S;
1951*5f7ddb14SDimitry Andric     auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E);
1952*5f7ddb14SDimitry Andric     return getRedirectedFileStatus(Path, RE->useExternalName(UseExternalNames),
1953*5f7ddb14SDimitry Andric                                    *S);
19540b57cec5SDimitry Andric   }
1955*5f7ddb14SDimitry Andric 
1956*5f7ddb14SDimitry Andric   auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
1957*5f7ddb14SDimitry Andric   return Status::copyWithNewName(DE->getStatus(), Path);
19580b57cec5SDimitry Andric }
19590b57cec5SDimitry Andric 
status(const Twine & Path_)1960af732203SDimitry Andric ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path_) {
1961af732203SDimitry Andric   SmallString<256> Path;
1962af732203SDimitry Andric   Path_.toVector(Path);
1963af732203SDimitry Andric 
1964af732203SDimitry Andric   if (std::error_code EC = makeCanonical(Path))
1965af732203SDimitry Andric     return EC;
1966af732203SDimitry Andric 
1967*5f7ddb14SDimitry Andric   ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
19680b57cec5SDimitry Andric   if (!Result) {
1969*5f7ddb14SDimitry Andric     if (shouldFallBackToExternalFS(Result.getError()))
19700b57cec5SDimitry Andric       return ExternalFS->status(Path);
19710b57cec5SDimitry Andric     return Result.getError();
19720b57cec5SDimitry Andric   }
1973*5f7ddb14SDimitry Andric 
1974*5f7ddb14SDimitry Andric   ErrorOr<Status> S = status(Path, *Result);
1975*5f7ddb14SDimitry Andric   if (!S && shouldFallBackToExternalFS(S.getError(), Result->E))
1976*5f7ddb14SDimitry Andric     S = ExternalFS->status(Path);
1977*5f7ddb14SDimitry Andric   return S;
19780b57cec5SDimitry Andric }
19790b57cec5SDimitry Andric 
19800b57cec5SDimitry Andric namespace {
19810b57cec5SDimitry Andric 
19820b57cec5SDimitry Andric /// Provide a file wrapper with an overriden status.
19830b57cec5SDimitry Andric class FileWithFixedStatus : public File {
19840b57cec5SDimitry Andric   std::unique_ptr<File> InnerFile;
19850b57cec5SDimitry Andric   Status S;
19860b57cec5SDimitry Andric 
19870b57cec5SDimitry Andric public:
FileWithFixedStatus(std::unique_ptr<File> InnerFile,Status S)19880b57cec5SDimitry Andric   FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
19890b57cec5SDimitry Andric       : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
19900b57cec5SDimitry Andric 
status()19910b57cec5SDimitry Andric   ErrorOr<Status> status() override { return S; }
19920b57cec5SDimitry Andric   ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
19930b57cec5SDimitry Andric 
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)19940b57cec5SDimitry Andric   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
19950b57cec5SDimitry Andric             bool IsVolatile) override {
19960b57cec5SDimitry Andric     return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
19970b57cec5SDimitry Andric                                 IsVolatile);
19980b57cec5SDimitry Andric   }
19990b57cec5SDimitry Andric 
close()20000b57cec5SDimitry Andric   std::error_code close() override { return InnerFile->close(); }
20010b57cec5SDimitry Andric };
20020b57cec5SDimitry Andric 
20030b57cec5SDimitry Andric } // namespace
20040b57cec5SDimitry Andric 
20050b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Path_)2006af732203SDimitry Andric RedirectingFileSystem::openFileForRead(const Twine &Path_) {
2007af732203SDimitry Andric   SmallString<256> Path;
2008af732203SDimitry Andric   Path_.toVector(Path);
2009af732203SDimitry Andric 
2010af732203SDimitry Andric   if (std::error_code EC = makeCanonical(Path))
2011af732203SDimitry Andric     return EC;
2012af732203SDimitry Andric 
2013*5f7ddb14SDimitry Andric   ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
2014*5f7ddb14SDimitry Andric   if (!Result) {
2015*5f7ddb14SDimitry Andric     if (shouldFallBackToExternalFS(Result.getError()))
20160b57cec5SDimitry Andric       return ExternalFS->openFileForRead(Path);
2017*5f7ddb14SDimitry Andric     return Result.getError();
20180b57cec5SDimitry Andric   }
20190b57cec5SDimitry Andric 
2020*5f7ddb14SDimitry Andric   if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
20210b57cec5SDimitry Andric     return make_error_code(llvm::errc::invalid_argument);
20220b57cec5SDimitry Andric 
2023*5f7ddb14SDimitry Andric   StringRef ExtRedirect = *Result->getExternalRedirect();
2024*5f7ddb14SDimitry Andric   auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
20250b57cec5SDimitry Andric 
2026*5f7ddb14SDimitry Andric   auto ExternalFile = ExternalFS->openFileForRead(ExtRedirect);
2027*5f7ddb14SDimitry Andric   if (!ExternalFile) {
2028*5f7ddb14SDimitry Andric     if (shouldFallBackToExternalFS(ExternalFile.getError(), Result->E))
2029*5f7ddb14SDimitry Andric       return ExternalFS->openFileForRead(Path);
2030*5f7ddb14SDimitry Andric     return ExternalFile;
2031*5f7ddb14SDimitry Andric   }
2032*5f7ddb14SDimitry Andric 
2033*5f7ddb14SDimitry Andric   auto ExternalStatus = (*ExternalFile)->status();
20340b57cec5SDimitry Andric   if (!ExternalStatus)
20350b57cec5SDimitry Andric     return ExternalStatus.getError();
20360b57cec5SDimitry Andric 
20370b57cec5SDimitry Andric   // FIXME: Update the status with the name and VFSMapped.
2038*5f7ddb14SDimitry Andric   Status S = getRedirectedFileStatus(
2039*5f7ddb14SDimitry Andric       Path, RE->useExternalName(UseExternalNames), *ExternalStatus);
20400b57cec5SDimitry Andric   return std::unique_ptr<File>(
2041*5f7ddb14SDimitry Andric       std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
20420b57cec5SDimitry Andric }
20430b57cec5SDimitry Andric 
20440b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & Path_,SmallVectorImpl<char> & Output) const2045af732203SDimitry Andric RedirectingFileSystem::getRealPath(const Twine &Path_,
20460b57cec5SDimitry Andric                                    SmallVectorImpl<char> &Output) const {
2047af732203SDimitry Andric   SmallString<256> Path;
2048af732203SDimitry Andric   Path_.toVector(Path);
2049af732203SDimitry Andric 
2050af732203SDimitry Andric   if (std::error_code EC = makeCanonical(Path))
2051af732203SDimitry Andric     return EC;
2052af732203SDimitry Andric 
2053*5f7ddb14SDimitry Andric   ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
20540b57cec5SDimitry Andric   if (!Result) {
2055*5f7ddb14SDimitry Andric     if (shouldFallBackToExternalFS(Result.getError()))
20560b57cec5SDimitry Andric       return ExternalFS->getRealPath(Path, Output);
20570b57cec5SDimitry Andric     return Result.getError();
20580b57cec5SDimitry Andric   }
20590b57cec5SDimitry Andric 
2060*5f7ddb14SDimitry Andric   // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2061*5f7ddb14SDimitry Andric   // path in the external file system.
2062*5f7ddb14SDimitry Andric   if (auto ExtRedirect = Result->getExternalRedirect()) {
2063*5f7ddb14SDimitry Andric     auto P = ExternalFS->getRealPath(*ExtRedirect, Output);
2064*5f7ddb14SDimitry Andric     if (!P && shouldFallBackToExternalFS(P, Result->E)) {
2065*5f7ddb14SDimitry Andric       return ExternalFS->getRealPath(Path, Output);
20660b57cec5SDimitry Andric     }
2067*5f7ddb14SDimitry Andric     return P;
2068*5f7ddb14SDimitry Andric   }
2069*5f7ddb14SDimitry Andric 
2070*5f7ddb14SDimitry Andric   // If we found a DirectoryEntry, still fall back to ExternalFS if allowed,
20710b57cec5SDimitry Andric   // because directories don't have a single external contents path.
20728bcb0991SDimitry Andric   return shouldUseExternalFS() ? ExternalFS->getRealPath(Path, Output)
20730b57cec5SDimitry Andric                                : llvm::errc::invalid_argument;
20740b57cec5SDimitry Andric }
20750b57cec5SDimitry Andric 
2076af732203SDimitry Andric std::unique_ptr<FileSystem>
getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)20770b57cec5SDimitry Andric vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
20780b57cec5SDimitry Andric                     SourceMgr::DiagHandlerTy DiagHandler,
20790b57cec5SDimitry Andric                     StringRef YAMLFilePath, void *DiagContext,
20800b57cec5SDimitry Andric                     IntrusiveRefCntPtr<FileSystem> ExternalFS) {
20810b57cec5SDimitry Andric   return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
20820b57cec5SDimitry Andric                                        YAMLFilePath, DiagContext,
20830b57cec5SDimitry Andric                                        std::move(ExternalFS));
20840b57cec5SDimitry Andric }
20850b57cec5SDimitry Andric 
getVFSEntries(RedirectingFileSystem::Entry * SrcE,SmallVectorImpl<StringRef> & Path,SmallVectorImpl<YAMLVFSEntry> & Entries)20860b57cec5SDimitry Andric static void getVFSEntries(RedirectingFileSystem::Entry *SrcE,
20870b57cec5SDimitry Andric                           SmallVectorImpl<StringRef> &Path,
20880b57cec5SDimitry Andric                           SmallVectorImpl<YAMLVFSEntry> &Entries) {
20890b57cec5SDimitry Andric   auto Kind = SrcE->getKind();
20900b57cec5SDimitry Andric   if (Kind == RedirectingFileSystem::EK_Directory) {
2091*5f7ddb14SDimitry Andric     auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
20920b57cec5SDimitry Andric     assert(DE && "Must be a directory");
20930b57cec5SDimitry Andric     for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
20940b57cec5SDimitry Andric          llvm::make_range(DE->contents_begin(), DE->contents_end())) {
20950b57cec5SDimitry Andric       Path.push_back(SubEntry->getName());
20960b57cec5SDimitry Andric       getVFSEntries(SubEntry.get(), Path, Entries);
20970b57cec5SDimitry Andric       Path.pop_back();
20980b57cec5SDimitry Andric     }
20990b57cec5SDimitry Andric     return;
21000b57cec5SDimitry Andric   }
21010b57cec5SDimitry Andric 
2102*5f7ddb14SDimitry Andric   if (Kind == RedirectingFileSystem::EK_DirectoryRemap) {
2103*5f7ddb14SDimitry Andric     auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
2104*5f7ddb14SDimitry Andric     assert(DR && "Must be a directory remap");
2105*5f7ddb14SDimitry Andric     SmallString<128> VPath;
2106*5f7ddb14SDimitry Andric     for (auto &Comp : Path)
2107*5f7ddb14SDimitry Andric       llvm::sys::path::append(VPath, Comp);
2108*5f7ddb14SDimitry Andric     Entries.push_back(
2109*5f7ddb14SDimitry Andric         YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
2110*5f7ddb14SDimitry Andric     return;
2111*5f7ddb14SDimitry Andric   }
2112*5f7ddb14SDimitry Andric 
21130b57cec5SDimitry Andric   assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
2114*5f7ddb14SDimitry Andric   auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
21150b57cec5SDimitry Andric   assert(FE && "Must be a file");
21160b57cec5SDimitry Andric   SmallString<128> VPath;
21170b57cec5SDimitry Andric   for (auto &Comp : Path)
21180b57cec5SDimitry Andric     llvm::sys::path::append(VPath, Comp);
21190b57cec5SDimitry Andric   Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
21200b57cec5SDimitry Andric }
21210b57cec5SDimitry Andric 
collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,SmallVectorImpl<YAMLVFSEntry> & CollectedEntries,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)21220b57cec5SDimitry Andric void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
21230b57cec5SDimitry Andric                              SourceMgr::DiagHandlerTy DiagHandler,
21240b57cec5SDimitry Andric                              StringRef YAMLFilePath,
21250b57cec5SDimitry Andric                              SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
21260b57cec5SDimitry Andric                              void *DiagContext,
21270b57cec5SDimitry Andric                              IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2128af732203SDimitry Andric   std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create(
21290b57cec5SDimitry Andric       std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
21300b57cec5SDimitry Andric       std::move(ExternalFS));
2131*5f7ddb14SDimitry Andric   if (!VFS)
2132*5f7ddb14SDimitry Andric     return;
2133*5f7ddb14SDimitry Andric   ErrorOr<RedirectingFileSystem::LookupResult> RootResult =
2134*5f7ddb14SDimitry Andric       VFS->lookupPath("/");
2135*5f7ddb14SDimitry Andric   if (!RootResult)
21360b57cec5SDimitry Andric     return;
21370b57cec5SDimitry Andric   SmallVector<StringRef, 8> Components;
21380b57cec5SDimitry Andric   Components.push_back("/");
2139*5f7ddb14SDimitry Andric   getVFSEntries(RootResult->E, Components, CollectedEntries);
21400b57cec5SDimitry Andric }
21410b57cec5SDimitry Andric 
getNextVirtualUniqueID()21420b57cec5SDimitry Andric UniqueID vfs::getNextVirtualUniqueID() {
21430b57cec5SDimitry Andric   static std::atomic<unsigned> UID;
21440b57cec5SDimitry Andric   unsigned ID = ++UID;
21450b57cec5SDimitry Andric   // The following assumes that uint64_t max will never collide with a real
21460b57cec5SDimitry Andric   // dev_t value from the OS.
21470b57cec5SDimitry Andric   return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
21480b57cec5SDimitry Andric }
21490b57cec5SDimitry Andric 
addEntry(StringRef VirtualPath,StringRef RealPath,bool IsDirectory)21505ffd83dbSDimitry Andric void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath,
21515ffd83dbSDimitry Andric                              bool IsDirectory) {
21520b57cec5SDimitry Andric   assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
21530b57cec5SDimitry Andric   assert(sys::path::is_absolute(RealPath) && "real path not absolute");
21540b57cec5SDimitry Andric   assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
21555ffd83dbSDimitry Andric   Mappings.emplace_back(VirtualPath, RealPath, IsDirectory);
21565ffd83dbSDimitry Andric }
21575ffd83dbSDimitry Andric 
addFileMapping(StringRef VirtualPath,StringRef RealPath)21585ffd83dbSDimitry Andric void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
21595ffd83dbSDimitry Andric   addEntry(VirtualPath, RealPath, /*IsDirectory=*/false);
21605ffd83dbSDimitry Andric }
21615ffd83dbSDimitry Andric 
addDirectoryMapping(StringRef VirtualPath,StringRef RealPath)21625ffd83dbSDimitry Andric void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath,
21635ffd83dbSDimitry Andric                                         StringRef RealPath) {
21645ffd83dbSDimitry Andric   addEntry(VirtualPath, RealPath, /*IsDirectory=*/true);
21650b57cec5SDimitry Andric }
21660b57cec5SDimitry Andric 
21670b57cec5SDimitry Andric namespace {
21680b57cec5SDimitry Andric 
21690b57cec5SDimitry Andric class JSONWriter {
21700b57cec5SDimitry Andric   llvm::raw_ostream &OS;
21710b57cec5SDimitry Andric   SmallVector<StringRef, 16> DirStack;
21720b57cec5SDimitry Andric 
getDirIndent()21730b57cec5SDimitry Andric   unsigned getDirIndent() { return 4 * DirStack.size(); }
getFileIndent()21740b57cec5SDimitry Andric   unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
21750b57cec5SDimitry Andric   bool containedIn(StringRef Parent, StringRef Path);
21760b57cec5SDimitry Andric   StringRef containedPart(StringRef Parent, StringRef Path);
21770b57cec5SDimitry Andric   void startDirectory(StringRef Path);
21780b57cec5SDimitry Andric   void endDirectory();
21790b57cec5SDimitry Andric   void writeEntry(StringRef VPath, StringRef RPath);
21800b57cec5SDimitry Andric 
21810b57cec5SDimitry Andric public:
JSONWriter(llvm::raw_ostream & OS)21820b57cec5SDimitry Andric   JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
21830b57cec5SDimitry Andric 
21840b57cec5SDimitry Andric   void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> UseExternalNames,
21850b57cec5SDimitry Andric              Optional<bool> IsCaseSensitive, Optional<bool> IsOverlayRelative,
21860b57cec5SDimitry Andric              StringRef OverlayDir);
21870b57cec5SDimitry Andric };
21880b57cec5SDimitry Andric 
21890b57cec5SDimitry Andric } // namespace
21900b57cec5SDimitry Andric 
containedIn(StringRef Parent,StringRef Path)21910b57cec5SDimitry Andric bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
21920b57cec5SDimitry Andric   using namespace llvm::sys;
21930b57cec5SDimitry Andric 
21940b57cec5SDimitry Andric   // Compare each path component.
21950b57cec5SDimitry Andric   auto IParent = path::begin(Parent), EParent = path::end(Parent);
21960b57cec5SDimitry Andric   for (auto IChild = path::begin(Path), EChild = path::end(Path);
21970b57cec5SDimitry Andric        IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
21980b57cec5SDimitry Andric     if (*IParent != *IChild)
21990b57cec5SDimitry Andric       return false;
22000b57cec5SDimitry Andric   }
22010b57cec5SDimitry Andric   // Have we exhausted the parent path?
22020b57cec5SDimitry Andric   return IParent == EParent;
22030b57cec5SDimitry Andric }
22040b57cec5SDimitry Andric 
containedPart(StringRef Parent,StringRef Path)22050b57cec5SDimitry Andric StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
22060b57cec5SDimitry Andric   assert(!Parent.empty());
22070b57cec5SDimitry Andric   assert(containedIn(Parent, Path));
22080b57cec5SDimitry Andric   return Path.slice(Parent.size() + 1, StringRef::npos);
22090b57cec5SDimitry Andric }
22100b57cec5SDimitry Andric 
startDirectory(StringRef Path)22110b57cec5SDimitry Andric void JSONWriter::startDirectory(StringRef Path) {
22120b57cec5SDimitry Andric   StringRef Name =
22130b57cec5SDimitry Andric       DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
22140b57cec5SDimitry Andric   DirStack.push_back(Path);
22150b57cec5SDimitry Andric   unsigned Indent = getDirIndent();
22160b57cec5SDimitry Andric   OS.indent(Indent) << "{\n";
22170b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'type': 'directory',\n";
22180b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
22190b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'contents': [\n";
22200b57cec5SDimitry Andric }
22210b57cec5SDimitry Andric 
endDirectory()22220b57cec5SDimitry Andric void JSONWriter::endDirectory() {
22230b57cec5SDimitry Andric   unsigned Indent = getDirIndent();
22240b57cec5SDimitry Andric   OS.indent(Indent + 2) << "]\n";
22250b57cec5SDimitry Andric   OS.indent(Indent) << "}";
22260b57cec5SDimitry Andric 
22270b57cec5SDimitry Andric   DirStack.pop_back();
22280b57cec5SDimitry Andric }
22290b57cec5SDimitry Andric 
writeEntry(StringRef VPath,StringRef RPath)22300b57cec5SDimitry Andric void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
22310b57cec5SDimitry Andric   unsigned Indent = getFileIndent();
22320b57cec5SDimitry Andric   OS.indent(Indent) << "{\n";
22330b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'type': 'file',\n";
22340b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
22350b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'external-contents': \""
22360b57cec5SDimitry Andric                         << llvm::yaml::escape(RPath) << "\"\n";
22370b57cec5SDimitry Andric   OS.indent(Indent) << "}";
22380b57cec5SDimitry Andric }
22390b57cec5SDimitry Andric 
write(ArrayRef<YAMLVFSEntry> Entries,Optional<bool> UseExternalNames,Optional<bool> IsCaseSensitive,Optional<bool> IsOverlayRelative,StringRef OverlayDir)22400b57cec5SDimitry Andric void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
22410b57cec5SDimitry Andric                        Optional<bool> UseExternalNames,
22420b57cec5SDimitry Andric                        Optional<bool> IsCaseSensitive,
22430b57cec5SDimitry Andric                        Optional<bool> IsOverlayRelative,
22440b57cec5SDimitry Andric                        StringRef OverlayDir) {
22450b57cec5SDimitry Andric   using namespace llvm::sys;
22460b57cec5SDimitry Andric 
22470b57cec5SDimitry Andric   OS << "{\n"
22480b57cec5SDimitry Andric         "  'version': 0,\n";
22490b57cec5SDimitry Andric   if (IsCaseSensitive.hasValue())
22500b57cec5SDimitry Andric     OS << "  'case-sensitive': '"
22510b57cec5SDimitry Andric        << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
22520b57cec5SDimitry Andric   if (UseExternalNames.hasValue())
22530b57cec5SDimitry Andric     OS << "  'use-external-names': '"
22540b57cec5SDimitry Andric        << (UseExternalNames.getValue() ? "true" : "false") << "',\n";
22550b57cec5SDimitry Andric   bool UseOverlayRelative = false;
22560b57cec5SDimitry Andric   if (IsOverlayRelative.hasValue()) {
22570b57cec5SDimitry Andric     UseOverlayRelative = IsOverlayRelative.getValue();
22580b57cec5SDimitry Andric     OS << "  'overlay-relative': '" << (UseOverlayRelative ? "true" : "false")
22590b57cec5SDimitry Andric        << "',\n";
22600b57cec5SDimitry Andric   }
22610b57cec5SDimitry Andric   OS << "  'roots': [\n";
22620b57cec5SDimitry Andric 
22630b57cec5SDimitry Andric   if (!Entries.empty()) {
22640b57cec5SDimitry Andric     const YAMLVFSEntry &Entry = Entries.front();
22655ffd83dbSDimitry Andric 
22665ffd83dbSDimitry Andric     startDirectory(
22675ffd83dbSDimitry Andric       Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath)
22685ffd83dbSDimitry Andric     );
22690b57cec5SDimitry Andric 
22700b57cec5SDimitry Andric     StringRef RPath = Entry.RPath;
22710b57cec5SDimitry Andric     if (UseOverlayRelative) {
22720b57cec5SDimitry Andric       unsigned OverlayDirLen = OverlayDir.size();
22730b57cec5SDimitry Andric       assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
22740b57cec5SDimitry Andric              "Overlay dir must be contained in RPath");
22750b57cec5SDimitry Andric       RPath = RPath.slice(OverlayDirLen, RPath.size());
22760b57cec5SDimitry Andric     }
22770b57cec5SDimitry Andric 
22785ffd83dbSDimitry Andric     bool IsCurrentDirEmpty = true;
22795ffd83dbSDimitry Andric     if (!Entry.IsDirectory) {
22800b57cec5SDimitry Andric       writeEntry(path::filename(Entry.VPath), RPath);
22815ffd83dbSDimitry Andric       IsCurrentDirEmpty = false;
22825ffd83dbSDimitry Andric     }
22830b57cec5SDimitry Andric 
22840b57cec5SDimitry Andric     for (const auto &Entry : Entries.slice(1)) {
22855ffd83dbSDimitry Andric       StringRef Dir =
22865ffd83dbSDimitry Andric           Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath);
22875ffd83dbSDimitry Andric       if (Dir == DirStack.back()) {
22885ffd83dbSDimitry Andric         if (!IsCurrentDirEmpty) {
22890b57cec5SDimitry Andric           OS << ",\n";
22905ffd83dbSDimitry Andric         }
22915ffd83dbSDimitry Andric       } else {
22925ffd83dbSDimitry Andric         bool IsDirPoppedFromStack = false;
22930b57cec5SDimitry Andric         while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
22940b57cec5SDimitry Andric           OS << "\n";
22950b57cec5SDimitry Andric           endDirectory();
22965ffd83dbSDimitry Andric           IsDirPoppedFromStack = true;
22970b57cec5SDimitry Andric         }
22985ffd83dbSDimitry Andric         if (IsDirPoppedFromStack || !IsCurrentDirEmpty) {
22990b57cec5SDimitry Andric           OS << ",\n";
23005ffd83dbSDimitry Andric         }
23010b57cec5SDimitry Andric         startDirectory(Dir);
23025ffd83dbSDimitry Andric         IsCurrentDirEmpty = true;
23030b57cec5SDimitry Andric       }
23040b57cec5SDimitry Andric       StringRef RPath = Entry.RPath;
23050b57cec5SDimitry Andric       if (UseOverlayRelative) {
23060b57cec5SDimitry Andric         unsigned OverlayDirLen = OverlayDir.size();
23070b57cec5SDimitry Andric         assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
23080b57cec5SDimitry Andric                "Overlay dir must be contained in RPath");
23090b57cec5SDimitry Andric         RPath = RPath.slice(OverlayDirLen, RPath.size());
23100b57cec5SDimitry Andric       }
23115ffd83dbSDimitry Andric       if (!Entry.IsDirectory) {
23120b57cec5SDimitry Andric         writeEntry(path::filename(Entry.VPath), RPath);
23135ffd83dbSDimitry Andric         IsCurrentDirEmpty = false;
23145ffd83dbSDimitry Andric       }
23150b57cec5SDimitry Andric     }
23160b57cec5SDimitry Andric 
23170b57cec5SDimitry Andric     while (!DirStack.empty()) {
23180b57cec5SDimitry Andric       OS << "\n";
23190b57cec5SDimitry Andric       endDirectory();
23200b57cec5SDimitry Andric     }
23210b57cec5SDimitry Andric     OS << "\n";
23220b57cec5SDimitry Andric   }
23230b57cec5SDimitry Andric 
23240b57cec5SDimitry Andric   OS << "  ]\n"
23250b57cec5SDimitry Andric      << "}\n";
23260b57cec5SDimitry Andric }
23270b57cec5SDimitry Andric 
write(llvm::raw_ostream & OS)23280b57cec5SDimitry Andric void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
23290b57cec5SDimitry Andric   llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
23300b57cec5SDimitry Andric     return LHS.VPath < RHS.VPath;
23310b57cec5SDimitry Andric   });
23320b57cec5SDimitry Andric 
23330b57cec5SDimitry Andric   JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
23340b57cec5SDimitry Andric                        IsOverlayRelative, OverlayDir);
23350b57cec5SDimitry Andric }
23360b57cec5SDimitry Andric 
recursive_directory_iterator(FileSystem & FS_,const Twine & Path,std::error_code & EC)23370b57cec5SDimitry Andric vfs::recursive_directory_iterator::recursive_directory_iterator(
23380b57cec5SDimitry Andric     FileSystem &FS_, const Twine &Path, std::error_code &EC)
23390b57cec5SDimitry Andric     : FS(&FS_) {
23400b57cec5SDimitry Andric   directory_iterator I = FS->dir_begin(Path, EC);
23410b57cec5SDimitry Andric   if (I != directory_iterator()) {
23420b57cec5SDimitry Andric     State = std::make_shared<detail::RecDirIterState>();
23430b57cec5SDimitry Andric     State->Stack.push(I);
23440b57cec5SDimitry Andric   }
23450b57cec5SDimitry Andric }
23460b57cec5SDimitry Andric 
23470b57cec5SDimitry Andric vfs::recursive_directory_iterator &
increment(std::error_code & EC)23480b57cec5SDimitry Andric recursive_directory_iterator::increment(std::error_code &EC) {
23490b57cec5SDimitry Andric   assert(FS && State && !State->Stack.empty() && "incrementing past end");
23500b57cec5SDimitry Andric   assert(!State->Stack.top()->path().empty() && "non-canonical end iterator");
23510b57cec5SDimitry Andric   vfs::directory_iterator End;
23520b57cec5SDimitry Andric 
23530b57cec5SDimitry Andric   if (State->HasNoPushRequest)
23540b57cec5SDimitry Andric     State->HasNoPushRequest = false;
23550b57cec5SDimitry Andric   else {
23560b57cec5SDimitry Andric     if (State->Stack.top()->type() == sys::fs::file_type::directory_file) {
23570b57cec5SDimitry Andric       vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC);
23580b57cec5SDimitry Andric       if (I != End) {
23590b57cec5SDimitry Andric         State->Stack.push(I);
23600b57cec5SDimitry Andric         return *this;
23610b57cec5SDimitry Andric       }
23620b57cec5SDimitry Andric     }
23630b57cec5SDimitry Andric   }
23640b57cec5SDimitry Andric 
23650b57cec5SDimitry Andric   while (!State->Stack.empty() && State->Stack.top().increment(EC) == End)
23660b57cec5SDimitry Andric     State->Stack.pop();
23670b57cec5SDimitry Andric 
23680b57cec5SDimitry Andric   if (State->Stack.empty())
23690b57cec5SDimitry Andric     State.reset(); // end iterator
23700b57cec5SDimitry Andric 
23710b57cec5SDimitry Andric   return *this;
23720b57cec5SDimitry Andric }
2373