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/STLExtras.h"
180b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
190b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
200b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
210b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h"
220b57cec5SDimitry Andric #include "llvm/ADT/Twine.h"
230b57cec5SDimitry Andric #include "llvm/ADT/iterator_range.h"
240b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h"
250b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
260b57cec5SDimitry Andric #include "llvm/Support/Chrono.h"
270b57cec5SDimitry Andric #include "llvm/Support/Compiler.h"
280b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
290b57cec5SDimitry Andric #include "llvm/Support/Errc.h"
300b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
310b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h"
320b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
33349cc55cSDimitry Andric #include "llvm/Support/FileSystem/UniqueID.h"
340b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
350b57cec5SDimitry Andric #include "llvm/Support/Path.h"
360b57cec5SDimitry Andric #include "llvm/Support/SMLoc.h"
370b57cec5SDimitry Andric #include "llvm/Support/SourceMgr.h"
380b57cec5SDimitry Andric #include "llvm/Support/YAMLParser.h"
390b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
400b57cec5SDimitry Andric #include <algorithm>
410b57cec5SDimitry Andric #include <atomic>
420b57cec5SDimitry Andric #include <cassert>
430b57cec5SDimitry Andric #include <cstdint>
440b57cec5SDimitry Andric #include <iterator>
450b57cec5SDimitry Andric #include <limits>
46fe013be4SDimitry Andric #include <map>
470b57cec5SDimitry Andric #include <memory>
48bdd1243dSDimitry Andric #include <optional>
490b57cec5SDimitry Andric #include <string>
500b57cec5SDimitry Andric #include <system_error>
510b57cec5SDimitry Andric #include <utility>
520b57cec5SDimitry Andric #include <vector>
530b57cec5SDimitry Andric 
540b57cec5SDimitry Andric using namespace llvm;
550b57cec5SDimitry Andric using namespace llvm::vfs;
560b57cec5SDimitry Andric 
570b57cec5SDimitry Andric using llvm::sys::fs::file_t;
580b57cec5SDimitry Andric using llvm::sys::fs::file_status;
590b57cec5SDimitry Andric using llvm::sys::fs::file_type;
600b57cec5SDimitry Andric using llvm::sys::fs::kInvalidFile;
610b57cec5SDimitry Andric using llvm::sys::fs::perms;
620b57cec5SDimitry Andric using llvm::sys::fs::UniqueID;
630b57cec5SDimitry Andric 
Status(const file_status & Status)640b57cec5SDimitry Andric Status::Status(const file_status &Status)
650b57cec5SDimitry Andric     : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
660b57cec5SDimitry Andric       User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
670b57cec5SDimitry Andric       Type(Status.type()), Perms(Status.permissions()) {}
680b57cec5SDimitry Andric 
Status(const Twine & Name,UniqueID UID,sys::TimePoint<> MTime,uint32_t User,uint32_t Group,uint64_t Size,file_type Type,perms Perms)690b57cec5SDimitry Andric Status::Status(const Twine &Name, UniqueID UID, sys::TimePoint<> MTime,
700b57cec5SDimitry Andric                uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
710b57cec5SDimitry Andric                perms Perms)
720b57cec5SDimitry Andric     : Name(Name.str()), UID(UID), MTime(MTime), User(User), Group(Group),
730b57cec5SDimitry Andric       Size(Size), Type(Type), Perms(Perms) {}
740b57cec5SDimitry Andric 
copyWithNewSize(const Status & In,uint64_t NewSize)750eae32dcSDimitry Andric Status Status::copyWithNewSize(const Status &In, uint64_t NewSize) {
760eae32dcSDimitry Andric   return Status(In.getName(), In.getUniqueID(), In.getLastModificationTime(),
770eae32dcSDimitry Andric                 In.getUser(), In.getGroup(), NewSize, In.getType(),
780eae32dcSDimitry Andric                 In.getPermissions());
790eae32dcSDimitry Andric }
800eae32dcSDimitry Andric 
copyWithNewName(const Status & In,const Twine & NewName)810b57cec5SDimitry Andric Status Status::copyWithNewName(const Status &In, const Twine &NewName) {
820b57cec5SDimitry Andric   return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
830b57cec5SDimitry Andric                 In.getUser(), In.getGroup(), In.getSize(), In.getType(),
840b57cec5SDimitry Andric                 In.getPermissions());
850b57cec5SDimitry Andric }
860b57cec5SDimitry Andric 
copyWithNewName(const file_status & In,const Twine & NewName)870b57cec5SDimitry Andric Status Status::copyWithNewName(const file_status &In, const Twine &NewName) {
880b57cec5SDimitry Andric   return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
890b57cec5SDimitry Andric                 In.getUser(), In.getGroup(), In.getSize(), In.type(),
900b57cec5SDimitry Andric                 In.permissions());
910b57cec5SDimitry Andric }
920b57cec5SDimitry Andric 
equivalent(const Status & Other) const930b57cec5SDimitry Andric bool Status::equivalent(const Status &Other) const {
940b57cec5SDimitry Andric   assert(isStatusKnown() && Other.isStatusKnown());
950b57cec5SDimitry Andric   return getUniqueID() == Other.getUniqueID();
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric 
isDirectory() const980b57cec5SDimitry Andric bool Status::isDirectory() const { return Type == file_type::directory_file; }
990b57cec5SDimitry Andric 
isRegularFile() const1000b57cec5SDimitry Andric bool Status::isRegularFile() const { return Type == file_type::regular_file; }
1010b57cec5SDimitry Andric 
isOther() const1020b57cec5SDimitry Andric bool Status::isOther() const {
1030b57cec5SDimitry Andric   return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
1040b57cec5SDimitry Andric }
1050b57cec5SDimitry Andric 
isSymlink() const1060b57cec5SDimitry Andric bool Status::isSymlink() const { return Type == file_type::symlink_file; }
1070b57cec5SDimitry Andric 
isStatusKnown() const1080b57cec5SDimitry Andric bool Status::isStatusKnown() const { return Type != file_type::status_error; }
1090b57cec5SDimitry Andric 
exists() const1100b57cec5SDimitry Andric bool Status::exists() const {
1110b57cec5SDimitry Andric   return isStatusKnown() && Type != file_type::file_not_found;
1120b57cec5SDimitry Andric }
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric File::~File() = default;
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric FileSystem::~FileSystem() = default;
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>>
getBufferForFile(const llvm::Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)1190b57cec5SDimitry Andric FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
1200b57cec5SDimitry Andric                              bool RequiresNullTerminator, bool IsVolatile) {
1210b57cec5SDimitry Andric   auto F = openFileForRead(Name);
1220b57cec5SDimitry Andric   if (!F)
1230b57cec5SDimitry Andric     return F.getError();
1240b57cec5SDimitry Andric 
1250b57cec5SDimitry Andric   return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
1260b57cec5SDimitry Andric }
1270b57cec5SDimitry Andric 
makeAbsolute(SmallVectorImpl<char> & Path) const1280b57cec5SDimitry Andric std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1290b57cec5SDimitry Andric   if (llvm::sys::path::is_absolute(Path))
1300b57cec5SDimitry Andric     return {};
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   auto WorkingDir = getCurrentWorkingDirectory();
1330b57cec5SDimitry Andric   if (!WorkingDir)
1340b57cec5SDimitry Andric     return WorkingDir.getError();
1350b57cec5SDimitry Andric 
1360b57cec5SDimitry Andric   llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
1370b57cec5SDimitry Andric   return {};
1380b57cec5SDimitry Andric }
1390b57cec5SDimitry Andric 
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const1400b57cec5SDimitry Andric std::error_code FileSystem::getRealPath(const Twine &Path,
1410b57cec5SDimitry Andric                                         SmallVectorImpl<char> &Output) const {
1420b57cec5SDimitry Andric   return errc::operation_not_permitted;
1430b57cec5SDimitry Andric }
1440b57cec5SDimitry Andric 
isLocal(const Twine & Path,bool & Result)1450b57cec5SDimitry Andric std::error_code FileSystem::isLocal(const Twine &Path, bool &Result) {
1460b57cec5SDimitry Andric   return errc::operation_not_permitted;
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric 
exists(const Twine & Path)1490b57cec5SDimitry Andric bool FileSystem::exists(const Twine &Path) {
1500b57cec5SDimitry Andric   auto Status = status(Path);
1510b57cec5SDimitry Andric   return Status && Status->exists();
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric 
15481ad6265SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
dump() const15581ad6265SDimitry Andric void FileSystem::dump() const { print(dbgs(), PrintType::RecursiveContents); }
15681ad6265SDimitry Andric #endif
15781ad6265SDimitry Andric 
1580b57cec5SDimitry Andric #ifndef NDEBUG
isTraversalComponent(StringRef Component)1590b57cec5SDimitry Andric static bool isTraversalComponent(StringRef Component) {
1600b57cec5SDimitry Andric   return Component.equals("..") || Component.equals(".");
1610b57cec5SDimitry Andric }
1620b57cec5SDimitry Andric 
pathHasTraversal(StringRef Path)1630b57cec5SDimitry Andric static bool pathHasTraversal(StringRef Path) {
1640b57cec5SDimitry Andric   using namespace llvm::sys;
1650b57cec5SDimitry Andric 
1660b57cec5SDimitry Andric   for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1670b57cec5SDimitry Andric     if (isTraversalComponent(Comp))
1680b57cec5SDimitry Andric       return true;
1690b57cec5SDimitry Andric   return false;
1700b57cec5SDimitry Andric }
1710b57cec5SDimitry Andric #endif
1720b57cec5SDimitry Andric 
1730b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
1740b57cec5SDimitry Andric // RealFileSystem implementation
1750b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric namespace {
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric /// Wrapper around a raw file descriptor.
1800b57cec5SDimitry Andric class RealFile : public File {
1810b57cec5SDimitry Andric   friend class RealFileSystem;
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric   file_t FD;
1840b57cec5SDimitry Andric   Status S;
1850b57cec5SDimitry Andric   std::string RealName;
1860b57cec5SDimitry Andric 
RealFile(file_t RawFD,StringRef NewName,StringRef NewRealPathName)1878bcb0991SDimitry Andric   RealFile(file_t RawFD, StringRef NewName, StringRef NewRealPathName)
1888bcb0991SDimitry Andric       : FD(RawFD), S(NewName, {}, {}, {}, {}, {},
1890b57cec5SDimitry Andric                      llvm::sys::fs::file_type::status_error, {}),
1900b57cec5SDimitry Andric         RealName(NewRealPathName.str()) {
1910b57cec5SDimitry Andric     assert(FD != kInvalidFile && "Invalid or inactive file descriptor");
1920b57cec5SDimitry Andric   }
1930b57cec5SDimitry Andric 
1940b57cec5SDimitry Andric public:
1950b57cec5SDimitry Andric   ~RealFile() override;
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric   ErrorOr<Status> status() override;
1980b57cec5SDimitry Andric   ErrorOr<std::string> getName() override;
1990b57cec5SDimitry Andric   ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
2000b57cec5SDimitry Andric                                                    int64_t FileSize,
2010b57cec5SDimitry Andric                                                    bool RequiresNullTerminator,
2020b57cec5SDimitry Andric                                                    bool IsVolatile) override;
2030b57cec5SDimitry Andric   std::error_code close() override;
204349cc55cSDimitry Andric   void setPath(const Twine &Path) override;
2050b57cec5SDimitry Andric };
2060b57cec5SDimitry Andric 
2070b57cec5SDimitry Andric } // namespace
2080b57cec5SDimitry Andric 
~RealFile()2090b57cec5SDimitry Andric RealFile::~RealFile() { close(); }
2100b57cec5SDimitry Andric 
status()2110b57cec5SDimitry Andric ErrorOr<Status> RealFile::status() {
2120b57cec5SDimitry Andric   assert(FD != kInvalidFile && "cannot stat closed file");
2130b57cec5SDimitry Andric   if (!S.isStatusKnown()) {
2140b57cec5SDimitry Andric     file_status RealStatus;
2150b57cec5SDimitry Andric     if (std::error_code EC = sys::fs::status(FD, RealStatus))
2160b57cec5SDimitry Andric       return EC;
2170b57cec5SDimitry Andric     S = Status::copyWithNewName(RealStatus, S.getName());
2180b57cec5SDimitry Andric   }
2190b57cec5SDimitry Andric   return S;
2200b57cec5SDimitry Andric }
2210b57cec5SDimitry Andric 
getName()2220b57cec5SDimitry Andric ErrorOr<std::string> RealFile::getName() {
2230b57cec5SDimitry Andric   return RealName.empty() ? S.getName().str() : RealName;
2240b57cec5SDimitry Andric }
2250b57cec5SDimitry Andric 
2260b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>>
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)2270b57cec5SDimitry Andric RealFile::getBuffer(const Twine &Name, int64_t FileSize,
2280b57cec5SDimitry Andric                     bool RequiresNullTerminator, bool IsVolatile) {
2290b57cec5SDimitry Andric   assert(FD != kInvalidFile && "cannot get buffer for closed file");
2300b57cec5SDimitry Andric   return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
2310b57cec5SDimitry Andric                                    IsVolatile);
2320b57cec5SDimitry Andric }
2330b57cec5SDimitry Andric 
close()2340b57cec5SDimitry Andric std::error_code RealFile::close() {
2350b57cec5SDimitry Andric   std::error_code EC = sys::fs::closeFile(FD);
2360b57cec5SDimitry Andric   FD = kInvalidFile;
2370b57cec5SDimitry Andric   return EC;
2380b57cec5SDimitry Andric }
2390b57cec5SDimitry Andric 
setPath(const Twine & Path)240349cc55cSDimitry Andric void RealFile::setPath(const Twine &Path) {
241349cc55cSDimitry Andric   RealName = Path.str();
242349cc55cSDimitry Andric   if (auto Status = status())
243349cc55cSDimitry Andric     S = Status.get().copyWithNewName(Status.get(), Path);
244349cc55cSDimitry Andric }
245349cc55cSDimitry Andric 
2460b57cec5SDimitry Andric namespace {
2470b57cec5SDimitry Andric 
2480b57cec5SDimitry Andric /// A file system according to your operating system.
2490b57cec5SDimitry Andric /// This may be linked to the process's working directory, or maintain its own.
2500b57cec5SDimitry Andric ///
2510b57cec5SDimitry Andric /// Currently, its own working directory is emulated by storing the path and
2520b57cec5SDimitry Andric /// sending absolute paths to llvm::sys::fs:: functions.
2530b57cec5SDimitry Andric /// A more principled approach would be to push this down a level, modelling
2540b57cec5SDimitry Andric /// the working dir as an llvm::sys::fs::WorkingDir or similar.
2550b57cec5SDimitry Andric /// This would enable the use of openat()-style functions on some platforms.
2560b57cec5SDimitry Andric class RealFileSystem : public FileSystem {
2570b57cec5SDimitry Andric public:
RealFileSystem(bool LinkCWDToProcess)2580b57cec5SDimitry Andric   explicit RealFileSystem(bool LinkCWDToProcess) {
2590b57cec5SDimitry Andric     if (!LinkCWDToProcess) {
2600b57cec5SDimitry Andric       SmallString<128> PWD, RealPWD;
261fe013be4SDimitry Andric       if (std::error_code EC = llvm::sys::fs::current_path(PWD))
262fe013be4SDimitry Andric         WD = EC;
263fe013be4SDimitry Andric       else if (llvm::sys::fs::real_path(PWD, RealPWD))
264fe013be4SDimitry Andric         WD = WorkingDirectory{PWD, PWD};
2650b57cec5SDimitry Andric       else
266fe013be4SDimitry Andric         WD = WorkingDirectory{PWD, RealPWD};
2670b57cec5SDimitry Andric     }
2680b57cec5SDimitry Andric   }
2690b57cec5SDimitry Andric 
2700b57cec5SDimitry Andric   ErrorOr<Status> status(const Twine &Path) override;
2710b57cec5SDimitry Andric   ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
2720b57cec5SDimitry Andric   directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
2730b57cec5SDimitry Andric 
2740b57cec5SDimitry Andric   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
2750b57cec5SDimitry Andric   std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
2760b57cec5SDimitry Andric   std::error_code isLocal(const Twine &Path, bool &Result) override;
2770b57cec5SDimitry Andric   std::error_code getRealPath(const Twine &Path,
2780b57cec5SDimitry Andric                               SmallVectorImpl<char> &Output) const override;
2790b57cec5SDimitry Andric 
28081ad6265SDimitry Andric protected:
28181ad6265SDimitry Andric   void printImpl(raw_ostream &OS, PrintType Type,
28281ad6265SDimitry Andric                  unsigned IndentLevel) const override;
28381ad6265SDimitry Andric 
2840b57cec5SDimitry Andric private:
2850b57cec5SDimitry Andric   // If this FS has its own working dir, use it to make Path absolute.
2860b57cec5SDimitry Andric   // The returned twine is safe to use as long as both Storage and Path live.
adjustPath(const Twine & Path,SmallVectorImpl<char> & Storage) const2870b57cec5SDimitry Andric   Twine adjustPath(const Twine &Path, SmallVectorImpl<char> &Storage) const {
288fe013be4SDimitry Andric     if (!WD || !*WD)
2890b57cec5SDimitry Andric       return Path;
2900b57cec5SDimitry Andric     Path.toVector(Storage);
291fe013be4SDimitry Andric     sys::fs::make_absolute(WD->get().Resolved, Storage);
2920b57cec5SDimitry Andric     return Storage;
2930b57cec5SDimitry Andric   }
2940b57cec5SDimitry Andric 
2950b57cec5SDimitry Andric   struct WorkingDirectory {
2960b57cec5SDimitry Andric     // The current working directory, without symlinks resolved. (echo $PWD).
2970b57cec5SDimitry Andric     SmallString<128> Specified;
2980b57cec5SDimitry Andric     // The current working directory, with links resolved. (readlink .).
2990b57cec5SDimitry Andric     SmallString<128> Resolved;
3000b57cec5SDimitry Andric   };
301fe013be4SDimitry Andric   std::optional<llvm::ErrorOr<WorkingDirectory>> WD;
3020b57cec5SDimitry Andric };
3030b57cec5SDimitry Andric 
3040b57cec5SDimitry Andric } // namespace
3050b57cec5SDimitry Andric 
status(const Twine & Path)3060b57cec5SDimitry Andric ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
3070b57cec5SDimitry Andric   SmallString<256> Storage;
3080b57cec5SDimitry Andric   sys::fs::file_status RealStatus;
3090b57cec5SDimitry Andric   if (std::error_code EC =
3100b57cec5SDimitry Andric           sys::fs::status(adjustPath(Path, Storage), RealStatus))
3110b57cec5SDimitry Andric     return EC;
3120b57cec5SDimitry Andric   return Status::copyWithNewName(RealStatus, Path);
3130b57cec5SDimitry Andric }
3140b57cec5SDimitry Andric 
3150b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Name)3160b57cec5SDimitry Andric RealFileSystem::openFileForRead(const Twine &Name) {
3170b57cec5SDimitry Andric   SmallString<256> RealName, Storage;
3180b57cec5SDimitry Andric   Expected<file_t> FDOrErr = sys::fs::openNativeFileForRead(
3190b57cec5SDimitry Andric       adjustPath(Name, Storage), sys::fs::OF_None, &RealName);
3200b57cec5SDimitry Andric   if (!FDOrErr)
3210b57cec5SDimitry Andric     return errorToErrorCode(FDOrErr.takeError());
3220b57cec5SDimitry Andric   return std::unique_ptr<File>(
3230b57cec5SDimitry Andric       new RealFile(*FDOrErr, Name.str(), RealName.str()));
3240b57cec5SDimitry Andric }
3250b57cec5SDimitry Andric 
getCurrentWorkingDirectory() const3260b57cec5SDimitry Andric llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
327fe013be4SDimitry Andric   if (WD && *WD)
328*a58f00eaSDimitry Andric     return std::string(WD->get().Specified);
3290b57cec5SDimitry Andric   if (WD)
330fe013be4SDimitry Andric     return WD->getError();
3310b57cec5SDimitry Andric 
3320b57cec5SDimitry Andric   SmallString<128> Dir;
3330b57cec5SDimitry Andric   if (std::error_code EC = llvm::sys::fs::current_path(Dir))
3340b57cec5SDimitry Andric     return EC;
335*a58f00eaSDimitry Andric   return std::string(Dir);
3360b57cec5SDimitry Andric }
3370b57cec5SDimitry Andric 
setCurrentWorkingDirectory(const Twine & Path)3380b57cec5SDimitry Andric std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
3390b57cec5SDimitry Andric   if (!WD)
3400b57cec5SDimitry Andric     return llvm::sys::fs::set_current_path(Path);
3410b57cec5SDimitry Andric 
3420b57cec5SDimitry Andric   SmallString<128> Absolute, Resolved, Storage;
3430b57cec5SDimitry Andric   adjustPath(Path, Storage).toVector(Absolute);
3440b57cec5SDimitry Andric   bool IsDir;
3450b57cec5SDimitry Andric   if (auto Err = llvm::sys::fs::is_directory(Absolute, IsDir))
3460b57cec5SDimitry Andric     return Err;
3470b57cec5SDimitry Andric   if (!IsDir)
3480b57cec5SDimitry Andric     return std::make_error_code(std::errc::not_a_directory);
3490b57cec5SDimitry Andric   if (auto Err = llvm::sys::fs::real_path(Absolute, Resolved))
3500b57cec5SDimitry Andric     return Err;
351fe013be4SDimitry Andric   WD = WorkingDirectory{Absolute, Resolved};
3520b57cec5SDimitry Andric   return std::error_code();
3530b57cec5SDimitry Andric }
3540b57cec5SDimitry Andric 
isLocal(const Twine & Path,bool & Result)3550b57cec5SDimitry Andric std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) {
3560b57cec5SDimitry Andric   SmallString<256> Storage;
3570b57cec5SDimitry Andric   return llvm::sys::fs::is_local(adjustPath(Path, Storage), Result);
3580b57cec5SDimitry Andric }
3590b57cec5SDimitry Andric 
3600b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const3610b57cec5SDimitry Andric RealFileSystem::getRealPath(const Twine &Path,
3620b57cec5SDimitry Andric                             SmallVectorImpl<char> &Output) const {
3630b57cec5SDimitry Andric   SmallString<256> Storage;
3640b57cec5SDimitry Andric   return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output);
3650b57cec5SDimitry Andric }
3660b57cec5SDimitry Andric 
printImpl(raw_ostream & OS,PrintType Type,unsigned IndentLevel) const36781ad6265SDimitry Andric void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type,
36881ad6265SDimitry Andric                                unsigned IndentLevel) const {
36981ad6265SDimitry Andric   printIndent(OS, IndentLevel);
37081ad6265SDimitry Andric   OS << "RealFileSystem using ";
37181ad6265SDimitry Andric   if (WD)
37281ad6265SDimitry Andric     OS << "own";
37381ad6265SDimitry Andric   else
37481ad6265SDimitry Andric     OS << "process";
37581ad6265SDimitry Andric   OS << " CWD\n";
37681ad6265SDimitry Andric }
37781ad6265SDimitry Andric 
getRealFileSystem()3780b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
3790b57cec5SDimitry Andric   static IntrusiveRefCntPtr<FileSystem> FS(new RealFileSystem(true));
3800b57cec5SDimitry Andric   return FS;
3810b57cec5SDimitry Andric }
3820b57cec5SDimitry Andric 
createPhysicalFileSystem()3830b57cec5SDimitry Andric std::unique_ptr<FileSystem> vfs::createPhysicalFileSystem() {
3848bcb0991SDimitry Andric   return std::make_unique<RealFileSystem>(false);
3850b57cec5SDimitry Andric }
3860b57cec5SDimitry Andric 
3870b57cec5SDimitry Andric namespace {
3880b57cec5SDimitry Andric 
3890b57cec5SDimitry Andric class RealFSDirIter : public llvm::vfs::detail::DirIterImpl {
3900b57cec5SDimitry Andric   llvm::sys::fs::directory_iterator Iter;
3910b57cec5SDimitry Andric 
3920b57cec5SDimitry Andric public:
RealFSDirIter(const Twine & Path,std::error_code & EC)3930b57cec5SDimitry Andric   RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
3940b57cec5SDimitry Andric     if (Iter != llvm::sys::fs::directory_iterator())
3950b57cec5SDimitry Andric       CurrentEntry = directory_entry(Iter->path(), Iter->type());
3960b57cec5SDimitry Andric   }
3970b57cec5SDimitry Andric 
increment()3980b57cec5SDimitry Andric   std::error_code increment() override {
3990b57cec5SDimitry Andric     std::error_code EC;
4000b57cec5SDimitry Andric     Iter.increment(EC);
4010b57cec5SDimitry Andric     CurrentEntry = (Iter == llvm::sys::fs::directory_iterator())
4020b57cec5SDimitry Andric                        ? directory_entry()
4030b57cec5SDimitry Andric                        : directory_entry(Iter->path(), Iter->type());
4040b57cec5SDimitry Andric     return EC;
4050b57cec5SDimitry Andric   }
4060b57cec5SDimitry Andric };
4070b57cec5SDimitry Andric 
4080b57cec5SDimitry Andric } // namespace
4090b57cec5SDimitry Andric 
dir_begin(const Twine & Dir,std::error_code & EC)4100b57cec5SDimitry Andric directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
4110b57cec5SDimitry Andric                                              std::error_code &EC) {
4120b57cec5SDimitry Andric   SmallString<128> Storage;
4130b57cec5SDimitry Andric   return directory_iterator(
4140b57cec5SDimitry Andric       std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC));
4150b57cec5SDimitry Andric }
4160b57cec5SDimitry Andric 
4170b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
4180b57cec5SDimitry Andric // OverlayFileSystem implementation
4190b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
4200b57cec5SDimitry Andric 
OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS)4210b57cec5SDimitry Andric OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
4220b57cec5SDimitry Andric   FSList.push_back(std::move(BaseFS));
4230b57cec5SDimitry Andric }
4240b57cec5SDimitry Andric 
pushOverlay(IntrusiveRefCntPtr<FileSystem> FS)4250b57cec5SDimitry Andric void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
4260b57cec5SDimitry Andric   FSList.push_back(FS);
4270b57cec5SDimitry Andric   // Synchronize added file systems by duplicating the working directory from
4280b57cec5SDimitry Andric   // the first one in the list.
4290b57cec5SDimitry Andric   FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
4300b57cec5SDimitry Andric }
4310b57cec5SDimitry Andric 
status(const Twine & Path)4320b57cec5SDimitry Andric ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
4330b57cec5SDimitry Andric   // FIXME: handle symlinks that cross file systems
4340b57cec5SDimitry Andric   for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
4350b57cec5SDimitry Andric     ErrorOr<Status> Status = (*I)->status(Path);
4360b57cec5SDimitry Andric     if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
4370b57cec5SDimitry Andric       return Status;
4380b57cec5SDimitry Andric   }
4390b57cec5SDimitry Andric   return make_error_code(llvm::errc::no_such_file_or_directory);
4400b57cec5SDimitry Andric }
4410b57cec5SDimitry Andric 
4420b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const llvm::Twine & Path)4430b57cec5SDimitry Andric OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
4440b57cec5SDimitry Andric   // FIXME: handle symlinks that cross file systems
4450b57cec5SDimitry Andric   for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
4460b57cec5SDimitry Andric     auto Result = (*I)->openFileForRead(Path);
4470b57cec5SDimitry Andric     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
4480b57cec5SDimitry Andric       return Result;
4490b57cec5SDimitry Andric   }
4500b57cec5SDimitry Andric   return make_error_code(llvm::errc::no_such_file_or_directory);
4510b57cec5SDimitry Andric }
4520b57cec5SDimitry Andric 
4530b57cec5SDimitry Andric llvm::ErrorOr<std::string>
getCurrentWorkingDirectory() const4540b57cec5SDimitry Andric OverlayFileSystem::getCurrentWorkingDirectory() const {
4550b57cec5SDimitry Andric   // All file systems are synchronized, just take the first working directory.
4560b57cec5SDimitry Andric   return FSList.front()->getCurrentWorkingDirectory();
4570b57cec5SDimitry Andric }
4580b57cec5SDimitry Andric 
4590b57cec5SDimitry Andric std::error_code
setCurrentWorkingDirectory(const Twine & Path)4600b57cec5SDimitry Andric OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
4610b57cec5SDimitry Andric   for (auto &FS : FSList)
4620b57cec5SDimitry Andric     if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
4630b57cec5SDimitry Andric       return EC;
4640b57cec5SDimitry Andric   return {};
4650b57cec5SDimitry Andric }
4660b57cec5SDimitry Andric 
isLocal(const Twine & Path,bool & Result)4670b57cec5SDimitry Andric std::error_code OverlayFileSystem::isLocal(const Twine &Path, bool &Result) {
4680b57cec5SDimitry Andric   for (auto &FS : FSList)
4690b57cec5SDimitry Andric     if (FS->exists(Path))
4700b57cec5SDimitry Andric       return FS->isLocal(Path, Result);
4710b57cec5SDimitry Andric   return errc::no_such_file_or_directory;
4720b57cec5SDimitry Andric }
4730b57cec5SDimitry Andric 
4740b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const4750b57cec5SDimitry Andric OverlayFileSystem::getRealPath(const Twine &Path,
4760b57cec5SDimitry Andric                                SmallVectorImpl<char> &Output) const {
477349cc55cSDimitry Andric   for (const auto &FS : FSList)
4780b57cec5SDimitry Andric     if (FS->exists(Path))
4790b57cec5SDimitry Andric       return FS->getRealPath(Path, Output);
4800b57cec5SDimitry Andric   return errc::no_such_file_or_directory;
4810b57cec5SDimitry Andric }
4820b57cec5SDimitry Andric 
printImpl(raw_ostream & OS,PrintType Type,unsigned IndentLevel) const48381ad6265SDimitry Andric void OverlayFileSystem::printImpl(raw_ostream &OS, PrintType Type,
48481ad6265SDimitry Andric                                   unsigned IndentLevel) const {
48581ad6265SDimitry Andric   printIndent(OS, IndentLevel);
48681ad6265SDimitry Andric   OS << "OverlayFileSystem\n";
48781ad6265SDimitry Andric   if (Type == PrintType::Summary)
48881ad6265SDimitry Andric     return;
48981ad6265SDimitry Andric 
49081ad6265SDimitry Andric   if (Type == PrintType::Contents)
49181ad6265SDimitry Andric     Type = PrintType::Summary;
492c9157d92SDimitry Andric   for (const auto &FS : overlays_range())
49381ad6265SDimitry Andric     FS->print(OS, Type, IndentLevel + 1);
49481ad6265SDimitry Andric }
49581ad6265SDimitry Andric 
4960b57cec5SDimitry Andric llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;
4970b57cec5SDimitry Andric 
4980b57cec5SDimitry Andric namespace {
4990b57cec5SDimitry Andric 
500fe6060f1SDimitry Andric /// Combines and deduplicates directory entries across multiple file systems.
501fe6060f1SDimitry Andric class CombiningDirIterImpl : public llvm::vfs::detail::DirIterImpl {
502fe6060f1SDimitry Andric   using FileSystemPtr = llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>;
503fe6060f1SDimitry Andric 
50481ad6265SDimitry Andric   /// Iterators to combine, processed in reverse order.
50581ad6265SDimitry Andric   SmallVector<directory_iterator, 8> IterList;
50681ad6265SDimitry Andric   /// The iterator currently being traversed.
5070b57cec5SDimitry Andric   directory_iterator CurrentDirIter;
508fe6060f1SDimitry Andric   /// The set of names already returned as entries.
5090b57cec5SDimitry Andric   llvm::StringSet<> SeenNames;
5100b57cec5SDimitry Andric 
51181ad6265SDimitry Andric   /// Sets \c CurrentDirIter to the next iterator in the list, or leaves it as
51281ad6265SDimitry Andric   /// is (at its end position) if we've already gone through them all.
incrementIter(bool IsFirstTime)51381ad6265SDimitry Andric   std::error_code incrementIter(bool IsFirstTime) {
51481ad6265SDimitry Andric     while (!IterList.empty()) {
51581ad6265SDimitry Andric       CurrentDirIter = IterList.back();
51681ad6265SDimitry Andric       IterList.pop_back();
5170b57cec5SDimitry Andric       if (CurrentDirIter != directory_iterator())
5180b57cec5SDimitry Andric         break; // found
5190b57cec5SDimitry Andric     }
52081ad6265SDimitry Andric 
52181ad6265SDimitry Andric     if (IsFirstTime && CurrentDirIter == directory_iterator())
52281ad6265SDimitry Andric       return errc::no_such_file_or_directory;
5230b57cec5SDimitry Andric     return {};
5240b57cec5SDimitry Andric   }
5250b57cec5SDimitry Andric 
incrementDirIter(bool IsFirstTime)5260b57cec5SDimitry Andric   std::error_code incrementDirIter(bool IsFirstTime) {
5270b57cec5SDimitry Andric     assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
5280b57cec5SDimitry Andric            "incrementing past end");
5290b57cec5SDimitry Andric     std::error_code EC;
5300b57cec5SDimitry Andric     if (!IsFirstTime)
5310b57cec5SDimitry Andric       CurrentDirIter.increment(EC);
5320b57cec5SDimitry Andric     if (!EC && CurrentDirIter == directory_iterator())
53381ad6265SDimitry Andric       EC = incrementIter(IsFirstTime);
5340b57cec5SDimitry Andric     return EC;
5350b57cec5SDimitry Andric   }
5360b57cec5SDimitry Andric 
incrementImpl(bool IsFirstTime)5370b57cec5SDimitry Andric   std::error_code incrementImpl(bool IsFirstTime) {
5380b57cec5SDimitry Andric     while (true) {
5390b57cec5SDimitry Andric       std::error_code EC = incrementDirIter(IsFirstTime);
5400b57cec5SDimitry Andric       if (EC || CurrentDirIter == directory_iterator()) {
5410b57cec5SDimitry Andric         CurrentEntry = directory_entry();
5420b57cec5SDimitry Andric         return EC;
5430b57cec5SDimitry Andric       }
5440b57cec5SDimitry Andric       CurrentEntry = *CurrentDirIter;
5450b57cec5SDimitry Andric       StringRef Name = llvm::sys::path::filename(CurrentEntry.path());
5460b57cec5SDimitry Andric       if (SeenNames.insert(Name).second)
5470b57cec5SDimitry Andric         return EC; // name not seen before
5480b57cec5SDimitry Andric     }
5490b57cec5SDimitry Andric     llvm_unreachable("returned above");
5500b57cec5SDimitry Andric   }
5510b57cec5SDimitry Andric 
5520b57cec5SDimitry Andric public:
CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems,std::string Dir,std::error_code & EC)553fe6060f1SDimitry Andric   CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems, std::string Dir,
55481ad6265SDimitry Andric                        std::error_code &EC) {
555c9157d92SDimitry Andric     for (const auto &FS : FileSystems) {
55681ad6265SDimitry Andric       std::error_code FEC;
55781ad6265SDimitry Andric       directory_iterator Iter = FS->dir_begin(Dir, FEC);
55881ad6265SDimitry Andric       if (FEC && FEC != errc::no_such_file_or_directory) {
55981ad6265SDimitry Andric         EC = FEC;
56081ad6265SDimitry Andric         return;
56181ad6265SDimitry Andric       }
56281ad6265SDimitry Andric       if (!FEC)
56381ad6265SDimitry Andric         IterList.push_back(Iter);
56481ad6265SDimitry Andric     }
565fe6060f1SDimitry Andric     EC = incrementImpl(true);
566fe6060f1SDimitry Andric   }
567fe6060f1SDimitry Andric 
CombiningDirIterImpl(ArrayRef<directory_iterator> DirIters,std::error_code & EC)56881ad6265SDimitry Andric   CombiningDirIterImpl(ArrayRef<directory_iterator> DirIters,
56981ad6265SDimitry Andric                        std::error_code &EC)
57081ad6265SDimitry Andric       : IterList(DirIters.begin(), DirIters.end()) {
5710b57cec5SDimitry Andric     EC = incrementImpl(true);
5720b57cec5SDimitry Andric   }
5730b57cec5SDimitry Andric 
increment()5740b57cec5SDimitry Andric   std::error_code increment() override { return incrementImpl(false); }
5750b57cec5SDimitry Andric };
5760b57cec5SDimitry Andric 
5770b57cec5SDimitry Andric } // namespace
5780b57cec5SDimitry Andric 
dir_begin(const Twine & Dir,std::error_code & EC)5790b57cec5SDimitry Andric directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
5800b57cec5SDimitry Andric                                                 std::error_code &EC) {
58181ad6265SDimitry Andric   directory_iterator Combined = directory_iterator(
582fe6060f1SDimitry Andric       std::make_shared<CombiningDirIterImpl>(FSList, Dir.str(), EC));
58381ad6265SDimitry Andric   if (EC)
58481ad6265SDimitry Andric     return {};
58581ad6265SDimitry Andric   return Combined;
5860b57cec5SDimitry Andric }
5870b57cec5SDimitry Andric 
anchor()5880b57cec5SDimitry Andric void ProxyFileSystem::anchor() {}
5890b57cec5SDimitry Andric 
5900b57cec5SDimitry Andric namespace llvm {
5910b57cec5SDimitry Andric namespace vfs {
5920b57cec5SDimitry Andric 
5930b57cec5SDimitry Andric namespace detail {
5940b57cec5SDimitry Andric 
59581ad6265SDimitry Andric enum InMemoryNodeKind {
59681ad6265SDimitry Andric   IME_File,
59781ad6265SDimitry Andric   IME_Directory,
59881ad6265SDimitry Andric   IME_HardLink,
59981ad6265SDimitry Andric   IME_SymbolicLink,
60081ad6265SDimitry Andric };
6010b57cec5SDimitry Andric 
6020b57cec5SDimitry Andric /// The in memory file system is a tree of Nodes. Every node can either be a
60381ad6265SDimitry Andric /// file, symlink, hardlink or a directory.
6040b57cec5SDimitry Andric class InMemoryNode {
6050b57cec5SDimitry Andric   InMemoryNodeKind Kind;
6060b57cec5SDimitry Andric   std::string FileName;
6070b57cec5SDimitry Andric 
6080b57cec5SDimitry Andric public:
InMemoryNode(llvm::StringRef FileName,InMemoryNodeKind Kind)6090b57cec5SDimitry Andric   InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind)
6105ffd83dbSDimitry Andric       : Kind(Kind), FileName(std::string(llvm::sys::path::filename(FileName))) {
6115ffd83dbSDimitry Andric   }
6120b57cec5SDimitry Andric   virtual ~InMemoryNode() = default;
6130b57cec5SDimitry Andric 
61404eeddc0SDimitry Andric   /// Return the \p Status for this node. \p RequestedName should be the name
61504eeddc0SDimitry Andric   /// through which the caller referred to this node. It will override
61604eeddc0SDimitry Andric   /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
61704eeddc0SDimitry Andric   virtual Status getStatus(const Twine &RequestedName) const = 0;
61804eeddc0SDimitry Andric 
6190b57cec5SDimitry Andric   /// Get the filename of this node (the name without the directory part).
getFileName() const6200b57cec5SDimitry Andric   StringRef getFileName() const { return FileName; }
getKind() const6210b57cec5SDimitry Andric   InMemoryNodeKind getKind() const { return Kind; }
6220b57cec5SDimitry Andric   virtual std::string toString(unsigned Indent) const = 0;
6230b57cec5SDimitry Andric };
6240b57cec5SDimitry Andric 
6250b57cec5SDimitry Andric class InMemoryFile : public InMemoryNode {
6260b57cec5SDimitry Andric   Status Stat;
6270b57cec5SDimitry Andric   std::unique_ptr<llvm::MemoryBuffer> Buffer;
6280b57cec5SDimitry Andric 
6290b57cec5SDimitry Andric public:
InMemoryFile(Status Stat,std::unique_ptr<llvm::MemoryBuffer> Buffer)6300b57cec5SDimitry Andric   InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
6310b57cec5SDimitry Andric       : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)),
6320b57cec5SDimitry Andric         Buffer(std::move(Buffer)) {}
6330b57cec5SDimitry Andric 
getStatus(const Twine & RequestedName) const63404eeddc0SDimitry Andric   Status getStatus(const Twine &RequestedName) const override {
6350b57cec5SDimitry Andric     return Status::copyWithNewName(Stat, RequestedName);
6360b57cec5SDimitry Andric   }
getBuffer() const6370b57cec5SDimitry Andric   llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); }
6380b57cec5SDimitry Andric 
toString(unsigned Indent) const6390b57cec5SDimitry Andric   std::string toString(unsigned Indent) const override {
6400b57cec5SDimitry Andric     return (std::string(Indent, ' ') + Stat.getName() + "\n").str();
6410b57cec5SDimitry Andric   }
6420b57cec5SDimitry Andric 
classof(const InMemoryNode * N)6430b57cec5SDimitry Andric   static bool classof(const InMemoryNode *N) {
6440b57cec5SDimitry Andric     return N->getKind() == IME_File;
6450b57cec5SDimitry Andric   }
6460b57cec5SDimitry Andric };
6470b57cec5SDimitry Andric 
6480b57cec5SDimitry Andric namespace {
6490b57cec5SDimitry Andric 
6500b57cec5SDimitry Andric class InMemoryHardLink : public InMemoryNode {
6510b57cec5SDimitry Andric   const InMemoryFile &ResolvedFile;
6520b57cec5SDimitry Andric 
6530b57cec5SDimitry Andric public:
InMemoryHardLink(StringRef Path,const InMemoryFile & ResolvedFile)6540b57cec5SDimitry Andric   InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile)
6550b57cec5SDimitry Andric       : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {}
getResolvedFile() const6560b57cec5SDimitry Andric   const InMemoryFile &getResolvedFile() const { return ResolvedFile; }
6570b57cec5SDimitry Andric 
getStatus(const Twine & RequestedName) const65804eeddc0SDimitry Andric   Status getStatus(const Twine &RequestedName) const override {
65904eeddc0SDimitry Andric     return ResolvedFile.getStatus(RequestedName);
66004eeddc0SDimitry Andric   }
66104eeddc0SDimitry Andric 
toString(unsigned Indent) const6620b57cec5SDimitry Andric   std::string toString(unsigned Indent) const override {
6630b57cec5SDimitry Andric     return std::string(Indent, ' ') + "HardLink to -> " +
6640b57cec5SDimitry Andric            ResolvedFile.toString(0);
6650b57cec5SDimitry Andric   }
6660b57cec5SDimitry Andric 
classof(const InMemoryNode * N)6670b57cec5SDimitry Andric   static bool classof(const InMemoryNode *N) {
6680b57cec5SDimitry Andric     return N->getKind() == IME_HardLink;
6690b57cec5SDimitry Andric   }
6700b57cec5SDimitry Andric };
6710b57cec5SDimitry Andric 
67281ad6265SDimitry Andric class InMemorySymbolicLink : public InMemoryNode {
67381ad6265SDimitry Andric   std::string TargetPath;
67481ad6265SDimitry Andric   Status Stat;
67581ad6265SDimitry Andric 
67681ad6265SDimitry Andric public:
InMemorySymbolicLink(StringRef Path,StringRef TargetPath,Status Stat)67781ad6265SDimitry Andric   InMemorySymbolicLink(StringRef Path, StringRef TargetPath, Status Stat)
67881ad6265SDimitry Andric       : InMemoryNode(Path, IME_SymbolicLink), TargetPath(std::move(TargetPath)),
67981ad6265SDimitry Andric         Stat(Stat) {}
68081ad6265SDimitry Andric 
toString(unsigned Indent) const68181ad6265SDimitry Andric   std::string toString(unsigned Indent) const override {
68281ad6265SDimitry Andric     return std::string(Indent, ' ') + "SymbolicLink to -> " + TargetPath;
68381ad6265SDimitry Andric   }
68481ad6265SDimitry Andric 
getStatus(const Twine & RequestedName) const68581ad6265SDimitry Andric   Status getStatus(const Twine &RequestedName) const override {
68681ad6265SDimitry Andric     return Status::copyWithNewName(Stat, RequestedName);
68781ad6265SDimitry Andric   }
68881ad6265SDimitry Andric 
getTargetPath() const68981ad6265SDimitry Andric   StringRef getTargetPath() const { return TargetPath; }
69081ad6265SDimitry Andric 
classof(const InMemoryNode * N)69181ad6265SDimitry Andric   static bool classof(const InMemoryNode *N) {
69281ad6265SDimitry Andric     return N->getKind() == IME_SymbolicLink;
69381ad6265SDimitry Andric   }
69481ad6265SDimitry Andric };
69581ad6265SDimitry Andric 
6960b57cec5SDimitry Andric /// Adapt a InMemoryFile for VFS' File interface.  The goal is to make
6970b57cec5SDimitry Andric /// \p InMemoryFileAdaptor mimic as much as possible the behavior of
6980b57cec5SDimitry Andric /// \p RealFile.
6990b57cec5SDimitry Andric class InMemoryFileAdaptor : public File {
7000b57cec5SDimitry Andric   const InMemoryFile &Node;
7010b57cec5SDimitry Andric   /// The name to use when returning a Status for this file.
7020b57cec5SDimitry Andric   std::string RequestedName;
7030b57cec5SDimitry Andric 
7040b57cec5SDimitry Andric public:
InMemoryFileAdaptor(const InMemoryFile & Node,std::string RequestedName)7050b57cec5SDimitry Andric   explicit InMemoryFileAdaptor(const InMemoryFile &Node,
7060b57cec5SDimitry Andric                                std::string RequestedName)
7070b57cec5SDimitry Andric       : Node(Node), RequestedName(std::move(RequestedName)) {}
7080b57cec5SDimitry Andric 
status()7090b57cec5SDimitry Andric   llvm::ErrorOr<Status> status() override {
7100b57cec5SDimitry Andric     return Node.getStatus(RequestedName);
7110b57cec5SDimitry Andric   }
7120b57cec5SDimitry Andric 
7130b57cec5SDimitry Andric   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)7140b57cec5SDimitry Andric   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
7150b57cec5SDimitry Andric             bool IsVolatile) override {
7160b57cec5SDimitry Andric     llvm::MemoryBuffer *Buf = Node.getBuffer();
7170b57cec5SDimitry Andric     return llvm::MemoryBuffer::getMemBuffer(
7180b57cec5SDimitry Andric         Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
7190b57cec5SDimitry Andric   }
7200b57cec5SDimitry Andric 
close()7210b57cec5SDimitry Andric   std::error_code close() override { return {}; }
722349cc55cSDimitry Andric 
setPath(const Twine & Path)723349cc55cSDimitry Andric   void setPath(const Twine &Path) override { RequestedName = Path.str(); }
7240b57cec5SDimitry Andric };
7250b57cec5SDimitry Andric } // namespace
7260b57cec5SDimitry Andric 
7270b57cec5SDimitry Andric class InMemoryDirectory : public InMemoryNode {
7280b57cec5SDimitry Andric   Status Stat;
729fe013be4SDimitry Andric   std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
7300b57cec5SDimitry Andric 
7310b57cec5SDimitry Andric public:
InMemoryDirectory(Status Stat)7320b57cec5SDimitry Andric   InMemoryDirectory(Status Stat)
7330b57cec5SDimitry Andric       : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {}
7340b57cec5SDimitry Andric 
7350b57cec5SDimitry Andric   /// Return the \p Status for this node. \p RequestedName should be the name
7360b57cec5SDimitry Andric   /// through which the caller referred to this node. It will override
7370b57cec5SDimitry Andric   /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
getStatus(const Twine & RequestedName) const73804eeddc0SDimitry Andric   Status getStatus(const Twine &RequestedName) const override {
7390b57cec5SDimitry Andric     return Status::copyWithNewName(Stat, RequestedName);
7400b57cec5SDimitry Andric   }
741349cc55cSDimitry Andric 
getUniqueID() const742349cc55cSDimitry Andric   UniqueID getUniqueID() const { return Stat.getUniqueID(); }
743349cc55cSDimitry Andric 
getChild(StringRef Name) const74481ad6265SDimitry Andric   InMemoryNode *getChild(StringRef Name) const {
745fe013be4SDimitry Andric     auto I = Entries.find(Name.str());
7460b57cec5SDimitry Andric     if (I != Entries.end())
7470b57cec5SDimitry Andric       return I->second.get();
7480b57cec5SDimitry Andric     return nullptr;
7490b57cec5SDimitry Andric   }
7500b57cec5SDimitry Andric 
addChild(StringRef Name,std::unique_ptr<InMemoryNode> Child)7510b57cec5SDimitry Andric   InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
752fe013be4SDimitry Andric     return Entries.emplace(Name, std::move(Child)).first->second.get();
7530b57cec5SDimitry Andric   }
7540b57cec5SDimitry Andric 
7550b57cec5SDimitry Andric   using const_iterator = decltype(Entries)::const_iterator;
7560b57cec5SDimitry Andric 
begin() const7570b57cec5SDimitry Andric   const_iterator begin() const { return Entries.begin(); }
end() const7580b57cec5SDimitry Andric   const_iterator end() const { return Entries.end(); }
7590b57cec5SDimitry Andric 
toString(unsigned Indent) const7600b57cec5SDimitry Andric   std::string toString(unsigned Indent) const override {
7610b57cec5SDimitry Andric     std::string Result =
7620b57cec5SDimitry Andric         (std::string(Indent, ' ') + Stat.getName() + "\n").str();
7630b57cec5SDimitry Andric     for (const auto &Entry : Entries)
7640b57cec5SDimitry Andric       Result += Entry.second->toString(Indent + 2);
7650b57cec5SDimitry Andric     return Result;
7660b57cec5SDimitry Andric   }
7670b57cec5SDimitry Andric 
classof(const InMemoryNode * N)7680b57cec5SDimitry Andric   static bool classof(const InMemoryNode *N) {
7690b57cec5SDimitry Andric     return N->getKind() == IME_Directory;
7700b57cec5SDimitry Andric   }
7710b57cec5SDimitry Andric };
7720b57cec5SDimitry Andric 
7730b57cec5SDimitry Andric } // namespace detail
7740b57cec5SDimitry Andric 
775349cc55cSDimitry Andric // The UniqueID of in-memory files is derived from path and content.
776349cc55cSDimitry Andric // This avoids difficulties in creating exactly equivalent in-memory FSes,
777349cc55cSDimitry Andric // as often needed in multithreaded programs.
getUniqueID(hash_code Hash)778349cc55cSDimitry Andric static sys::fs::UniqueID getUniqueID(hash_code Hash) {
779349cc55cSDimitry Andric   return sys::fs::UniqueID(std::numeric_limits<uint64_t>::max(),
780349cc55cSDimitry Andric                            uint64_t(size_t(Hash)));
781349cc55cSDimitry Andric }
getFileID(sys::fs::UniqueID Parent,llvm::StringRef Name,llvm::StringRef Contents)782349cc55cSDimitry Andric static sys::fs::UniqueID getFileID(sys::fs::UniqueID Parent,
783349cc55cSDimitry Andric                                    llvm::StringRef Name,
784349cc55cSDimitry Andric                                    llvm::StringRef Contents) {
785349cc55cSDimitry Andric   return getUniqueID(llvm::hash_combine(Parent.getFile(), Name, Contents));
786349cc55cSDimitry Andric }
getDirectoryID(sys::fs::UniqueID Parent,llvm::StringRef Name)787349cc55cSDimitry Andric static sys::fs::UniqueID getDirectoryID(sys::fs::UniqueID Parent,
788349cc55cSDimitry Andric                                         llvm::StringRef Name) {
789349cc55cSDimitry Andric   return getUniqueID(llvm::hash_combine(Parent.getFile(), Name));
790349cc55cSDimitry Andric }
791349cc55cSDimitry Andric 
makeStatus() const79204eeddc0SDimitry Andric Status detail::NewInMemoryNodeInfo::makeStatus() const {
79304eeddc0SDimitry Andric   UniqueID UID =
79404eeddc0SDimitry Andric       (Type == sys::fs::file_type::directory_file)
79504eeddc0SDimitry Andric           ? getDirectoryID(DirUID, Name)
79604eeddc0SDimitry Andric           : getFileID(DirUID, Name, Buffer ? Buffer->getBuffer() : "");
79704eeddc0SDimitry Andric 
79804eeddc0SDimitry Andric   return Status(Path, UID, llvm::sys::toTimePoint(ModificationTime), User,
79904eeddc0SDimitry Andric                 Group, Buffer ? Buffer->getBufferSize() : 0, Type, Perms);
80004eeddc0SDimitry Andric }
80104eeddc0SDimitry Andric 
InMemoryFileSystem(bool UseNormalizedPaths)8020b57cec5SDimitry Andric InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
8030b57cec5SDimitry Andric     : Root(new detail::InMemoryDirectory(
804349cc55cSDimitry Andric           Status("", getDirectoryID(llvm::sys::fs::UniqueID(), ""),
805349cc55cSDimitry Andric                  llvm::sys::TimePoint<>(), 0, 0, 0,
806349cc55cSDimitry Andric                  llvm::sys::fs::file_type::directory_file,
8070b57cec5SDimitry Andric                  llvm::sys::fs::perms::all_all))),
8080b57cec5SDimitry Andric       UseNormalizedPaths(UseNormalizedPaths) {}
8090b57cec5SDimitry Andric 
8100b57cec5SDimitry Andric InMemoryFileSystem::~InMemoryFileSystem() = default;
8110b57cec5SDimitry Andric 
toString() const8120b57cec5SDimitry Andric std::string InMemoryFileSystem::toString() const {
8130b57cec5SDimitry Andric   return Root->toString(/*Indent=*/0);
8140b57cec5SDimitry Andric }
8150b57cec5SDimitry Andric 
addFile(const Twine & P,time_t ModificationTime,std::unique_ptr<llvm::MemoryBuffer> Buffer,std::optional<uint32_t> User,std::optional<uint32_t> Group,std::optional<llvm::sys::fs::file_type> Type,std::optional<llvm::sys::fs::perms> Perms,MakeNodeFn MakeNode)8160b57cec5SDimitry Andric bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
8170b57cec5SDimitry Andric                                  std::unique_ptr<llvm::MemoryBuffer> Buffer,
818bdd1243dSDimitry Andric                                  std::optional<uint32_t> User,
819bdd1243dSDimitry Andric                                  std::optional<uint32_t> Group,
820bdd1243dSDimitry Andric                                  std::optional<llvm::sys::fs::file_type> Type,
821bdd1243dSDimitry Andric                                  std::optional<llvm::sys::fs::perms> Perms,
82204eeddc0SDimitry Andric                                  MakeNodeFn MakeNode) {
8230b57cec5SDimitry Andric   SmallString<128> Path;
8240b57cec5SDimitry Andric   P.toVector(Path);
8250b57cec5SDimitry Andric 
8260b57cec5SDimitry Andric   // Fix up relative paths. This just prepends the current working directory.
8270b57cec5SDimitry Andric   std::error_code EC = makeAbsolute(Path);
8280b57cec5SDimitry Andric   assert(!EC);
8290b57cec5SDimitry Andric   (void)EC;
8300b57cec5SDimitry Andric 
8310b57cec5SDimitry Andric   if (useNormalizedPaths())
8320b57cec5SDimitry Andric     llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
8330b57cec5SDimitry Andric 
8340b57cec5SDimitry Andric   if (Path.empty())
8350b57cec5SDimitry Andric     return false;
8360b57cec5SDimitry Andric 
8370b57cec5SDimitry Andric   detail::InMemoryDirectory *Dir = Root.get();
8380b57cec5SDimitry Andric   auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path);
83981ad6265SDimitry Andric   const auto ResolvedUser = User.value_or(0);
84081ad6265SDimitry Andric   const auto ResolvedGroup = Group.value_or(0);
84181ad6265SDimitry Andric   const auto ResolvedType = Type.value_or(sys::fs::file_type::regular_file);
84281ad6265SDimitry Andric   const auto ResolvedPerms = Perms.value_or(sys::fs::all_all);
8430b57cec5SDimitry Andric   // Any intermediate directories we create should be accessible by
8440b57cec5SDimitry Andric   // the owner, even if Perms says otherwise for the final path.
8450b57cec5SDimitry Andric   const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all;
8460b57cec5SDimitry Andric   while (true) {
8470b57cec5SDimitry Andric     StringRef Name = *I;
8480b57cec5SDimitry Andric     detail::InMemoryNode *Node = Dir->getChild(Name);
8490b57cec5SDimitry Andric     ++I;
8500b57cec5SDimitry Andric     if (!Node) {
8510b57cec5SDimitry Andric       if (I == E) {
8520b57cec5SDimitry Andric         // End of the path.
85304eeddc0SDimitry Andric         Dir->addChild(
85404eeddc0SDimitry Andric             Name, MakeNode({Dir->getUniqueID(), Path, Name, ModificationTime,
85504eeddc0SDimitry Andric                             std::move(Buffer), ResolvedUser, ResolvedGroup,
85604eeddc0SDimitry Andric                             ResolvedType, ResolvedPerms}));
8570b57cec5SDimitry Andric         return true;
8580b57cec5SDimitry Andric       }
8590b57cec5SDimitry Andric 
8600b57cec5SDimitry Andric       // Create a new directory. Use the path up to here.
8610b57cec5SDimitry Andric       Status Stat(
8620b57cec5SDimitry Andric           StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
863349cc55cSDimitry Andric           getDirectoryID(Dir->getUniqueID(), Name),
864349cc55cSDimitry Andric           llvm::sys::toTimePoint(ModificationTime), ResolvedUser, ResolvedGroup,
865349cc55cSDimitry Andric           0, sys::fs::file_type::directory_file, NewDirectoryPerms);
8660b57cec5SDimitry Andric       Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
8678bcb0991SDimitry Andric           Name, std::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
8680b57cec5SDimitry Andric       continue;
8690b57cec5SDimitry Andric     }
8700b57cec5SDimitry Andric 
8710b57cec5SDimitry Andric     if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) {
8720b57cec5SDimitry Andric       Dir = NewDir;
8730b57cec5SDimitry Andric     } else {
8740b57cec5SDimitry Andric       assert((isa<detail::InMemoryFile>(Node) ||
8750b57cec5SDimitry Andric               isa<detail::InMemoryHardLink>(Node)) &&
8760b57cec5SDimitry Andric              "Must be either file, hardlink or directory!");
8770b57cec5SDimitry Andric 
8780b57cec5SDimitry Andric       // Trying to insert a directory in place of a file.
8790b57cec5SDimitry Andric       if (I != E)
8800b57cec5SDimitry Andric         return false;
8810b57cec5SDimitry Andric 
8820b57cec5SDimitry Andric       // Return false only if the new file is different from the existing one.
8830b57cec5SDimitry Andric       if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) {
8840b57cec5SDimitry Andric         return Link->getResolvedFile().getBuffer()->getBuffer() ==
8850b57cec5SDimitry Andric                Buffer->getBuffer();
8860b57cec5SDimitry Andric       }
8870b57cec5SDimitry Andric       return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
8880b57cec5SDimitry Andric              Buffer->getBuffer();
8890b57cec5SDimitry Andric     }
8900b57cec5SDimitry Andric   }
8910b57cec5SDimitry Andric }
8920b57cec5SDimitry Andric 
addFile(const Twine & P,time_t ModificationTime,std::unique_ptr<llvm::MemoryBuffer> Buffer,std::optional<uint32_t> User,std::optional<uint32_t> Group,std::optional<llvm::sys::fs::file_type> Type,std::optional<llvm::sys::fs::perms> Perms)8930b57cec5SDimitry Andric bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
8940b57cec5SDimitry Andric                                  std::unique_ptr<llvm::MemoryBuffer> Buffer,
895bdd1243dSDimitry Andric                                  std::optional<uint32_t> User,
896bdd1243dSDimitry Andric                                  std::optional<uint32_t> Group,
897bdd1243dSDimitry Andric                                  std::optional<llvm::sys::fs::file_type> Type,
898bdd1243dSDimitry Andric                                  std::optional<llvm::sys::fs::perms> Perms) {
8990b57cec5SDimitry Andric   return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type,
90004eeddc0SDimitry Andric                  Perms,
90104eeddc0SDimitry Andric                  [](detail::NewInMemoryNodeInfo NNI)
90204eeddc0SDimitry Andric                      -> std::unique_ptr<detail::InMemoryNode> {
90304eeddc0SDimitry Andric                    Status Stat = NNI.makeStatus();
90404eeddc0SDimitry Andric                    if (Stat.getType() == sys::fs::file_type::directory_file)
90504eeddc0SDimitry Andric                      return std::make_unique<detail::InMemoryDirectory>(Stat);
90604eeddc0SDimitry Andric                    return std::make_unique<detail::InMemoryFile>(
90704eeddc0SDimitry Andric                        Stat, std::move(NNI.Buffer));
90804eeddc0SDimitry Andric                  });
9090b57cec5SDimitry Andric }
9100b57cec5SDimitry Andric 
addFileNoOwn(const Twine & P,time_t ModificationTime,const llvm::MemoryBufferRef & Buffer,std::optional<uint32_t> User,std::optional<uint32_t> Group,std::optional<llvm::sys::fs::file_type> Type,std::optional<llvm::sys::fs::perms> Perms)911bdd1243dSDimitry Andric bool InMemoryFileSystem::addFileNoOwn(
912bdd1243dSDimitry Andric     const Twine &P, time_t ModificationTime,
913bdd1243dSDimitry Andric     const llvm::MemoryBufferRef &Buffer, std::optional<uint32_t> User,
914bdd1243dSDimitry Andric     std::optional<uint32_t> Group, std::optional<llvm::sys::fs::file_type> Type,
915bdd1243dSDimitry Andric     std::optional<llvm::sys::fs::perms> Perms) {
916e8d8bef9SDimitry Andric   return addFile(P, ModificationTime, llvm::MemoryBuffer::getMemBuffer(Buffer),
9170b57cec5SDimitry Andric                  std::move(User), std::move(Group), std::move(Type),
91804eeddc0SDimitry Andric                  std::move(Perms),
91904eeddc0SDimitry Andric                  [](detail::NewInMemoryNodeInfo NNI)
92004eeddc0SDimitry Andric                      -> std::unique_ptr<detail::InMemoryNode> {
92104eeddc0SDimitry Andric                    Status Stat = NNI.makeStatus();
92204eeddc0SDimitry Andric                    if (Stat.getType() == sys::fs::file_type::directory_file)
92304eeddc0SDimitry Andric                      return std::make_unique<detail::InMemoryDirectory>(Stat);
92404eeddc0SDimitry Andric                    return std::make_unique<detail::InMemoryFile>(
92504eeddc0SDimitry Andric                        Stat, std::move(NNI.Buffer));
92604eeddc0SDimitry Andric                  });
9270b57cec5SDimitry Andric }
9280b57cec5SDimitry Andric 
92981ad6265SDimitry Andric detail::NamedNodeOrError
lookupNode(const Twine & P,bool FollowFinalSymlink,size_t SymlinkDepth) const93081ad6265SDimitry Andric InMemoryFileSystem::lookupNode(const Twine &P, bool FollowFinalSymlink,
93181ad6265SDimitry Andric                                size_t SymlinkDepth) const {
9320b57cec5SDimitry Andric   SmallString<128> Path;
9330b57cec5SDimitry Andric   P.toVector(Path);
9340b57cec5SDimitry Andric 
9350b57cec5SDimitry Andric   // Fix up relative paths. This just prepends the current working directory.
93681ad6265SDimitry Andric   std::error_code EC = makeAbsolute(Path);
9370b57cec5SDimitry Andric   assert(!EC);
9380b57cec5SDimitry Andric   (void)EC;
9390b57cec5SDimitry Andric 
94081ad6265SDimitry Andric   if (useNormalizedPaths())
9410b57cec5SDimitry Andric     llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
9420b57cec5SDimitry Andric 
94381ad6265SDimitry Andric   const detail::InMemoryDirectory *Dir = Root.get();
9440b57cec5SDimitry Andric   if (Path.empty())
94581ad6265SDimitry Andric     return detail::NamedNodeOrError(Path, Dir);
9460b57cec5SDimitry Andric 
9470b57cec5SDimitry Andric   auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
9480b57cec5SDimitry Andric   while (true) {
9490b57cec5SDimitry Andric     detail::InMemoryNode *Node = Dir->getChild(*I);
9500b57cec5SDimitry Andric     ++I;
9510b57cec5SDimitry Andric     if (!Node)
9520b57cec5SDimitry Andric       return errc::no_such_file_or_directory;
9530b57cec5SDimitry Andric 
95481ad6265SDimitry Andric     if (auto Symlink = dyn_cast<detail::InMemorySymbolicLink>(Node)) {
95581ad6265SDimitry Andric       // If we're at the end of the path, and we're not following through
95681ad6265SDimitry Andric       // terminal symlinks, then we're done.
95781ad6265SDimitry Andric       if (I == E && !FollowFinalSymlink)
95881ad6265SDimitry Andric         return detail::NamedNodeOrError(Path, Symlink);
95981ad6265SDimitry Andric 
96081ad6265SDimitry Andric       if (SymlinkDepth > InMemoryFileSystem::MaxSymlinkDepth)
96181ad6265SDimitry Andric         return errc::no_such_file_or_directory;
96281ad6265SDimitry Andric 
96381ad6265SDimitry Andric       SmallString<128> TargetPath = Symlink->getTargetPath();
96481ad6265SDimitry Andric       if (std::error_code EC = makeAbsolute(TargetPath))
96581ad6265SDimitry Andric         return EC;
96681ad6265SDimitry Andric 
96781ad6265SDimitry Andric       // Keep going with the target. We always want to follow symlinks here
96881ad6265SDimitry Andric       // because we're either at the end of a path that we want to follow, or
96981ad6265SDimitry Andric       // not at the end of a path, in which case we need to follow the symlink
97081ad6265SDimitry Andric       // regardless.
97181ad6265SDimitry Andric       auto Target =
97281ad6265SDimitry Andric           lookupNode(TargetPath, /*FollowFinalSymlink=*/true, SymlinkDepth + 1);
97381ad6265SDimitry Andric       if (!Target || I == E)
97481ad6265SDimitry Andric         return Target;
97581ad6265SDimitry Andric 
97681ad6265SDimitry Andric       if (!isa<detail::InMemoryDirectory>(*Target))
97781ad6265SDimitry Andric         return errc::no_such_file_or_directory;
97881ad6265SDimitry Andric 
97981ad6265SDimitry Andric       // Otherwise, continue on the search in the symlinked directory.
98081ad6265SDimitry Andric       Dir = cast<detail::InMemoryDirectory>(*Target);
98181ad6265SDimitry Andric       continue;
98281ad6265SDimitry Andric     }
98381ad6265SDimitry Andric 
9840b57cec5SDimitry Andric     // Return the file if it's at the end of the path.
9850b57cec5SDimitry Andric     if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
9860b57cec5SDimitry Andric       if (I == E)
98781ad6265SDimitry Andric         return detail::NamedNodeOrError(Path, File);
9880b57cec5SDimitry Andric       return errc::no_such_file_or_directory;
9890b57cec5SDimitry Andric     }
9900b57cec5SDimitry Andric 
9910b57cec5SDimitry Andric     // If Node is HardLink then return the resolved file.
9920b57cec5SDimitry Andric     if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) {
9930b57cec5SDimitry Andric       if (I == E)
99481ad6265SDimitry Andric         return detail::NamedNodeOrError(Path, &File->getResolvedFile());
9950b57cec5SDimitry Andric       return errc::no_such_file_or_directory;
9960b57cec5SDimitry Andric     }
9970b57cec5SDimitry Andric     // Traverse directories.
9980b57cec5SDimitry Andric     Dir = cast<detail::InMemoryDirectory>(Node);
9990b57cec5SDimitry Andric     if (I == E)
100081ad6265SDimitry Andric       return detail::NamedNodeOrError(Path, Dir);
10010b57cec5SDimitry Andric   }
10020b57cec5SDimitry Andric }
10030b57cec5SDimitry Andric 
addHardLink(const Twine & NewLink,const Twine & Target)100481ad6265SDimitry Andric bool InMemoryFileSystem::addHardLink(const Twine &NewLink,
100581ad6265SDimitry Andric                                      const Twine &Target) {
100681ad6265SDimitry Andric   auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
100781ad6265SDimitry Andric   // Whether symlinks in the hardlink target are followed is
100881ad6265SDimitry Andric   // implementation-defined in POSIX.
100981ad6265SDimitry Andric   // We're following symlinks here to be consistent with macOS.
101081ad6265SDimitry Andric   auto TargetNode = lookupNode(Target, /*FollowFinalSymlink=*/true);
10110b57cec5SDimitry Andric   // FromPath must not have been added before. ToPath must have been added
10120b57cec5SDimitry Andric   // before. Resolved ToPath must be a File.
101381ad6265SDimitry Andric   if (!TargetNode || NewLinkNode || !isa<detail::InMemoryFile>(*TargetNode))
10140b57cec5SDimitry Andric     return false;
1015bdd1243dSDimitry Andric   return addFile(NewLink, 0, nullptr, std::nullopt, std::nullopt, std::nullopt,
1016bdd1243dSDimitry Andric                  std::nullopt, [&](detail::NewInMemoryNodeInfo NNI) {
101704eeddc0SDimitry Andric                    return std::make_unique<detail::InMemoryHardLink>(
101881ad6265SDimitry Andric                        NNI.Path.str(),
101981ad6265SDimitry Andric                        *cast<detail::InMemoryFile>(*TargetNode));
102081ad6265SDimitry Andric                  });
102181ad6265SDimitry Andric }
102281ad6265SDimitry Andric 
addSymbolicLink(const Twine & NewLink,const Twine & Target,time_t ModificationTime,std::optional<uint32_t> User,std::optional<uint32_t> Group,std::optional<llvm::sys::fs::perms> Perms)1023bdd1243dSDimitry Andric bool InMemoryFileSystem::addSymbolicLink(
1024bdd1243dSDimitry Andric     const Twine &NewLink, const Twine &Target, time_t ModificationTime,
1025bdd1243dSDimitry Andric     std::optional<uint32_t> User, std::optional<uint32_t> Group,
1026bdd1243dSDimitry Andric     std::optional<llvm::sys::fs::perms> Perms) {
102781ad6265SDimitry Andric   auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
102881ad6265SDimitry Andric   if (NewLinkNode)
102981ad6265SDimitry Andric     return false;
103081ad6265SDimitry Andric 
103181ad6265SDimitry Andric   SmallString<128> NewLinkStr, TargetStr;
103281ad6265SDimitry Andric   NewLink.toVector(NewLinkStr);
103381ad6265SDimitry Andric   Target.toVector(TargetStr);
103481ad6265SDimitry Andric 
103581ad6265SDimitry Andric   return addFile(NewLinkStr, ModificationTime, nullptr, User, Group,
103681ad6265SDimitry Andric                  sys::fs::file_type::symlink_file, Perms,
103781ad6265SDimitry Andric                  [&](detail::NewInMemoryNodeInfo NNI) {
103881ad6265SDimitry Andric                    return std::make_unique<detail::InMemorySymbolicLink>(
103981ad6265SDimitry Andric                        NewLinkStr, TargetStr, NNI.makeStatus());
104004eeddc0SDimitry Andric                  });
10410b57cec5SDimitry Andric }
10420b57cec5SDimitry Andric 
status(const Twine & Path)10430b57cec5SDimitry Andric llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
104481ad6265SDimitry Andric   auto Node = lookupNode(Path, /*FollowFinalSymlink=*/true);
10450b57cec5SDimitry Andric   if (Node)
104604eeddc0SDimitry Andric     return (*Node)->getStatus(Path);
10470b57cec5SDimitry Andric   return Node.getError();
10480b57cec5SDimitry Andric }
10490b57cec5SDimitry Andric 
10500b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Path)10510b57cec5SDimitry Andric InMemoryFileSystem::openFileForRead(const Twine &Path) {
105281ad6265SDimitry Andric   auto Node = lookupNode(Path,/*FollowFinalSymlink=*/true);
10530b57cec5SDimitry Andric   if (!Node)
10540b57cec5SDimitry Andric     return Node.getError();
10550b57cec5SDimitry Andric 
10560b57cec5SDimitry Andric   // When we have a file provide a heap-allocated wrapper for the memory buffer
10570b57cec5SDimitry Andric   // to match the ownership semantics for File.
10580b57cec5SDimitry Andric   if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
10590b57cec5SDimitry Andric     return std::unique_ptr<File>(
10600b57cec5SDimitry Andric         new detail::InMemoryFileAdaptor(*F, Path.str()));
10610b57cec5SDimitry Andric 
10620b57cec5SDimitry Andric   // FIXME: errc::not_a_file?
10630b57cec5SDimitry Andric   return make_error_code(llvm::errc::invalid_argument);
10640b57cec5SDimitry Andric }
10650b57cec5SDimitry Andric 
10660b57cec5SDimitry Andric /// Adaptor from InMemoryDir::iterator to directory_iterator.
106781ad6265SDimitry Andric class InMemoryFileSystem::DirIterator : public llvm::vfs::detail::DirIterImpl {
106881ad6265SDimitry Andric   const InMemoryFileSystem *FS;
10690b57cec5SDimitry Andric   detail::InMemoryDirectory::const_iterator I;
10700b57cec5SDimitry Andric   detail::InMemoryDirectory::const_iterator E;
10710b57cec5SDimitry Andric   std::string RequestedDirName;
10720b57cec5SDimitry Andric 
setCurrentEntry()10730b57cec5SDimitry Andric   void setCurrentEntry() {
10740b57cec5SDimitry Andric     if (I != E) {
10750b57cec5SDimitry Andric       SmallString<256> Path(RequestedDirName);
10760b57cec5SDimitry Andric       llvm::sys::path::append(Path, I->second->getFileName());
1077480093f4SDimitry Andric       sys::fs::file_type Type = sys::fs::file_type::type_unknown;
10780b57cec5SDimitry Andric       switch (I->second->getKind()) {
10790b57cec5SDimitry Andric       case detail::IME_File:
10800b57cec5SDimitry Andric       case detail::IME_HardLink:
10810b57cec5SDimitry Andric         Type = sys::fs::file_type::regular_file;
10820b57cec5SDimitry Andric         break;
10830b57cec5SDimitry Andric       case detail::IME_Directory:
10840b57cec5SDimitry Andric         Type = sys::fs::file_type::directory_file;
10850b57cec5SDimitry Andric         break;
108681ad6265SDimitry Andric       case detail::IME_SymbolicLink:
108781ad6265SDimitry Andric         if (auto SymlinkTarget =
108881ad6265SDimitry Andric                 FS->lookupNode(Path, /*FollowFinalSymlink=*/true)) {
108981ad6265SDimitry Andric           Path = SymlinkTarget.getName();
109081ad6265SDimitry Andric           Type = (*SymlinkTarget)->getStatus(Path).getType();
109181ad6265SDimitry Andric         }
109281ad6265SDimitry Andric         break;
10930b57cec5SDimitry Andric       }
1094*a58f00eaSDimitry Andric       CurrentEntry = directory_entry(std::string(Path), Type);
10950b57cec5SDimitry Andric     } else {
10960b57cec5SDimitry Andric       // When we're at the end, make CurrentEntry invalid and DirIterImpl will
10970b57cec5SDimitry Andric       // do the rest.
10980b57cec5SDimitry Andric       CurrentEntry = directory_entry();
10990b57cec5SDimitry Andric     }
11000b57cec5SDimitry Andric   }
11010b57cec5SDimitry Andric 
11020b57cec5SDimitry Andric public:
110381ad6265SDimitry Andric   DirIterator() = default;
11040b57cec5SDimitry Andric 
DirIterator(const InMemoryFileSystem * FS,const detail::InMemoryDirectory & Dir,std::string RequestedDirName)110581ad6265SDimitry Andric   DirIterator(const InMemoryFileSystem *FS,
110681ad6265SDimitry Andric               const detail::InMemoryDirectory &Dir,
11070b57cec5SDimitry Andric               std::string RequestedDirName)
110881ad6265SDimitry Andric       : FS(FS), I(Dir.begin()), E(Dir.end()),
11090b57cec5SDimitry Andric         RequestedDirName(std::move(RequestedDirName)) {
11100b57cec5SDimitry Andric     setCurrentEntry();
11110b57cec5SDimitry Andric   }
11120b57cec5SDimitry Andric 
increment()11130b57cec5SDimitry Andric   std::error_code increment() override {
11140b57cec5SDimitry Andric     ++I;
11150b57cec5SDimitry Andric     setCurrentEntry();
11160b57cec5SDimitry Andric     return {};
11170b57cec5SDimitry Andric   }
11180b57cec5SDimitry Andric };
11190b57cec5SDimitry Andric 
dir_begin(const Twine & Dir,std::error_code & EC)11200b57cec5SDimitry Andric directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
11210b57cec5SDimitry Andric                                                  std::error_code &EC) {
112281ad6265SDimitry Andric   auto Node = lookupNode(Dir, /*FollowFinalSymlink=*/true);
11230b57cec5SDimitry Andric   if (!Node) {
11240b57cec5SDimitry Andric     EC = Node.getError();
112581ad6265SDimitry Andric     return directory_iterator(std::make_shared<DirIterator>());
11260b57cec5SDimitry Andric   }
11270b57cec5SDimitry Andric 
11280b57cec5SDimitry Andric   if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
11290b57cec5SDimitry Andric     return directory_iterator(
113081ad6265SDimitry Andric         std::make_shared<DirIterator>(this, *DirNode, Dir.str()));
11310b57cec5SDimitry Andric 
11320b57cec5SDimitry Andric   EC = make_error_code(llvm::errc::not_a_directory);
113381ad6265SDimitry Andric   return directory_iterator(std::make_shared<DirIterator>());
11340b57cec5SDimitry Andric }
11350b57cec5SDimitry Andric 
setCurrentWorkingDirectory(const Twine & P)11360b57cec5SDimitry Andric std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) {
11370b57cec5SDimitry Andric   SmallString<128> Path;
11380b57cec5SDimitry Andric   P.toVector(Path);
11390b57cec5SDimitry Andric 
11400b57cec5SDimitry Andric   // Fix up relative paths. This just prepends the current working directory.
11410b57cec5SDimitry Andric   std::error_code EC = makeAbsolute(Path);
11420b57cec5SDimitry Andric   assert(!EC);
11430b57cec5SDimitry Andric   (void)EC;
11440b57cec5SDimitry Andric 
11450b57cec5SDimitry Andric   if (useNormalizedPaths())
11460b57cec5SDimitry Andric     llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
11470b57cec5SDimitry Andric 
11480b57cec5SDimitry Andric   if (!Path.empty())
1149*a58f00eaSDimitry Andric     WorkingDirectory = std::string(Path);
11500b57cec5SDimitry Andric   return {};
11510b57cec5SDimitry Andric }
11520b57cec5SDimitry Andric 
11530b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const11540b57cec5SDimitry Andric InMemoryFileSystem::getRealPath(const Twine &Path,
11550b57cec5SDimitry Andric                                 SmallVectorImpl<char> &Output) const {
11560b57cec5SDimitry Andric   auto CWD = getCurrentWorkingDirectory();
11570b57cec5SDimitry Andric   if (!CWD || CWD->empty())
11580b57cec5SDimitry Andric     return errc::operation_not_permitted;
11590b57cec5SDimitry Andric   Path.toVector(Output);
11600b57cec5SDimitry Andric   if (auto EC = makeAbsolute(Output))
11610b57cec5SDimitry Andric     return EC;
11620b57cec5SDimitry Andric   llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true);
11630b57cec5SDimitry Andric   return {};
11640b57cec5SDimitry Andric }
11650b57cec5SDimitry Andric 
isLocal(const Twine & Path,bool & Result)11660b57cec5SDimitry Andric std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
11670b57cec5SDimitry Andric   Result = false;
11680b57cec5SDimitry Andric   return {};
11690b57cec5SDimitry Andric }
11700b57cec5SDimitry Andric 
printImpl(raw_ostream & OS,PrintType PrintContents,unsigned IndentLevel) const117181ad6265SDimitry Andric void InMemoryFileSystem::printImpl(raw_ostream &OS, PrintType PrintContents,
117281ad6265SDimitry Andric                                    unsigned IndentLevel) const {
117381ad6265SDimitry Andric   printIndent(OS, IndentLevel);
117481ad6265SDimitry Andric   OS << "InMemoryFileSystem\n";
117581ad6265SDimitry Andric }
117681ad6265SDimitry Andric 
11770b57cec5SDimitry Andric } // namespace vfs
11780b57cec5SDimitry Andric } // namespace llvm
11790b57cec5SDimitry Andric 
11800b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
11810b57cec5SDimitry Andric // RedirectingFileSystem implementation
11820b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
11830b57cec5SDimitry Andric 
11845ffd83dbSDimitry Andric namespace {
11855ffd83dbSDimitry Andric 
getExistingStyle(llvm::StringRef Path)1186fe6060f1SDimitry Andric static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) {
1187fe6060f1SDimitry Andric   // Detect the path style in use by checking the first separator.
11885ffd83dbSDimitry Andric   llvm::sys::path::Style style = llvm::sys::path::Style::native;
11895ffd83dbSDimitry Andric   const size_t n = Path.find_first_of("/\\");
1190349cc55cSDimitry Andric   // Can't distinguish between posix and windows_slash here.
11915ffd83dbSDimitry Andric   if (n != static_cast<size_t>(-1))
11925ffd83dbSDimitry Andric     style = (Path[n] == '/') ? llvm::sys::path::Style::posix
1193349cc55cSDimitry Andric                              : llvm::sys::path::Style::windows_backslash;
1194fe6060f1SDimitry Andric   return style;
1195fe6060f1SDimitry Andric }
1196fe6060f1SDimitry Andric 
1197fe6060f1SDimitry Andric /// Removes leading "./" as well as path components like ".." and ".".
canonicalize(llvm::StringRef Path)1198fe6060f1SDimitry Andric static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {
1199fe6060f1SDimitry Andric   // First detect the path style in use by checking the first separator.
1200fe6060f1SDimitry Andric   llvm::sys::path::Style style = getExistingStyle(Path);
12015ffd83dbSDimitry Andric 
12025ffd83dbSDimitry Andric   // Now remove the dots.  Explicitly specifying the path style prevents the
12035ffd83dbSDimitry Andric   // direction of the slashes from changing.
12045ffd83dbSDimitry Andric   llvm::SmallString<256> result =
12055ffd83dbSDimitry Andric       llvm::sys::path::remove_leading_dotslash(Path, style);
12065ffd83dbSDimitry Andric   llvm::sys::path::remove_dots(result, /*remove_dot_dot=*/true, style);
12075ffd83dbSDimitry Andric   return result;
12085ffd83dbSDimitry Andric }
12095ffd83dbSDimitry Andric 
121081ad6265SDimitry Andric /// Whether the error and entry specify a file/directory that was not found.
isFileNotFound(std::error_code EC,RedirectingFileSystem::Entry * E=nullptr)121181ad6265SDimitry Andric static bool isFileNotFound(std::error_code EC,
121281ad6265SDimitry Andric                            RedirectingFileSystem::Entry *E = nullptr) {
121381ad6265SDimitry Andric   if (E && !isa<RedirectingFileSystem::DirectoryRemapEntry>(E))
121481ad6265SDimitry Andric     return false;
121581ad6265SDimitry Andric   return EC == llvm::errc::no_such_file_or_directory;
121681ad6265SDimitry Andric }
121781ad6265SDimitry Andric 
12185ffd83dbSDimitry Andric } // anonymous namespace
12195ffd83dbSDimitry Andric 
12205ffd83dbSDimitry Andric 
RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)12218bcb0991SDimitry Andric RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
12228bcb0991SDimitry Andric     : ExternalFS(std::move(FS)) {
12238bcb0991SDimitry Andric   if (ExternalFS)
12248bcb0991SDimitry Andric     if (auto ExternalWorkingDirectory =
12258bcb0991SDimitry Andric             ExternalFS->getCurrentWorkingDirectory()) {
12268bcb0991SDimitry Andric       WorkingDirectory = *ExternalWorkingDirectory;
12278bcb0991SDimitry Andric     }
12288bcb0991SDimitry Andric }
12298bcb0991SDimitry Andric 
1230fe6060f1SDimitry Andric /// Directory iterator implementation for \c RedirectingFileSystem's
1231fe6060f1SDimitry Andric /// directory entries.
1232fe6060f1SDimitry Andric class llvm::vfs::RedirectingFSDirIterImpl
12330b57cec5SDimitry Andric     : public llvm::vfs::detail::DirIterImpl {
12340b57cec5SDimitry Andric   std::string Dir;
1235fe6060f1SDimitry Andric   RedirectingFileSystem::DirectoryEntry::iterator Current, End;
12360b57cec5SDimitry Andric 
incrementImpl(bool IsFirstTime)1237fe6060f1SDimitry Andric   std::error_code incrementImpl(bool IsFirstTime) {
1238fe6060f1SDimitry Andric     assert((IsFirstTime || Current != End) && "cannot iterate past end");
1239fe6060f1SDimitry Andric     if (!IsFirstTime)
1240fe6060f1SDimitry Andric       ++Current;
1241fe6060f1SDimitry Andric     if (Current != End) {
1242fe6060f1SDimitry Andric       SmallString<128> PathStr(Dir);
1243fe6060f1SDimitry Andric       llvm::sys::path::append(PathStr, (*Current)->getName());
1244fe6060f1SDimitry Andric       sys::fs::file_type Type = sys::fs::file_type::type_unknown;
1245fe6060f1SDimitry Andric       switch ((*Current)->getKind()) {
1246fe6060f1SDimitry Andric       case RedirectingFileSystem::EK_Directory:
1247bdd1243dSDimitry Andric         [[fallthrough]];
1248fe6060f1SDimitry Andric       case RedirectingFileSystem::EK_DirectoryRemap:
1249fe6060f1SDimitry Andric         Type = sys::fs::file_type::directory_file;
1250fe6060f1SDimitry Andric         break;
1251fe6060f1SDimitry Andric       case RedirectingFileSystem::EK_File:
1252fe6060f1SDimitry Andric         Type = sys::fs::file_type::regular_file;
1253fe6060f1SDimitry Andric         break;
1254fe6060f1SDimitry Andric       }
1255*a58f00eaSDimitry Andric       CurrentEntry = directory_entry(std::string(PathStr), Type);
1256fe6060f1SDimitry Andric     } else {
1257fe6060f1SDimitry Andric       CurrentEntry = directory_entry();
1258fe6060f1SDimitry Andric     }
1259fe6060f1SDimitry Andric     return {};
1260fe6060f1SDimitry Andric   };
12610b57cec5SDimitry Andric 
12620b57cec5SDimitry Andric public:
RedirectingFSDirIterImpl(const Twine & Path,RedirectingFileSystem::DirectoryEntry::iterator Begin,RedirectingFileSystem::DirectoryEntry::iterator End,std::error_code & EC)1263fe6060f1SDimitry Andric   RedirectingFSDirIterImpl(
1264fe6060f1SDimitry Andric       const Twine &Path, RedirectingFileSystem::DirectoryEntry::iterator Begin,
1265fe6060f1SDimitry Andric       RedirectingFileSystem::DirectoryEntry::iterator End, std::error_code &EC)
1266fe6060f1SDimitry Andric       : Dir(Path.str()), Current(Begin), End(End) {
1267fe6060f1SDimitry Andric     EC = incrementImpl(/*IsFirstTime=*/true);
1268fe6060f1SDimitry Andric   }
12690b57cec5SDimitry Andric 
increment()1270fe6060f1SDimitry Andric   std::error_code increment() override {
1271fe6060f1SDimitry Andric     return incrementImpl(/*IsFirstTime=*/false);
1272fe6060f1SDimitry Andric   }
1273fe6060f1SDimitry Andric };
1274fe6060f1SDimitry Andric 
1275349cc55cSDimitry Andric namespace {
1276fe6060f1SDimitry Andric /// Directory iterator implementation for \c RedirectingFileSystem's
1277fe6060f1SDimitry Andric /// directory remap entries that maps the paths reported by the external
1278fe6060f1SDimitry Andric /// file system's directory iterator back to the virtual directory's path.
1279fe6060f1SDimitry Andric class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl {
1280fe6060f1SDimitry Andric   std::string Dir;
1281fe6060f1SDimitry Andric   llvm::sys::path::Style DirStyle;
1282fe6060f1SDimitry Andric   llvm::vfs::directory_iterator ExternalIter;
1283fe6060f1SDimitry Andric 
1284fe6060f1SDimitry Andric public:
RedirectingFSDirRemapIterImpl(std::string DirPath,llvm::vfs::directory_iterator ExtIter)1285fe6060f1SDimitry Andric   RedirectingFSDirRemapIterImpl(std::string DirPath,
1286fe6060f1SDimitry Andric                                 llvm::vfs::directory_iterator ExtIter)
1287fe6060f1SDimitry Andric       : Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)),
1288fe6060f1SDimitry Andric         ExternalIter(ExtIter) {
1289fe6060f1SDimitry Andric     if (ExternalIter != llvm::vfs::directory_iterator())
1290fe6060f1SDimitry Andric       setCurrentEntry();
1291fe6060f1SDimitry Andric   }
1292fe6060f1SDimitry Andric 
setCurrentEntry()1293fe6060f1SDimitry Andric   void setCurrentEntry() {
1294fe6060f1SDimitry Andric     StringRef ExternalPath = ExternalIter->path();
1295fe6060f1SDimitry Andric     llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath);
1296fe6060f1SDimitry Andric     StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle);
1297fe6060f1SDimitry Andric 
1298fe6060f1SDimitry Andric     SmallString<128> NewPath(Dir);
1299fe6060f1SDimitry Andric     llvm::sys::path::append(NewPath, DirStyle, File);
1300fe6060f1SDimitry Andric 
1301fe6060f1SDimitry Andric     CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type());
1302fe6060f1SDimitry Andric   }
1303fe6060f1SDimitry Andric 
increment()1304fe6060f1SDimitry Andric   std::error_code increment() override {
1305fe6060f1SDimitry Andric     std::error_code EC;
1306fe6060f1SDimitry Andric     ExternalIter.increment(EC);
1307fe6060f1SDimitry Andric     if (!EC && ExternalIter != llvm::vfs::directory_iterator())
1308fe6060f1SDimitry Andric       setCurrentEntry();
1309fe6060f1SDimitry Andric     else
1310fe6060f1SDimitry Andric       CurrentEntry = directory_entry();
1311fe6060f1SDimitry Andric     return EC;
1312fe6060f1SDimitry Andric   }
13130b57cec5SDimitry Andric };
1314349cc55cSDimitry Andric } // namespace
13150b57cec5SDimitry Andric 
13160b57cec5SDimitry Andric llvm::ErrorOr<std::string>
getCurrentWorkingDirectory() const13170b57cec5SDimitry Andric RedirectingFileSystem::getCurrentWorkingDirectory() const {
13188bcb0991SDimitry Andric   return WorkingDirectory;
13190b57cec5SDimitry Andric }
13200b57cec5SDimitry Andric 
13210b57cec5SDimitry Andric std::error_code
setCurrentWorkingDirectory(const Twine & Path)13220b57cec5SDimitry Andric RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
13238bcb0991SDimitry Andric   // Don't change the working directory if the path doesn't exist.
13248bcb0991SDimitry Andric   if (!exists(Path))
13258bcb0991SDimitry Andric     return errc::no_such_file_or_directory;
13268bcb0991SDimitry Andric 
13278bcb0991SDimitry Andric   SmallString<128> AbsolutePath;
13288bcb0991SDimitry Andric   Path.toVector(AbsolutePath);
13298bcb0991SDimitry Andric   if (std::error_code EC = makeAbsolute(AbsolutePath))
13308bcb0991SDimitry Andric     return EC;
1331*a58f00eaSDimitry Andric   WorkingDirectory = std::string(AbsolutePath);
13328bcb0991SDimitry Andric   return {};
13330b57cec5SDimitry Andric }
13340b57cec5SDimitry Andric 
isLocal(const Twine & Path_,bool & Result)1335e8d8bef9SDimitry Andric std::error_code RedirectingFileSystem::isLocal(const Twine &Path_,
13360b57cec5SDimitry Andric                                                bool &Result) {
1337e8d8bef9SDimitry Andric   SmallString<256> Path;
1338e8d8bef9SDimitry Andric   Path_.toVector(Path);
1339e8d8bef9SDimitry Andric 
1340c9157d92SDimitry Andric   if (makeCanonical(Path))
1341e8d8bef9SDimitry Andric     return {};
1342e8d8bef9SDimitry Andric 
13430b57cec5SDimitry Andric   return ExternalFS->isLocal(Path, Result);
13440b57cec5SDimitry Andric }
13450b57cec5SDimitry Andric 
makeAbsolute(SmallVectorImpl<char> & Path) const1346480093f4SDimitry Andric std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1347349cc55cSDimitry Andric   // is_absolute(..., Style::windows_*) accepts paths with both slash types.
1348480093f4SDimitry Andric   if (llvm::sys::path::is_absolute(Path, llvm::sys::path::Style::posix) ||
1349349cc55cSDimitry Andric       llvm::sys::path::is_absolute(Path,
1350349cc55cSDimitry Andric                                    llvm::sys::path::Style::windows_backslash))
1351bdd1243dSDimitry Andric     // This covers windows absolute path with forward slash as well, as the
1352bdd1243dSDimitry Andric     // forward slashes are treated as path seperation in llvm::path
1353bdd1243dSDimitry Andric     // regardless of what path::Style is used.
1354480093f4SDimitry Andric     return {};
1355480093f4SDimitry Andric 
1356480093f4SDimitry Andric   auto WorkingDir = getCurrentWorkingDirectory();
1357480093f4SDimitry Andric   if (!WorkingDir)
1358480093f4SDimitry Andric     return WorkingDir.getError();
1359480093f4SDimitry Andric 
1360bdd1243dSDimitry Andric   return makeAbsolute(WorkingDir.get(), Path);
1361bdd1243dSDimitry Andric }
1362bdd1243dSDimitry Andric 
1363bdd1243dSDimitry Andric std::error_code
makeAbsolute(StringRef WorkingDir,SmallVectorImpl<char> & Path) const1364bdd1243dSDimitry Andric RedirectingFileSystem::makeAbsolute(StringRef WorkingDir,
1365bdd1243dSDimitry Andric                                     SmallVectorImpl<char> &Path) const {
13665ffd83dbSDimitry Andric   // We can't use sys::fs::make_absolute because that assumes the path style
13675ffd83dbSDimitry Andric   // is native and there is no way to override that.  Since we know WorkingDir
13685ffd83dbSDimitry Andric   // is absolute, we can use it to determine which style we actually have and
13695ffd83dbSDimitry Andric   // append Path ourselves.
1370bdd1243dSDimitry Andric   if (!WorkingDir.empty() &&
1371bdd1243dSDimitry Andric       !sys::path::is_absolute(WorkingDir, sys::path::Style::posix) &&
1372bdd1243dSDimitry Andric       !sys::path::is_absolute(WorkingDir,
1373bdd1243dSDimitry Andric                               sys::path::Style::windows_backslash)) {
1374bdd1243dSDimitry Andric     return std::error_code();
1375bdd1243dSDimitry Andric   }
1376349cc55cSDimitry Andric   sys::path::Style style = sys::path::Style::windows_backslash;
1377bdd1243dSDimitry Andric   if (sys::path::is_absolute(WorkingDir, sys::path::Style::posix)) {
13785ffd83dbSDimitry Andric     style = sys::path::Style::posix;
1379349cc55cSDimitry Andric   } else {
1380349cc55cSDimitry Andric     // Distinguish between windows_backslash and windows_slash; getExistingStyle
1381349cc55cSDimitry Andric     // returns posix for a path with windows_slash.
1382bdd1243dSDimitry Andric     if (getExistingStyle(WorkingDir) != sys::path::Style::windows_backslash)
1383349cc55cSDimitry Andric       style = sys::path::Style::windows_slash;
13845ffd83dbSDimitry Andric   }
13855ffd83dbSDimitry Andric 
1386bdd1243dSDimitry Andric   std::string Result = std::string(WorkingDir);
13875ffd83dbSDimitry Andric   StringRef Dir(Result);
1388c9157d92SDimitry Andric   if (!Dir.ends_with(sys::path::get_separator(style))) {
13895ffd83dbSDimitry Andric     Result += sys::path::get_separator(style);
13905ffd83dbSDimitry Andric   }
1391bdd1243dSDimitry Andric   // backslashes '\' are legit path charactors under POSIX. Windows APIs
1392bdd1243dSDimitry Andric   // like CreateFile accepts forward slashes '/' as path
1393bdd1243dSDimitry Andric   // separator (even when mixed with backslashes). Therefore,
1394bdd1243dSDimitry Andric   // `Path` should be directly appended to `WorkingDir` without converting
1395bdd1243dSDimitry Andric   // path separator.
13965ffd83dbSDimitry Andric   Result.append(Path.data(), Path.size());
13975ffd83dbSDimitry Andric   Path.assign(Result.begin(), Result.end());
13985ffd83dbSDimitry Andric 
1399480093f4SDimitry Andric   return {};
1400480093f4SDimitry Andric }
1401480093f4SDimitry Andric 
dir_begin(const Twine & Dir,std::error_code & EC)14020b57cec5SDimitry Andric directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
14030b57cec5SDimitry Andric                                                     std::error_code &EC) {
1404e8d8bef9SDimitry Andric   SmallString<256> Path;
1405e8d8bef9SDimitry Andric   Dir.toVector(Path);
1406e8d8bef9SDimitry Andric 
1407e8d8bef9SDimitry Andric   EC = makeCanonical(Path);
1408e8d8bef9SDimitry Andric   if (EC)
1409e8d8bef9SDimitry Andric     return {};
1410e8d8bef9SDimitry Andric 
1411fe6060f1SDimitry Andric   ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
1412fe6060f1SDimitry Andric   if (!Result) {
141381ad6265SDimitry Andric     if (Redirection != RedirectKind::RedirectOnly &&
141481ad6265SDimitry Andric         isFileNotFound(Result.getError()))
1415e8d8bef9SDimitry Andric       return ExternalFS->dir_begin(Path, EC);
141681ad6265SDimitry Andric 
141781ad6265SDimitry Andric     EC = Result.getError();
14180b57cec5SDimitry Andric     return {};
14190b57cec5SDimitry Andric   }
1420fe6060f1SDimitry Andric 
1421fe6060f1SDimitry Andric   // Use status to make sure the path exists and refers to a directory.
1422349cc55cSDimitry Andric   ErrorOr<Status> S = status(Path, Dir, *Result);
14230b57cec5SDimitry Andric   if (!S) {
142481ad6265SDimitry Andric     if (Redirection != RedirectKind::RedirectOnly &&
142581ad6265SDimitry Andric         isFileNotFound(S.getError(), Result->E))
1426fe6060f1SDimitry Andric       return ExternalFS->dir_begin(Dir, EC);
142781ad6265SDimitry Andric 
14280b57cec5SDimitry Andric     EC = S.getError();
14290b57cec5SDimitry Andric     return {};
14300b57cec5SDimitry Andric   }
143181ad6265SDimitry Andric 
14320b57cec5SDimitry Andric   if (!S->isDirectory()) {
143381ad6265SDimitry Andric     EC = errc::not_a_directory;
14340b57cec5SDimitry Andric     return {};
14350b57cec5SDimitry Andric   }
14360b57cec5SDimitry Andric 
1437fe6060f1SDimitry Andric   // Create the appropriate directory iterator based on whether we found a
1438fe6060f1SDimitry Andric   // DirectoryRemapEntry or DirectoryEntry.
143981ad6265SDimitry Andric   directory_iterator RedirectIter;
144081ad6265SDimitry Andric   std::error_code RedirectEC;
1441fe6060f1SDimitry Andric   if (auto ExtRedirect = Result->getExternalRedirect()) {
1442fe6060f1SDimitry Andric     auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
144381ad6265SDimitry Andric     RedirectIter = ExternalFS->dir_begin(*ExtRedirect, RedirectEC);
1444fe6060f1SDimitry Andric 
1445fe6060f1SDimitry Andric     if (!RE->useExternalName(UseExternalNames)) {
1446fe6060f1SDimitry Andric       // Update the paths in the results to use the virtual directory's path.
144781ad6265SDimitry Andric       RedirectIter =
1448fe6060f1SDimitry Andric           directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>(
144981ad6265SDimitry Andric               std::string(Path), RedirectIter));
1450fe6060f1SDimitry Andric     }
1451fe6060f1SDimitry Andric   } else {
1452fe6060f1SDimitry Andric     auto DE = cast<DirectoryEntry>(Result->E);
145381ad6265SDimitry Andric     RedirectIter =
145481ad6265SDimitry Andric         directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
145581ad6265SDimitry Andric             Path, DE->contents_begin(), DE->contents_end(), RedirectEC));
1456fe6060f1SDimitry Andric   }
1457fe6060f1SDimitry Andric 
145881ad6265SDimitry Andric   if (RedirectEC) {
145981ad6265SDimitry Andric     if (RedirectEC != errc::no_such_file_or_directory) {
146081ad6265SDimitry Andric       EC = RedirectEC;
146181ad6265SDimitry Andric       return {};
146281ad6265SDimitry Andric     }
146381ad6265SDimitry Andric     RedirectIter = {};
146481ad6265SDimitry Andric   }
146581ad6265SDimitry Andric 
146681ad6265SDimitry Andric   if (Redirection == RedirectKind::RedirectOnly) {
146781ad6265SDimitry Andric     EC = RedirectEC;
146881ad6265SDimitry Andric     return RedirectIter;
146981ad6265SDimitry Andric   }
147081ad6265SDimitry Andric 
147181ad6265SDimitry Andric   std::error_code ExternalEC;
147281ad6265SDimitry Andric   directory_iterator ExternalIter = ExternalFS->dir_begin(Path, ExternalEC);
147381ad6265SDimitry Andric   if (ExternalEC) {
147481ad6265SDimitry Andric     if (ExternalEC != errc::no_such_file_or_directory) {
147581ad6265SDimitry Andric       EC = ExternalEC;
147681ad6265SDimitry Andric       return {};
147781ad6265SDimitry Andric     }
147881ad6265SDimitry Andric     ExternalIter = {};
147981ad6265SDimitry Andric   }
148081ad6265SDimitry Andric 
148181ad6265SDimitry Andric   SmallVector<directory_iterator, 2> Iters;
148281ad6265SDimitry Andric   switch (Redirection) {
148381ad6265SDimitry Andric   case RedirectKind::Fallthrough:
148481ad6265SDimitry Andric     Iters.push_back(ExternalIter);
148581ad6265SDimitry Andric     Iters.push_back(RedirectIter);
148681ad6265SDimitry Andric     break;
148781ad6265SDimitry Andric   case RedirectKind::Fallback:
148881ad6265SDimitry Andric     Iters.push_back(RedirectIter);
148981ad6265SDimitry Andric     Iters.push_back(ExternalIter);
149081ad6265SDimitry Andric     break;
149181ad6265SDimitry Andric   default:
149281ad6265SDimitry Andric     llvm_unreachable("unhandled RedirectKind");
149381ad6265SDimitry Andric   }
149481ad6265SDimitry Andric 
149581ad6265SDimitry Andric   directory_iterator Combined{
149681ad6265SDimitry Andric       std::make_shared<CombiningDirIterImpl>(Iters, EC)};
149781ad6265SDimitry Andric   if (EC)
149881ad6265SDimitry Andric     return {};
149981ad6265SDimitry Andric   return Combined;
15000b57cec5SDimitry Andric }
15010b57cec5SDimitry Andric 
setOverlayFileDir(StringRef Dir)1502bdd1243dSDimitry Andric void RedirectingFileSystem::setOverlayFileDir(StringRef Dir) {
1503bdd1243dSDimitry Andric   OverlayFileDir = Dir.str();
15040b57cec5SDimitry Andric }
15050b57cec5SDimitry Andric 
getOverlayFileDir() const1506bdd1243dSDimitry Andric StringRef RedirectingFileSystem::getOverlayFileDir() const {
1507bdd1243dSDimitry Andric   return OverlayFileDir;
15080b57cec5SDimitry Andric }
15090b57cec5SDimitry Andric 
setFallthrough(bool Fallthrough)1510e8d8bef9SDimitry Andric void RedirectingFileSystem::setFallthrough(bool Fallthrough) {
151181ad6265SDimitry Andric   if (Fallthrough) {
151281ad6265SDimitry Andric     Redirection = RedirectingFileSystem::RedirectKind::Fallthrough;
151381ad6265SDimitry Andric   } else {
151481ad6265SDimitry Andric     Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly;
151581ad6265SDimitry Andric   }
151681ad6265SDimitry Andric }
151781ad6265SDimitry Andric 
setRedirection(RedirectingFileSystem::RedirectKind Kind)151881ad6265SDimitry Andric void RedirectingFileSystem::setRedirection(
151981ad6265SDimitry Andric     RedirectingFileSystem::RedirectKind Kind) {
152081ad6265SDimitry Andric   Redirection = Kind;
1521e8d8bef9SDimitry Andric }
1522e8d8bef9SDimitry Andric 
getRoots() const1523e8d8bef9SDimitry Andric std::vector<StringRef> RedirectingFileSystem::getRoots() const {
1524e8d8bef9SDimitry Andric   std::vector<StringRef> R;
1525bdd1243dSDimitry Andric   R.reserve(Roots.size());
1526e8d8bef9SDimitry Andric   for (const auto &Root : Roots)
1527e8d8bef9SDimitry Andric     R.push_back(Root->getName());
1528e8d8bef9SDimitry Andric   return R;
1529e8d8bef9SDimitry Andric }
1530e8d8bef9SDimitry Andric 
printImpl(raw_ostream & OS,PrintType Type,unsigned IndentLevel) const153181ad6265SDimitry Andric void RedirectingFileSystem::printImpl(raw_ostream &OS, PrintType Type,
153281ad6265SDimitry Andric                                       unsigned IndentLevel) const {
153381ad6265SDimitry Andric   printIndent(OS, IndentLevel);
153481ad6265SDimitry Andric   OS << "RedirectingFileSystem (UseExternalNames: "
153581ad6265SDimitry Andric      << (UseExternalNames ? "true" : "false") << ")\n";
153681ad6265SDimitry Andric   if (Type == PrintType::Summary)
153781ad6265SDimitry Andric     return;
153881ad6265SDimitry Andric 
15390b57cec5SDimitry Andric   for (const auto &Root : Roots)
154081ad6265SDimitry Andric     printEntry(OS, Root.get(), IndentLevel);
154181ad6265SDimitry Andric 
154281ad6265SDimitry Andric   printIndent(OS, IndentLevel);
154381ad6265SDimitry Andric   OS << "ExternalFS:\n";
154481ad6265SDimitry Andric   ExternalFS->print(OS, Type == PrintType::Contents ? PrintType::Summary : Type,
154581ad6265SDimitry Andric                     IndentLevel + 1);
15460b57cec5SDimitry Andric }
15470b57cec5SDimitry Andric 
printEntry(raw_ostream & OS,RedirectingFileSystem::Entry * E,unsigned IndentLevel) const154881ad6265SDimitry Andric void RedirectingFileSystem::printEntry(raw_ostream &OS,
15498bcb0991SDimitry Andric                                        RedirectingFileSystem::Entry *E,
155081ad6265SDimitry Andric                                        unsigned IndentLevel) const {
155181ad6265SDimitry Andric   printIndent(OS, IndentLevel);
155281ad6265SDimitry Andric   OS << "'" << E->getName() << "'";
15530b57cec5SDimitry Andric 
155481ad6265SDimitry Andric   switch (E->getKind()) {
155581ad6265SDimitry Andric   case EK_Directory: {
155681ad6265SDimitry Andric     auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(E);
15570b57cec5SDimitry Andric 
155881ad6265SDimitry Andric     OS << "\n";
15590b57cec5SDimitry Andric     for (std::unique_ptr<Entry> &SubEntry :
15600b57cec5SDimitry Andric          llvm::make_range(DE->contents_begin(), DE->contents_end()))
156181ad6265SDimitry Andric       printEntry(OS, SubEntry.get(), IndentLevel + 1);
156281ad6265SDimitry Andric     break;
156381ad6265SDimitry Andric   }
156481ad6265SDimitry Andric   case EK_DirectoryRemap:
156581ad6265SDimitry Andric   case EK_File: {
156681ad6265SDimitry Andric     auto *RE = cast<RedirectingFileSystem::RemapEntry>(E);
156781ad6265SDimitry Andric     OS << " -> '" << RE->getExternalContentsPath() << "'";
156881ad6265SDimitry Andric     switch (RE->getUseName()) {
156981ad6265SDimitry Andric     case NK_NotSet:
157081ad6265SDimitry Andric       break;
157181ad6265SDimitry Andric     case NK_External:
157281ad6265SDimitry Andric       OS << " (UseExternalName: true)";
157381ad6265SDimitry Andric       break;
157481ad6265SDimitry Andric     case NK_Virtual:
157581ad6265SDimitry Andric       OS << " (UseExternalName: false)";
157681ad6265SDimitry Andric       break;
157781ad6265SDimitry Andric     }
157881ad6265SDimitry Andric     OS << "\n";
157981ad6265SDimitry Andric     break;
15800b57cec5SDimitry Andric   }
15810b57cec5SDimitry Andric   }
158281ad6265SDimitry Andric }
15830b57cec5SDimitry Andric 
15840b57cec5SDimitry Andric /// A helper class to hold the common YAML parsing state.
15850b57cec5SDimitry Andric class llvm::vfs::RedirectingFileSystemParser {
15860b57cec5SDimitry Andric   yaml::Stream &Stream;
15870b57cec5SDimitry Andric 
error(yaml::Node * N,const Twine & Msg)15880b57cec5SDimitry Andric   void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); }
15890b57cec5SDimitry Andric 
15900b57cec5SDimitry Andric   // false on error
parseScalarString(yaml::Node * N,StringRef & Result,SmallVectorImpl<char> & Storage)15910b57cec5SDimitry Andric   bool parseScalarString(yaml::Node *N, StringRef &Result,
15920b57cec5SDimitry Andric                          SmallVectorImpl<char> &Storage) {
15930b57cec5SDimitry Andric     const auto *S = dyn_cast<yaml::ScalarNode>(N);
15940b57cec5SDimitry Andric 
15950b57cec5SDimitry Andric     if (!S) {
15960b57cec5SDimitry Andric       error(N, "expected string");
15970b57cec5SDimitry Andric       return false;
15980b57cec5SDimitry Andric     }
15990b57cec5SDimitry Andric     Result = S->getValue(Storage);
16000b57cec5SDimitry Andric     return true;
16010b57cec5SDimitry Andric   }
16020b57cec5SDimitry Andric 
16030b57cec5SDimitry Andric   // false on error
parseScalarBool(yaml::Node * N,bool & Result)16040b57cec5SDimitry Andric   bool parseScalarBool(yaml::Node *N, bool &Result) {
16050b57cec5SDimitry Andric     SmallString<5> Storage;
16060b57cec5SDimitry Andric     StringRef Value;
16070b57cec5SDimitry Andric     if (!parseScalarString(N, Value, Storage))
16080b57cec5SDimitry Andric       return false;
16090b57cec5SDimitry Andric 
1610fe6060f1SDimitry Andric     if (Value.equals_insensitive("true") || Value.equals_insensitive("on") ||
1611fe6060f1SDimitry Andric         Value.equals_insensitive("yes") || Value == "1") {
16120b57cec5SDimitry Andric       Result = true;
16130b57cec5SDimitry Andric       return true;
1614fe6060f1SDimitry Andric     } else if (Value.equals_insensitive("false") ||
1615fe6060f1SDimitry Andric                Value.equals_insensitive("off") ||
1616fe6060f1SDimitry Andric                Value.equals_insensitive("no") || Value == "0") {
16170b57cec5SDimitry Andric       Result = false;
16180b57cec5SDimitry Andric       return true;
16190b57cec5SDimitry Andric     }
16200b57cec5SDimitry Andric 
16210b57cec5SDimitry Andric     error(N, "expected boolean value");
16220b57cec5SDimitry Andric     return false;
16230b57cec5SDimitry Andric   }
16240b57cec5SDimitry Andric 
1625bdd1243dSDimitry Andric   std::optional<RedirectingFileSystem::RedirectKind>
parseRedirectKind(yaml::Node * N)162681ad6265SDimitry Andric   parseRedirectKind(yaml::Node *N) {
162781ad6265SDimitry Andric     SmallString<12> Storage;
162881ad6265SDimitry Andric     StringRef Value;
162981ad6265SDimitry Andric     if (!parseScalarString(N, Value, Storage))
1630bdd1243dSDimitry Andric       return std::nullopt;
163181ad6265SDimitry Andric 
163281ad6265SDimitry Andric     if (Value.equals_insensitive("fallthrough")) {
163381ad6265SDimitry Andric       return RedirectingFileSystem::RedirectKind::Fallthrough;
163481ad6265SDimitry Andric     } else if (Value.equals_insensitive("fallback")) {
163581ad6265SDimitry Andric       return RedirectingFileSystem::RedirectKind::Fallback;
163681ad6265SDimitry Andric     } else if (Value.equals_insensitive("redirect-only")) {
163781ad6265SDimitry Andric       return RedirectingFileSystem::RedirectKind::RedirectOnly;
163881ad6265SDimitry Andric     }
1639bdd1243dSDimitry Andric     return std::nullopt;
1640bdd1243dSDimitry Andric   }
1641bdd1243dSDimitry Andric 
1642bdd1243dSDimitry Andric   std::optional<RedirectingFileSystem::RootRelativeKind>
parseRootRelativeKind(yaml::Node * N)1643bdd1243dSDimitry Andric   parseRootRelativeKind(yaml::Node *N) {
1644bdd1243dSDimitry Andric     SmallString<12> Storage;
1645bdd1243dSDimitry Andric     StringRef Value;
1646bdd1243dSDimitry Andric     if (!parseScalarString(N, Value, Storage))
1647bdd1243dSDimitry Andric       return std::nullopt;
1648bdd1243dSDimitry Andric     if (Value.equals_insensitive("cwd")) {
1649bdd1243dSDimitry Andric       return RedirectingFileSystem::RootRelativeKind::CWD;
1650bdd1243dSDimitry Andric     } else if (Value.equals_insensitive("overlay-dir")) {
1651bdd1243dSDimitry Andric       return RedirectingFileSystem::RootRelativeKind::OverlayDir;
1652bdd1243dSDimitry Andric     }
1653bdd1243dSDimitry Andric     return std::nullopt;
165481ad6265SDimitry Andric   }
165581ad6265SDimitry Andric 
16560b57cec5SDimitry Andric   struct KeyStatus {
16570b57cec5SDimitry Andric     bool Required;
16580b57cec5SDimitry Andric     bool Seen = false;
16590b57cec5SDimitry Andric 
KeyStatusllvm::vfs::RedirectingFileSystemParser::KeyStatus16600b57cec5SDimitry Andric     KeyStatus(bool Required = false) : Required(Required) {}
16610b57cec5SDimitry Andric   };
16620b57cec5SDimitry Andric 
16630b57cec5SDimitry Andric   using KeyStatusPair = std::pair<StringRef, KeyStatus>;
16640b57cec5SDimitry Andric 
16650b57cec5SDimitry Andric   // false on error
checkDuplicateOrUnknownKey(yaml::Node * KeyNode,StringRef Key,DenseMap<StringRef,KeyStatus> & Keys)16660b57cec5SDimitry Andric   bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
16670b57cec5SDimitry Andric                                   DenseMap<StringRef, KeyStatus> &Keys) {
16680b57cec5SDimitry Andric     if (!Keys.count(Key)) {
16690b57cec5SDimitry Andric       error(KeyNode, "unknown key");
16700b57cec5SDimitry Andric       return false;
16710b57cec5SDimitry Andric     }
16720b57cec5SDimitry Andric     KeyStatus &S = Keys[Key];
16730b57cec5SDimitry Andric     if (S.Seen) {
16740b57cec5SDimitry Andric       error(KeyNode, Twine("duplicate key '") + Key + "'");
16750b57cec5SDimitry Andric       return false;
16760b57cec5SDimitry Andric     }
16770b57cec5SDimitry Andric     S.Seen = true;
16780b57cec5SDimitry Andric     return true;
16790b57cec5SDimitry Andric   }
16800b57cec5SDimitry Andric 
16810b57cec5SDimitry Andric   // false on error
checkMissingKeys(yaml::Node * Obj,DenseMap<StringRef,KeyStatus> & Keys)16820b57cec5SDimitry Andric   bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
16830b57cec5SDimitry Andric     for (const auto &I : Keys) {
16840b57cec5SDimitry Andric       if (I.second.Required && !I.second.Seen) {
16850b57cec5SDimitry Andric         error(Obj, Twine("missing key '") + I.first + "'");
16860b57cec5SDimitry Andric         return false;
16870b57cec5SDimitry Andric       }
16880b57cec5SDimitry Andric     }
16890b57cec5SDimitry Andric     return true;
16900b57cec5SDimitry Andric   }
16910b57cec5SDimitry Andric 
1692e8d8bef9SDimitry Andric public:
1693e8d8bef9SDimitry Andric   static RedirectingFileSystem::Entry *
lookupOrCreateEntry(RedirectingFileSystem * FS,StringRef Name,RedirectingFileSystem::Entry * ParentEntry=nullptr)16940b57cec5SDimitry Andric   lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,
16950b57cec5SDimitry Andric                       RedirectingFileSystem::Entry *ParentEntry = nullptr) {
16960b57cec5SDimitry Andric     if (!ParentEntry) { // Look for a existent root
16970b57cec5SDimitry Andric       for (const auto &Root : FS->Roots) {
16980b57cec5SDimitry Andric         if (Name.equals(Root->getName())) {
16990b57cec5SDimitry Andric           ParentEntry = Root.get();
17000b57cec5SDimitry Andric           return ParentEntry;
17010b57cec5SDimitry Andric         }
17020b57cec5SDimitry Andric       }
17030b57cec5SDimitry Andric     } else { // Advance to the next component
1704fe6060f1SDimitry Andric       auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
17050b57cec5SDimitry Andric       for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :
17060b57cec5SDimitry Andric            llvm::make_range(DE->contents_begin(), DE->contents_end())) {
17070b57cec5SDimitry Andric         auto *DirContent =
1708fe6060f1SDimitry Andric             dyn_cast<RedirectingFileSystem::DirectoryEntry>(Content.get());
17090b57cec5SDimitry Andric         if (DirContent && Name.equals(Content->getName()))
17100b57cec5SDimitry Andric           return DirContent;
17110b57cec5SDimitry Andric       }
17120b57cec5SDimitry Andric     }
17130b57cec5SDimitry Andric 
17140b57cec5SDimitry Andric     // ... or create a new one
17150b57cec5SDimitry Andric     std::unique_ptr<RedirectingFileSystem::Entry> E =
1716fe6060f1SDimitry Andric         std::make_unique<RedirectingFileSystem::DirectoryEntry>(
17170b57cec5SDimitry Andric             Name, Status("", getNextVirtualUniqueID(),
17180b57cec5SDimitry Andric                          std::chrono::system_clock::now(), 0, 0, 0,
17190b57cec5SDimitry Andric                          file_type::directory_file, sys::fs::all_all));
17200b57cec5SDimitry Andric 
17210b57cec5SDimitry Andric     if (!ParentEntry) { // Add a new root to the overlay
17220b57cec5SDimitry Andric       FS->Roots.push_back(std::move(E));
17230b57cec5SDimitry Andric       ParentEntry = FS->Roots.back().get();
17240b57cec5SDimitry Andric       return ParentEntry;
17250b57cec5SDimitry Andric     }
17260b57cec5SDimitry Andric 
1727fe6060f1SDimitry Andric     auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
17280b57cec5SDimitry Andric     DE->addContent(std::move(E));
17290b57cec5SDimitry Andric     return DE->getLastContent();
17300b57cec5SDimitry Andric   }
17310b57cec5SDimitry Andric 
1732e8d8bef9SDimitry Andric private:
uniqueOverlayTree(RedirectingFileSystem * FS,RedirectingFileSystem::Entry * SrcE,RedirectingFileSystem::Entry * NewParentE=nullptr)17330b57cec5SDimitry Andric   void uniqueOverlayTree(RedirectingFileSystem *FS,
17340b57cec5SDimitry Andric                          RedirectingFileSystem::Entry *SrcE,
17350b57cec5SDimitry Andric                          RedirectingFileSystem::Entry *NewParentE = nullptr) {
17360b57cec5SDimitry Andric     StringRef Name = SrcE->getName();
17370b57cec5SDimitry Andric     switch (SrcE->getKind()) {
17380b57cec5SDimitry Andric     case RedirectingFileSystem::EK_Directory: {
1739fe6060f1SDimitry Andric       auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
17400b57cec5SDimitry Andric       // Empty directories could be present in the YAML as a way to
17410b57cec5SDimitry Andric       // describe a file for a current directory after some of its subdir
17420b57cec5SDimitry Andric       // is parsed. This only leads to redundant walks, ignore it.
17430b57cec5SDimitry Andric       if (!Name.empty())
17440b57cec5SDimitry Andric         NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
17450b57cec5SDimitry Andric       for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
17460b57cec5SDimitry Andric            llvm::make_range(DE->contents_begin(), DE->contents_end()))
17470b57cec5SDimitry Andric         uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
17480b57cec5SDimitry Andric       break;
17490b57cec5SDimitry Andric     }
1750fe6060f1SDimitry Andric     case RedirectingFileSystem::EK_DirectoryRemap: {
1751fe6060f1SDimitry Andric       assert(NewParentE && "Parent entry must exist");
1752fe6060f1SDimitry Andric       auto *DR = cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
1753fe6060f1SDimitry Andric       auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1754fe6060f1SDimitry Andric       DE->addContent(
1755fe6060f1SDimitry Andric           std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
1756fe6060f1SDimitry Andric               Name, DR->getExternalContentsPath(), DR->getUseName()));
1757fe6060f1SDimitry Andric       break;
1758fe6060f1SDimitry Andric     }
17590b57cec5SDimitry Andric     case RedirectingFileSystem::EK_File: {
17600b57cec5SDimitry Andric       assert(NewParentE && "Parent entry must exist");
1761fe6060f1SDimitry Andric       auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);
1762fe6060f1SDimitry Andric       auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1763fe6060f1SDimitry Andric       DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>(
17640b57cec5SDimitry Andric           Name, FE->getExternalContentsPath(), FE->getUseName()));
17650b57cec5SDimitry Andric       break;
17660b57cec5SDimitry Andric     }
17670b57cec5SDimitry Andric     }
17680b57cec5SDimitry Andric   }
17690b57cec5SDimitry Andric 
17700b57cec5SDimitry Andric   std::unique_ptr<RedirectingFileSystem::Entry>
parseEntry(yaml::Node * N,RedirectingFileSystem * FS,bool IsRootEntry)17710b57cec5SDimitry Andric   parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) {
17720b57cec5SDimitry Andric     auto *M = dyn_cast<yaml::MappingNode>(N);
17730b57cec5SDimitry Andric     if (!M) {
17740b57cec5SDimitry Andric       error(N, "expected mapping node for file or directory entry");
17750b57cec5SDimitry Andric       return nullptr;
17760b57cec5SDimitry Andric     }
17770b57cec5SDimitry Andric 
17780b57cec5SDimitry Andric     KeyStatusPair Fields[] = {
17790b57cec5SDimitry Andric         KeyStatusPair("name", true),
17800b57cec5SDimitry Andric         KeyStatusPair("type", true),
17810b57cec5SDimitry Andric         KeyStatusPair("contents", false),
17820b57cec5SDimitry Andric         KeyStatusPair("external-contents", false),
17830b57cec5SDimitry Andric         KeyStatusPair("use-external-name", false),
17840b57cec5SDimitry Andric     };
17850b57cec5SDimitry Andric 
17860b57cec5SDimitry Andric     DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
17870b57cec5SDimitry Andric 
1788fe6060f1SDimitry Andric     enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;
17890b57cec5SDimitry Andric     std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
17900b57cec5SDimitry Andric         EntryArrayContents;
17915ffd83dbSDimitry Andric     SmallString<256> ExternalContentsPath;
17925ffd83dbSDimitry Andric     SmallString<256> Name;
17930b57cec5SDimitry Andric     yaml::Node *NameValueNode = nullptr;
1794fe6060f1SDimitry Andric     auto UseExternalName = RedirectingFileSystem::NK_NotSet;
17950b57cec5SDimitry Andric     RedirectingFileSystem::EntryKind Kind;
17960b57cec5SDimitry Andric 
17970b57cec5SDimitry Andric     for (auto &I : *M) {
17980b57cec5SDimitry Andric       StringRef Key;
17990b57cec5SDimitry Andric       // Reuse the buffer for key and value, since we don't look at key after
18000b57cec5SDimitry Andric       // parsing value.
18010b57cec5SDimitry Andric       SmallString<256> Buffer;
18020b57cec5SDimitry Andric       if (!parseScalarString(I.getKey(), Key, Buffer))
18030b57cec5SDimitry Andric         return nullptr;
18040b57cec5SDimitry Andric 
18050b57cec5SDimitry Andric       if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
18060b57cec5SDimitry Andric         return nullptr;
18070b57cec5SDimitry Andric 
18080b57cec5SDimitry Andric       StringRef Value;
18090b57cec5SDimitry Andric       if (Key == "name") {
18100b57cec5SDimitry Andric         if (!parseScalarString(I.getValue(), Value, Buffer))
18110b57cec5SDimitry Andric           return nullptr;
18120b57cec5SDimitry Andric 
18130b57cec5SDimitry Andric         NameValueNode = I.getValue();
18140b57cec5SDimitry Andric         // Guarantee that old YAML files containing paths with ".." and "."
18150b57cec5SDimitry Andric         // are properly canonicalized before read into the VFS.
18165ffd83dbSDimitry Andric         Name = canonicalize(Value).str();
18170b57cec5SDimitry Andric       } else if (Key == "type") {
18180b57cec5SDimitry Andric         if (!parseScalarString(I.getValue(), Value, Buffer))
18190b57cec5SDimitry Andric           return nullptr;
18200b57cec5SDimitry Andric         if (Value == "file")
18210b57cec5SDimitry Andric           Kind = RedirectingFileSystem::EK_File;
18220b57cec5SDimitry Andric         else if (Value == "directory")
18230b57cec5SDimitry Andric           Kind = RedirectingFileSystem::EK_Directory;
1824fe6060f1SDimitry Andric         else if (Value == "directory-remap")
1825fe6060f1SDimitry Andric           Kind = RedirectingFileSystem::EK_DirectoryRemap;
18260b57cec5SDimitry Andric         else {
18270b57cec5SDimitry Andric           error(I.getValue(), "unknown value for 'type'");
18280b57cec5SDimitry Andric           return nullptr;
18290b57cec5SDimitry Andric         }
18300b57cec5SDimitry Andric       } else if (Key == "contents") {
1831fe6060f1SDimitry Andric         if (ContentsField != CF_NotSet) {
18320b57cec5SDimitry Andric           error(I.getKey(),
18330b57cec5SDimitry Andric                 "entry already has 'contents' or 'external-contents'");
18340b57cec5SDimitry Andric           return nullptr;
18350b57cec5SDimitry Andric         }
1836fe6060f1SDimitry Andric         ContentsField = CF_List;
18370b57cec5SDimitry Andric         auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
18380b57cec5SDimitry Andric         if (!Contents) {
18390b57cec5SDimitry Andric           // FIXME: this is only for directories, what about files?
18400b57cec5SDimitry Andric           error(I.getValue(), "expected array");
18410b57cec5SDimitry Andric           return nullptr;
18420b57cec5SDimitry Andric         }
18430b57cec5SDimitry Andric 
18440b57cec5SDimitry Andric         for (auto &I : *Contents) {
18450b57cec5SDimitry Andric           if (std::unique_ptr<RedirectingFileSystem::Entry> E =
18460b57cec5SDimitry Andric                   parseEntry(&I, FS, /*IsRootEntry*/ false))
18470b57cec5SDimitry Andric             EntryArrayContents.push_back(std::move(E));
18480b57cec5SDimitry Andric           else
18490b57cec5SDimitry Andric             return nullptr;
18500b57cec5SDimitry Andric         }
18510b57cec5SDimitry Andric       } else if (Key == "external-contents") {
1852fe6060f1SDimitry Andric         if (ContentsField != CF_NotSet) {
18530b57cec5SDimitry Andric           error(I.getKey(),
18540b57cec5SDimitry Andric                 "entry already has 'contents' or 'external-contents'");
18550b57cec5SDimitry Andric           return nullptr;
18560b57cec5SDimitry Andric         }
1857fe6060f1SDimitry Andric         ContentsField = CF_External;
18580b57cec5SDimitry Andric         if (!parseScalarString(I.getValue(), Value, Buffer))
18590b57cec5SDimitry Andric           return nullptr;
18600b57cec5SDimitry Andric 
18610b57cec5SDimitry Andric         SmallString<256> FullPath;
18620b57cec5SDimitry Andric         if (FS->IsRelativeOverlay) {
1863bdd1243dSDimitry Andric           FullPath = FS->getOverlayFileDir();
18640b57cec5SDimitry Andric           assert(!FullPath.empty() &&
18650b57cec5SDimitry Andric                  "External contents prefix directory must exist");
18660b57cec5SDimitry Andric           llvm::sys::path::append(FullPath, Value);
18670b57cec5SDimitry Andric         } else {
18680b57cec5SDimitry Andric           FullPath = Value;
18690b57cec5SDimitry Andric         }
18700b57cec5SDimitry Andric 
18710b57cec5SDimitry Andric         // Guarantee that old YAML files containing paths with ".." and "."
18720b57cec5SDimitry Andric         // are properly canonicalized before read into the VFS.
18735ffd83dbSDimitry Andric         FullPath = canonicalize(FullPath);
18740b57cec5SDimitry Andric         ExternalContentsPath = FullPath.str();
18750b57cec5SDimitry Andric       } else if (Key == "use-external-name") {
18760b57cec5SDimitry Andric         bool Val;
18770b57cec5SDimitry Andric         if (!parseScalarBool(I.getValue(), Val))
18780b57cec5SDimitry Andric           return nullptr;
1879fe6060f1SDimitry Andric         UseExternalName = Val ? RedirectingFileSystem::NK_External
1880fe6060f1SDimitry Andric                               : RedirectingFileSystem::NK_Virtual;
18810b57cec5SDimitry Andric       } else {
18820b57cec5SDimitry Andric         llvm_unreachable("key missing from Keys");
18830b57cec5SDimitry Andric       }
18840b57cec5SDimitry Andric     }
18850b57cec5SDimitry Andric 
18860b57cec5SDimitry Andric     if (Stream.failed())
18870b57cec5SDimitry Andric       return nullptr;
18880b57cec5SDimitry Andric 
18890b57cec5SDimitry Andric     // check for missing keys
1890fe6060f1SDimitry Andric     if (ContentsField == CF_NotSet) {
18910b57cec5SDimitry Andric       error(N, "missing key 'contents' or 'external-contents'");
18920b57cec5SDimitry Andric       return nullptr;
18930b57cec5SDimitry Andric     }
18940b57cec5SDimitry Andric     if (!checkMissingKeys(N, Keys))
18950b57cec5SDimitry Andric       return nullptr;
18960b57cec5SDimitry Andric 
18970b57cec5SDimitry Andric     // check invalid configuration
18980b57cec5SDimitry Andric     if (Kind == RedirectingFileSystem::EK_Directory &&
1899fe6060f1SDimitry Andric         UseExternalName != RedirectingFileSystem::NK_NotSet) {
1900fe6060f1SDimitry Andric       error(N, "'use-external-name' is not supported for 'directory' entries");
1901fe6060f1SDimitry Andric       return nullptr;
1902fe6060f1SDimitry Andric     }
1903fe6060f1SDimitry Andric 
1904fe6060f1SDimitry Andric     if (Kind == RedirectingFileSystem::EK_DirectoryRemap &&
1905fe6060f1SDimitry Andric         ContentsField == CF_List) {
1906fe6060f1SDimitry Andric       error(N, "'contents' is not supported for 'directory-remap' entries");
19070b57cec5SDimitry Andric       return nullptr;
19080b57cec5SDimitry Andric     }
19090b57cec5SDimitry Andric 
1910480093f4SDimitry Andric     sys::path::Style path_style = sys::path::Style::native;
1911480093f4SDimitry Andric     if (IsRootEntry) {
1912480093f4SDimitry Andric       // VFS root entries may be in either Posix or Windows style.  Figure out
1913480093f4SDimitry Andric       // which style we have, and use it consistently.
1914480093f4SDimitry Andric       if (sys::path::is_absolute(Name, sys::path::Style::posix)) {
1915480093f4SDimitry Andric         path_style = sys::path::Style::posix;
1916349cc55cSDimitry Andric       } else if (sys::path::is_absolute(Name,
1917349cc55cSDimitry Andric                                         sys::path::Style::windows_backslash)) {
1918349cc55cSDimitry Andric         path_style = sys::path::Style::windows_backslash;
1919480093f4SDimitry Andric       } else {
1920bdd1243dSDimitry Andric         // Relative VFS root entries are made absolute to either the overlay
1921bdd1243dSDimitry Andric         // directory, or the current working directory, then we can determine
1922bdd1243dSDimitry Andric         // the path style from that.
1923bdd1243dSDimitry Andric         std::error_code EC;
1924bdd1243dSDimitry Andric         if (FS->RootRelative ==
1925bdd1243dSDimitry Andric             RedirectingFileSystem::RootRelativeKind::OverlayDir) {
1926bdd1243dSDimitry Andric           StringRef FullPath = FS->getOverlayFileDir();
1927bdd1243dSDimitry Andric           assert(!FullPath.empty() && "Overlay file directory must exist");
1928bdd1243dSDimitry Andric           EC = FS->makeAbsolute(FullPath, Name);
1929bdd1243dSDimitry Andric           Name = canonicalize(Name);
1930bdd1243dSDimitry Andric         } else {
1931bdd1243dSDimitry Andric           EC = sys::fs::make_absolute(Name);
1932bdd1243dSDimitry Andric         }
193304eeddc0SDimitry Andric         if (EC) {
19340b57cec5SDimitry Andric           assert(NameValueNode && "Name presence should be checked earlier");
193504eeddc0SDimitry Andric           error(
193604eeddc0SDimitry Andric               NameValueNode,
19370b57cec5SDimitry Andric               "entry with relative path at the root level is not discoverable");
19380b57cec5SDimitry Andric           return nullptr;
19390b57cec5SDimitry Andric         }
194004eeddc0SDimitry Andric         path_style = sys::path::is_absolute(Name, sys::path::Style::posix)
194104eeddc0SDimitry Andric                          ? sys::path::Style::posix
194204eeddc0SDimitry Andric                          : sys::path::Style::windows_backslash;
194304eeddc0SDimitry Andric       }
1944bdd1243dSDimitry Andric       // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will
1945bdd1243dSDimitry Andric       // return true even if `Name` is using forward slashes. Distinguish
1946bdd1243dSDimitry Andric       // between windows_backslash and windows_slash.
1947bdd1243dSDimitry Andric       if (path_style == sys::path::Style::windows_backslash &&
1948bdd1243dSDimitry Andric           getExistingStyle(Name) != sys::path::Style::windows_backslash)
1949bdd1243dSDimitry Andric         path_style = sys::path::Style::windows_slash;
1950480093f4SDimitry Andric     }
19510b57cec5SDimitry Andric 
19520b57cec5SDimitry Andric     // Remove trailing slash(es), being careful not to remove the root path
1953fe6060f1SDimitry Andric     StringRef Trimmed = Name;
1954480093f4SDimitry Andric     size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();
19550b57cec5SDimitry Andric     while (Trimmed.size() > RootPathLen &&
1956480093f4SDimitry Andric            sys::path::is_separator(Trimmed.back(), path_style))
19570b57cec5SDimitry Andric       Trimmed = Trimmed.slice(0, Trimmed.size() - 1);
1958480093f4SDimitry Andric 
19590b57cec5SDimitry Andric     // Get the last component
1960480093f4SDimitry Andric     StringRef LastComponent = sys::path::filename(Trimmed, path_style);
19610b57cec5SDimitry Andric 
19620b57cec5SDimitry Andric     std::unique_ptr<RedirectingFileSystem::Entry> Result;
19630b57cec5SDimitry Andric     switch (Kind) {
19640b57cec5SDimitry Andric     case RedirectingFileSystem::EK_File:
1965fe6060f1SDimitry Andric       Result = std::make_unique<RedirectingFileSystem::FileEntry>(
1966fe6060f1SDimitry Andric           LastComponent, std::move(ExternalContentsPath), UseExternalName);
1967fe6060f1SDimitry Andric       break;
1968fe6060f1SDimitry Andric     case RedirectingFileSystem::EK_DirectoryRemap:
1969fe6060f1SDimitry Andric       Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
19700b57cec5SDimitry Andric           LastComponent, std::move(ExternalContentsPath), UseExternalName);
19710b57cec5SDimitry Andric       break;
19720b57cec5SDimitry Andric     case RedirectingFileSystem::EK_Directory:
1973fe6060f1SDimitry Andric       Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
19740b57cec5SDimitry Andric           LastComponent, std::move(EntryArrayContents),
1975fe6060f1SDimitry Andric           Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1976fe6060f1SDimitry Andric                  0, 0, 0, file_type::directory_file, sys::fs::all_all));
19770b57cec5SDimitry Andric       break;
19780b57cec5SDimitry Andric     }
19790b57cec5SDimitry Andric 
1980480093f4SDimitry Andric     StringRef Parent = sys::path::parent_path(Trimmed, path_style);
19810b57cec5SDimitry Andric     if (Parent.empty())
19820b57cec5SDimitry Andric       return Result;
19830b57cec5SDimitry Andric 
19840b57cec5SDimitry Andric     // if 'name' contains multiple components, create implicit directory entries
1985480093f4SDimitry Andric     for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style),
19860b57cec5SDimitry Andric                                      E = sys::path::rend(Parent);
19870b57cec5SDimitry Andric          I != E; ++I) {
19880b57cec5SDimitry Andric       std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;
19890b57cec5SDimitry Andric       Entries.push_back(std::move(Result));
1990fe6060f1SDimitry Andric       Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
19910b57cec5SDimitry Andric           *I, std::move(Entries),
1992fe6060f1SDimitry Andric           Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1993fe6060f1SDimitry Andric                  0, 0, 0, file_type::directory_file, sys::fs::all_all));
19940b57cec5SDimitry Andric     }
19950b57cec5SDimitry Andric     return Result;
19960b57cec5SDimitry Andric   }
19970b57cec5SDimitry Andric 
19980b57cec5SDimitry Andric public:
RedirectingFileSystemParser(yaml::Stream & S)19990b57cec5SDimitry Andric   RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
20000b57cec5SDimitry Andric 
20010b57cec5SDimitry Andric   // false on error
parse(yaml::Node * Root,RedirectingFileSystem * FS)20020b57cec5SDimitry Andric   bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
20030b57cec5SDimitry Andric     auto *Top = dyn_cast<yaml::MappingNode>(Root);
20040b57cec5SDimitry Andric     if (!Top) {
20050b57cec5SDimitry Andric       error(Root, "expected mapping node");
20060b57cec5SDimitry Andric       return false;
20070b57cec5SDimitry Andric     }
20080b57cec5SDimitry Andric 
20090b57cec5SDimitry Andric     KeyStatusPair Fields[] = {
20100b57cec5SDimitry Andric         KeyStatusPair("version", true),
20110b57cec5SDimitry Andric         KeyStatusPair("case-sensitive", false),
20120b57cec5SDimitry Andric         KeyStatusPair("use-external-names", false),
2013bdd1243dSDimitry Andric         KeyStatusPair("root-relative", false),
20140b57cec5SDimitry Andric         KeyStatusPair("overlay-relative", false),
20150b57cec5SDimitry Andric         KeyStatusPair("fallthrough", false),
201681ad6265SDimitry Andric         KeyStatusPair("redirecting-with", false),
20170b57cec5SDimitry Andric         KeyStatusPair("roots", true),
20180b57cec5SDimitry Andric     };
20190b57cec5SDimitry Andric 
20200b57cec5SDimitry Andric     DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
20210b57cec5SDimitry Andric     std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries;
20220b57cec5SDimitry Andric 
20230b57cec5SDimitry Andric     // Parse configuration and 'roots'
20240b57cec5SDimitry Andric     for (auto &I : *Top) {
20250b57cec5SDimitry Andric       SmallString<10> KeyBuffer;
20260b57cec5SDimitry Andric       StringRef Key;
20270b57cec5SDimitry Andric       if (!parseScalarString(I.getKey(), Key, KeyBuffer))
20280b57cec5SDimitry Andric         return false;
20290b57cec5SDimitry Andric 
20300b57cec5SDimitry Andric       if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
20310b57cec5SDimitry Andric         return false;
20320b57cec5SDimitry Andric 
20330b57cec5SDimitry Andric       if (Key == "roots") {
20340b57cec5SDimitry Andric         auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue());
20350b57cec5SDimitry Andric         if (!Roots) {
20360b57cec5SDimitry Andric           error(I.getValue(), "expected array");
20370b57cec5SDimitry Andric           return false;
20380b57cec5SDimitry Andric         }
20390b57cec5SDimitry Andric 
20400b57cec5SDimitry Andric         for (auto &I : *Roots) {
20410b57cec5SDimitry Andric           if (std::unique_ptr<RedirectingFileSystem::Entry> E =
20420b57cec5SDimitry Andric                   parseEntry(&I, FS, /*IsRootEntry*/ true))
20430b57cec5SDimitry Andric             RootEntries.push_back(std::move(E));
20440b57cec5SDimitry Andric           else
20450b57cec5SDimitry Andric             return false;
20460b57cec5SDimitry Andric         }
20470b57cec5SDimitry Andric       } else if (Key == "version") {
20480b57cec5SDimitry Andric         StringRef VersionString;
20490b57cec5SDimitry Andric         SmallString<4> Storage;
20500b57cec5SDimitry Andric         if (!parseScalarString(I.getValue(), VersionString, Storage))
20510b57cec5SDimitry Andric           return false;
20520b57cec5SDimitry Andric         int Version;
20530b57cec5SDimitry Andric         if (VersionString.getAsInteger<int>(10, Version)) {
20540b57cec5SDimitry Andric           error(I.getValue(), "expected integer");
20550b57cec5SDimitry Andric           return false;
20560b57cec5SDimitry Andric         }
20570b57cec5SDimitry Andric         if (Version < 0) {
20580b57cec5SDimitry Andric           error(I.getValue(), "invalid version number");
20590b57cec5SDimitry Andric           return false;
20600b57cec5SDimitry Andric         }
20610b57cec5SDimitry Andric         if (Version != 0) {
20620b57cec5SDimitry Andric           error(I.getValue(), "version mismatch, expected 0");
20630b57cec5SDimitry Andric           return false;
20640b57cec5SDimitry Andric         }
20650b57cec5SDimitry Andric       } else if (Key == "case-sensitive") {
20660b57cec5SDimitry Andric         if (!parseScalarBool(I.getValue(), FS->CaseSensitive))
20670b57cec5SDimitry Andric           return false;
20680b57cec5SDimitry Andric       } else if (Key == "overlay-relative") {
20690b57cec5SDimitry Andric         if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay))
20700b57cec5SDimitry Andric           return false;
20710b57cec5SDimitry Andric       } else if (Key == "use-external-names") {
20720b57cec5SDimitry Andric         if (!parseScalarBool(I.getValue(), FS->UseExternalNames))
20730b57cec5SDimitry Andric           return false;
20740b57cec5SDimitry Andric       } else if (Key == "fallthrough") {
207581ad6265SDimitry Andric         if (Keys["redirecting-with"].Seen) {
207681ad6265SDimitry Andric           error(I.getValue(),
207781ad6265SDimitry Andric                 "'fallthrough' and 'redirecting-with' are mutually exclusive");
20780b57cec5SDimitry Andric           return false;
207981ad6265SDimitry Andric         }
208081ad6265SDimitry Andric 
208181ad6265SDimitry Andric         bool ShouldFallthrough = false;
208281ad6265SDimitry Andric         if (!parseScalarBool(I.getValue(), ShouldFallthrough))
208381ad6265SDimitry Andric           return false;
208481ad6265SDimitry Andric 
208581ad6265SDimitry Andric         if (ShouldFallthrough) {
208681ad6265SDimitry Andric           FS->Redirection = RedirectingFileSystem::RedirectKind::Fallthrough;
208781ad6265SDimitry Andric         } else {
208881ad6265SDimitry Andric           FS->Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly;
208981ad6265SDimitry Andric         }
209081ad6265SDimitry Andric       } else if (Key == "redirecting-with") {
209181ad6265SDimitry Andric         if (Keys["fallthrough"].Seen) {
209281ad6265SDimitry Andric           error(I.getValue(),
209381ad6265SDimitry Andric                 "'fallthrough' and 'redirecting-with' are mutually exclusive");
209481ad6265SDimitry Andric           return false;
209581ad6265SDimitry Andric         }
209681ad6265SDimitry Andric 
209781ad6265SDimitry Andric         if (auto Kind = parseRedirectKind(I.getValue())) {
209881ad6265SDimitry Andric           FS->Redirection = *Kind;
209981ad6265SDimitry Andric         } else {
210081ad6265SDimitry Andric           error(I.getValue(), "expected valid redirect kind");
210181ad6265SDimitry Andric           return false;
210281ad6265SDimitry Andric         }
2103bdd1243dSDimitry Andric       } else if (Key == "root-relative") {
2104bdd1243dSDimitry Andric         if (auto Kind = parseRootRelativeKind(I.getValue())) {
2105bdd1243dSDimitry Andric           FS->RootRelative = *Kind;
2106bdd1243dSDimitry Andric         } else {
2107bdd1243dSDimitry Andric           error(I.getValue(), "expected valid root-relative kind");
2108bdd1243dSDimitry Andric           return false;
2109bdd1243dSDimitry Andric         }
21100b57cec5SDimitry Andric       } else {
21110b57cec5SDimitry Andric         llvm_unreachable("key missing from Keys");
21120b57cec5SDimitry Andric       }
21130b57cec5SDimitry Andric     }
21140b57cec5SDimitry Andric 
21150b57cec5SDimitry Andric     if (Stream.failed())
21160b57cec5SDimitry Andric       return false;
21170b57cec5SDimitry Andric 
21180b57cec5SDimitry Andric     if (!checkMissingKeys(Top, Keys))
21190b57cec5SDimitry Andric       return false;
21200b57cec5SDimitry Andric 
21210b57cec5SDimitry Andric     // Now that we sucessefully parsed the YAML file, canonicalize the internal
21220b57cec5SDimitry Andric     // representation to a proper directory tree so that we can search faster
21230b57cec5SDimitry Andric     // inside the VFS.
21240b57cec5SDimitry Andric     for (auto &E : RootEntries)
21250b57cec5SDimitry Andric       uniqueOverlayTree(FS, E.get());
21260b57cec5SDimitry Andric 
21270b57cec5SDimitry Andric     return true;
21280b57cec5SDimitry Andric   }
21290b57cec5SDimitry Andric };
21300b57cec5SDimitry Andric 
2131e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem>
create(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)21320b57cec5SDimitry Andric RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
21330b57cec5SDimitry Andric                               SourceMgr::DiagHandlerTy DiagHandler,
21340b57cec5SDimitry Andric                               StringRef YAMLFilePath, void *DiagContext,
21350b57cec5SDimitry Andric                               IntrusiveRefCntPtr<FileSystem> ExternalFS) {
21360b57cec5SDimitry Andric   SourceMgr SM;
21370b57cec5SDimitry Andric   yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
21380b57cec5SDimitry Andric 
21390b57cec5SDimitry Andric   SM.setDiagHandler(DiagHandler, DiagContext);
21400b57cec5SDimitry Andric   yaml::document_iterator DI = Stream.begin();
21410b57cec5SDimitry Andric   yaml::Node *Root = DI->getRoot();
21420b57cec5SDimitry Andric   if (DI == Stream.end() || !Root) {
21430b57cec5SDimitry Andric     SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
21440b57cec5SDimitry Andric     return nullptr;
21450b57cec5SDimitry Andric   }
21460b57cec5SDimitry Andric 
21470b57cec5SDimitry Andric   RedirectingFileSystemParser P(Stream);
21480b57cec5SDimitry Andric 
21490b57cec5SDimitry Andric   std::unique_ptr<RedirectingFileSystem> FS(
21508bcb0991SDimitry Andric       new RedirectingFileSystem(ExternalFS));
21510b57cec5SDimitry Andric 
21520b57cec5SDimitry Andric   if (!YAMLFilePath.empty()) {
21530b57cec5SDimitry Andric     // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
21540b57cec5SDimitry Andric     // to each 'external-contents' path.
21550b57cec5SDimitry Andric     //
21560b57cec5SDimitry Andric     // Example:
21570b57cec5SDimitry Andric     //    -ivfsoverlay dummy.cache/vfs/vfs.yaml
21580b57cec5SDimitry Andric     // yields:
2159bdd1243dSDimitry Andric     //  FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs
21600b57cec5SDimitry Andric     //
21610b57cec5SDimitry Andric     SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
21620b57cec5SDimitry Andric     std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir);
21630b57cec5SDimitry Andric     assert(!EC && "Overlay dir final path must be absolute");
21640b57cec5SDimitry Andric     (void)EC;
2165bdd1243dSDimitry Andric     FS->setOverlayFileDir(OverlayAbsDir);
21660b57cec5SDimitry Andric   }
21670b57cec5SDimitry Andric 
21680b57cec5SDimitry Andric   if (!P.parse(Root, FS.get()))
21690b57cec5SDimitry Andric     return nullptr;
21700b57cec5SDimitry Andric 
2171e8d8bef9SDimitry Andric   return FS;
21720b57cec5SDimitry Andric }
21730b57cec5SDimitry Andric 
create(ArrayRef<std::pair<std::string,std::string>> RemappedFiles,bool UseExternalNames,FileSystem & ExternalFS)2174e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
2175e8d8bef9SDimitry Andric     ArrayRef<std::pair<std::string, std::string>> RemappedFiles,
2176e8d8bef9SDimitry Andric     bool UseExternalNames, FileSystem &ExternalFS) {
2177e8d8bef9SDimitry Andric   std::unique_ptr<RedirectingFileSystem> FS(
2178e8d8bef9SDimitry Andric       new RedirectingFileSystem(&ExternalFS));
2179e8d8bef9SDimitry Andric   FS->UseExternalNames = UseExternalNames;
21800b57cec5SDimitry Andric 
2181e8d8bef9SDimitry Andric   StringMap<RedirectingFileSystem::Entry *> Entries;
2182e8d8bef9SDimitry Andric 
2183e8d8bef9SDimitry Andric   for (auto &Mapping : llvm::reverse(RemappedFiles)) {
2184e8d8bef9SDimitry Andric     SmallString<128> From = StringRef(Mapping.first);
2185e8d8bef9SDimitry Andric     SmallString<128> To = StringRef(Mapping.second);
2186e8d8bef9SDimitry Andric     {
2187e8d8bef9SDimitry Andric       auto EC = ExternalFS.makeAbsolute(From);
2188e8d8bef9SDimitry Andric       (void)EC;
2189e8d8bef9SDimitry Andric       assert(!EC && "Could not make absolute path");
2190e8d8bef9SDimitry Andric     }
2191e8d8bef9SDimitry Andric 
2192e8d8bef9SDimitry Andric     // Check if we've already mapped this file. The first one we see (in the
2193e8d8bef9SDimitry Andric     // reverse iteration) wins.
2194e8d8bef9SDimitry Andric     RedirectingFileSystem::Entry *&ToEntry = Entries[From];
2195e8d8bef9SDimitry Andric     if (ToEntry)
2196e8d8bef9SDimitry Andric       continue;
2197e8d8bef9SDimitry Andric 
2198e8d8bef9SDimitry Andric     // Add parent directories.
2199e8d8bef9SDimitry Andric     RedirectingFileSystem::Entry *Parent = nullptr;
2200e8d8bef9SDimitry Andric     StringRef FromDirectory = llvm::sys::path::parent_path(From);
2201e8d8bef9SDimitry Andric     for (auto I = llvm::sys::path::begin(FromDirectory),
2202e8d8bef9SDimitry Andric               E = llvm::sys::path::end(FromDirectory);
2203e8d8bef9SDimitry Andric          I != E; ++I) {
2204e8d8bef9SDimitry Andric       Parent = RedirectingFileSystemParser::lookupOrCreateEntry(FS.get(), *I,
2205e8d8bef9SDimitry Andric                                                                 Parent);
2206e8d8bef9SDimitry Andric     }
2207e8d8bef9SDimitry Andric     assert(Parent && "File without a directory?");
2208e8d8bef9SDimitry Andric     {
2209e8d8bef9SDimitry Andric       auto EC = ExternalFS.makeAbsolute(To);
2210e8d8bef9SDimitry Andric       (void)EC;
2211e8d8bef9SDimitry Andric       assert(!EC && "Could not make absolute path");
2212e8d8bef9SDimitry Andric     }
2213e8d8bef9SDimitry Andric 
2214e8d8bef9SDimitry Andric     // Add the file.
2215fe6060f1SDimitry Andric     auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
2216e8d8bef9SDimitry Andric         llvm::sys::path::filename(From), To,
2217fe6060f1SDimitry Andric         UseExternalNames ? RedirectingFileSystem::NK_External
2218fe6060f1SDimitry Andric                          : RedirectingFileSystem::NK_Virtual);
2219e8d8bef9SDimitry Andric     ToEntry = NewFile.get();
2220fe6060f1SDimitry Andric     cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(
2221e8d8bef9SDimitry Andric         std::move(NewFile));
2222e8d8bef9SDimitry Andric   }
2223e8d8bef9SDimitry Andric 
2224e8d8bef9SDimitry Andric   return FS;
2225e8d8bef9SDimitry Andric }
2226e8d8bef9SDimitry Andric 
LookupResult(Entry * E,sys::path::const_iterator Start,sys::path::const_iterator End)2227fe6060f1SDimitry Andric RedirectingFileSystem::LookupResult::LookupResult(
2228fe6060f1SDimitry Andric     Entry *E, sys::path::const_iterator Start, sys::path::const_iterator End)
2229fe6060f1SDimitry Andric     : E(E) {
2230fe6060f1SDimitry Andric   assert(E != nullptr);
2231fe6060f1SDimitry Andric   // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
2232fe6060f1SDimitry Andric   // path of the directory it maps to in the external file system plus any
2233fe6060f1SDimitry Andric   // remaining path components in the provided iterator.
2234fe6060f1SDimitry Andric   if (auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(E)) {
2235fe6060f1SDimitry Andric     SmallString<256> Redirect(DRE->getExternalContentsPath());
2236fe6060f1SDimitry Andric     sys::path::append(Redirect, Start, End,
2237fe6060f1SDimitry Andric                       getExistingStyle(DRE->getExternalContentsPath()));
2238fe6060f1SDimitry Andric     ExternalRedirect = std::string(Redirect);
2239fe6060f1SDimitry Andric   }
2240fe6060f1SDimitry Andric }
2241fe6060f1SDimitry Andric 
getPath(llvm::SmallVectorImpl<char> & Result) const2242fe013be4SDimitry Andric void RedirectingFileSystem::LookupResult::getPath(
2243fe013be4SDimitry Andric     llvm::SmallVectorImpl<char> &Result) const {
2244fe013be4SDimitry Andric   Result.clear();
2245fe013be4SDimitry Andric   for (Entry *Parent : Parents)
2246fe013be4SDimitry Andric     llvm::sys::path::append(Result, Parent->getName());
2247fe013be4SDimitry Andric   llvm::sys::path::append(Result, E->getName());
2248fe013be4SDimitry Andric }
2249fe013be4SDimitry Andric 
2250e8d8bef9SDimitry Andric std::error_code
makeCanonical(SmallVectorImpl<char> & Path) const2251e8d8bef9SDimitry Andric RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const {
22520b57cec5SDimitry Andric   if (std::error_code EC = makeAbsolute(Path))
22530b57cec5SDimitry Andric     return EC;
22540b57cec5SDimitry Andric 
2255e8d8bef9SDimitry Andric   llvm::SmallString<256> CanonicalPath =
2256e8d8bef9SDimitry Andric       canonicalize(StringRef(Path.data(), Path.size()));
2257e8d8bef9SDimitry Andric   if (CanonicalPath.empty())
22580b57cec5SDimitry Andric     return make_error_code(llvm::errc::invalid_argument);
22590b57cec5SDimitry Andric 
2260e8d8bef9SDimitry Andric   Path.assign(CanonicalPath.begin(), CanonicalPath.end());
2261e8d8bef9SDimitry Andric   return {};
2262e8d8bef9SDimitry Andric }
2263e8d8bef9SDimitry Andric 
2264fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult>
lookupPath(StringRef Path) const2265e8d8bef9SDimitry Andric RedirectingFileSystem::lookupPath(StringRef Path) const {
22660b57cec5SDimitry Andric   sys::path::const_iterator Start = sys::path::begin(Path);
22670b57cec5SDimitry Andric   sys::path::const_iterator End = sys::path::end(Path);
2268fe013be4SDimitry Andric   llvm::SmallVector<Entry *, 32> Entries;
22690b57cec5SDimitry Andric   for (const auto &Root : Roots) {
2270fe6060f1SDimitry Andric     ErrorOr<RedirectingFileSystem::LookupResult> Result =
2271fe013be4SDimitry Andric         lookupPathImpl(Start, End, Root.get(), Entries);
2272fe013be4SDimitry Andric     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) {
2273fe013be4SDimitry Andric       Result->Parents = std::move(Entries);
22740b57cec5SDimitry Andric       return Result;
22750b57cec5SDimitry Andric     }
2276fe013be4SDimitry Andric   }
22770b57cec5SDimitry Andric   return make_error_code(llvm::errc::no_such_file_or_directory);
22780b57cec5SDimitry Andric }
22790b57cec5SDimitry Andric 
2280fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult>
lookupPathImpl(sys::path::const_iterator Start,sys::path::const_iterator End,RedirectingFileSystem::Entry * From,llvm::SmallVectorImpl<Entry * > & Entries) const2281fe6060f1SDimitry Andric RedirectingFileSystem::lookupPathImpl(
2282fe6060f1SDimitry Andric     sys::path::const_iterator Start, sys::path::const_iterator End,
2283fe013be4SDimitry Andric     RedirectingFileSystem::Entry *From,
2284fe013be4SDimitry Andric     llvm::SmallVectorImpl<Entry *> &Entries) const {
22850b57cec5SDimitry Andric   assert(!isTraversalComponent(*Start) &&
22860b57cec5SDimitry Andric          !isTraversalComponent(From->getName()) &&
22870b57cec5SDimitry Andric          "Paths should not contain traversal components");
22880b57cec5SDimitry Andric 
22890b57cec5SDimitry Andric   StringRef FromName = From->getName();
22900b57cec5SDimitry Andric 
22910b57cec5SDimitry Andric   // Forward the search to the next component in case this is an empty one.
22920b57cec5SDimitry Andric   if (!FromName.empty()) {
2293480093f4SDimitry Andric     if (!pathComponentMatches(*Start, FromName))
22940b57cec5SDimitry Andric       return make_error_code(llvm::errc::no_such_file_or_directory);
22950b57cec5SDimitry Andric 
22960b57cec5SDimitry Andric     ++Start;
22970b57cec5SDimitry Andric 
22980b57cec5SDimitry Andric     if (Start == End) {
22990b57cec5SDimitry Andric       // Match!
2300fe6060f1SDimitry Andric       return LookupResult(From, Start, End);
23010b57cec5SDimitry Andric     }
23020b57cec5SDimitry Andric   }
23030b57cec5SDimitry Andric 
2304fe6060f1SDimitry Andric   if (isa<RedirectingFileSystem::FileEntry>(From))
23050b57cec5SDimitry Andric     return make_error_code(llvm::errc::not_a_directory);
23060b57cec5SDimitry Andric 
2307fe6060f1SDimitry Andric   if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From))
2308fe6060f1SDimitry Andric     return LookupResult(From, Start, End);
2309fe6060f1SDimitry Andric 
2310fe6060f1SDimitry Andric   auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From);
23110b57cec5SDimitry Andric   for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
23120b57cec5SDimitry Andric        llvm::make_range(DE->contents_begin(), DE->contents_end())) {
2313fe013be4SDimitry Andric     Entries.push_back(From);
2314fe6060f1SDimitry Andric     ErrorOr<RedirectingFileSystem::LookupResult> Result =
2315fe013be4SDimitry Andric         lookupPathImpl(Start, End, DirEntry.get(), Entries);
23160b57cec5SDimitry Andric     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
23170b57cec5SDimitry Andric       return Result;
2318fe013be4SDimitry Andric     Entries.pop_back();
23190b57cec5SDimitry Andric   }
2320480093f4SDimitry Andric 
23210b57cec5SDimitry Andric   return make_error_code(llvm::errc::no_such_file_or_directory);
23220b57cec5SDimitry Andric }
23230b57cec5SDimitry Andric 
getRedirectedFileStatus(const Twine & OriginalPath,bool UseExternalNames,Status ExternalStatus)2324349cc55cSDimitry Andric static Status getRedirectedFileStatus(const Twine &OriginalPath,
2325349cc55cSDimitry Andric                                       bool UseExternalNames,
23260b57cec5SDimitry Andric                                       Status ExternalStatus) {
232781ad6265SDimitry Andric   // The path has been mapped by some nested VFS and exposes an external path,
232881ad6265SDimitry Andric   // don't override it with the original path.
232981ad6265SDimitry Andric   if (ExternalStatus.ExposesExternalVFSPath)
233081ad6265SDimitry Andric     return ExternalStatus;
233181ad6265SDimitry Andric 
23320b57cec5SDimitry Andric   Status S = ExternalStatus;
23330b57cec5SDimitry Andric   if (!UseExternalNames)
2334349cc55cSDimitry Andric     S = Status::copyWithNewName(S, OriginalPath);
233581ad6265SDimitry Andric   else
233681ad6265SDimitry Andric     S.ExposesExternalVFSPath = true;
23370b57cec5SDimitry Andric   S.IsVFSMapped = true;
23380b57cec5SDimitry Andric   return S;
23390b57cec5SDimitry Andric }
23400b57cec5SDimitry Andric 
status(const Twine & CanonicalPath,const Twine & OriginalPath,const RedirectingFileSystem::LookupResult & Result)2341fe6060f1SDimitry Andric ErrorOr<Status> RedirectingFileSystem::status(
2342349cc55cSDimitry Andric     const Twine &CanonicalPath, const Twine &OriginalPath,
2343349cc55cSDimitry Andric     const RedirectingFileSystem::LookupResult &Result) {
2344bdd1243dSDimitry Andric   if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
2345349cc55cSDimitry Andric     SmallString<256> CanonicalRemappedPath((*ExtRedirect).str());
2346349cc55cSDimitry Andric     if (std::error_code EC = makeCanonical(CanonicalRemappedPath))
2347349cc55cSDimitry Andric       return EC;
2348349cc55cSDimitry Andric 
2349349cc55cSDimitry Andric     ErrorOr<Status> S = ExternalFS->status(CanonicalRemappedPath);
2350fe6060f1SDimitry Andric     if (!S)
23510b57cec5SDimitry Andric       return S;
2352349cc55cSDimitry Andric     S = Status::copyWithNewName(*S, *ExtRedirect);
2353fe6060f1SDimitry Andric     auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E);
2354349cc55cSDimitry Andric     return getRedirectedFileStatus(OriginalPath,
2355349cc55cSDimitry Andric                                    RE->useExternalName(UseExternalNames), *S);
23560b57cec5SDimitry Andric   }
2357fe6060f1SDimitry Andric 
2358fe6060f1SDimitry Andric   auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
2359349cc55cSDimitry Andric   return Status::copyWithNewName(DE->getStatus(), CanonicalPath);
23600b57cec5SDimitry Andric }
23610b57cec5SDimitry Andric 
2362349cc55cSDimitry Andric ErrorOr<Status>
getExternalStatus(const Twine & CanonicalPath,const Twine & OriginalPath) const2363349cc55cSDimitry Andric RedirectingFileSystem::getExternalStatus(const Twine &CanonicalPath,
2364349cc55cSDimitry Andric                                          const Twine &OriginalPath) const {
236581ad6265SDimitry Andric   auto Result = ExternalFS->status(CanonicalPath);
236681ad6265SDimitry Andric 
236781ad6265SDimitry Andric   // The path has been mapped by some nested VFS, don't override it with the
236881ad6265SDimitry Andric   // original path.
236981ad6265SDimitry Andric   if (!Result || Result->ExposesExternalVFSPath)
237081ad6265SDimitry Andric     return Result;
237181ad6265SDimitry Andric   return Status::copyWithNewName(Result.get(), OriginalPath);
2372349cc55cSDimitry Andric }
2373e8d8bef9SDimitry Andric 
status(const Twine & OriginalPath)2374349cc55cSDimitry Andric ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {
2375349cc55cSDimitry Andric   SmallString<256> CanonicalPath;
2376349cc55cSDimitry Andric   OriginalPath.toVector(CanonicalPath);
2377349cc55cSDimitry Andric 
2378349cc55cSDimitry Andric   if (std::error_code EC = makeCanonical(CanonicalPath))
2379e8d8bef9SDimitry Andric     return EC;
2380e8d8bef9SDimitry Andric 
238181ad6265SDimitry Andric   if (Redirection == RedirectKind::Fallback) {
238281ad6265SDimitry Andric     // Attempt to find the original file first, only falling back to the
238381ad6265SDimitry Andric     // mapped file if that fails.
238481ad6265SDimitry Andric     ErrorOr<Status> S = getExternalStatus(CanonicalPath, OriginalPath);
238581ad6265SDimitry Andric     if (S)
238681ad6265SDimitry Andric       return S;
238781ad6265SDimitry Andric   }
238881ad6265SDimitry Andric 
2389349cc55cSDimitry Andric   ErrorOr<RedirectingFileSystem::LookupResult> Result =
2390349cc55cSDimitry Andric       lookupPath(CanonicalPath);
23910b57cec5SDimitry Andric   if (!Result) {
239281ad6265SDimitry Andric     // Was not able to map file, fallthrough to using the original path if
239381ad6265SDimitry Andric     // that was the specified redirection type.
239481ad6265SDimitry Andric     if (Redirection == RedirectKind::Fallthrough &&
239581ad6265SDimitry Andric         isFileNotFound(Result.getError()))
2396349cc55cSDimitry Andric       return getExternalStatus(CanonicalPath, OriginalPath);
23970b57cec5SDimitry Andric     return Result.getError();
23980b57cec5SDimitry Andric   }
2399fe6060f1SDimitry Andric 
2400349cc55cSDimitry Andric   ErrorOr<Status> S = status(CanonicalPath, OriginalPath, *Result);
240181ad6265SDimitry Andric   if (!S && Redirection == RedirectKind::Fallthrough &&
240281ad6265SDimitry Andric       isFileNotFound(S.getError(), Result->E)) {
240381ad6265SDimitry Andric     // Mapped the file but it wasn't found in the underlying filesystem,
240481ad6265SDimitry Andric     // fallthrough to using the original path if that was the specified
240581ad6265SDimitry Andric     // redirection type.
2406349cc55cSDimitry Andric     return getExternalStatus(CanonicalPath, OriginalPath);
2407349cc55cSDimitry Andric   }
2408349cc55cSDimitry Andric 
2409fe6060f1SDimitry Andric   return S;
24100b57cec5SDimitry Andric }
24110b57cec5SDimitry Andric 
24120b57cec5SDimitry Andric namespace {
24130b57cec5SDimitry Andric 
24140b57cec5SDimitry Andric /// Provide a file wrapper with an overriden status.
24150b57cec5SDimitry Andric class FileWithFixedStatus : public File {
24160b57cec5SDimitry Andric   std::unique_ptr<File> InnerFile;
24170b57cec5SDimitry Andric   Status S;
24180b57cec5SDimitry Andric 
24190b57cec5SDimitry Andric public:
FileWithFixedStatus(std::unique_ptr<File> InnerFile,Status S)24200b57cec5SDimitry Andric   FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
24210b57cec5SDimitry Andric       : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
24220b57cec5SDimitry Andric 
status()24230b57cec5SDimitry Andric   ErrorOr<Status> status() override { return S; }
24240b57cec5SDimitry Andric   ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
24250b57cec5SDimitry Andric 
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)24260b57cec5SDimitry Andric   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
24270b57cec5SDimitry Andric             bool IsVolatile) override {
24280b57cec5SDimitry Andric     return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
24290b57cec5SDimitry Andric                                 IsVolatile);
24300b57cec5SDimitry Andric   }
24310b57cec5SDimitry Andric 
close()24320b57cec5SDimitry Andric   std::error_code close() override { return InnerFile->close(); }
2433349cc55cSDimitry Andric 
setPath(const Twine & Path)2434349cc55cSDimitry Andric   void setPath(const Twine &Path) override { S = S.copyWithNewName(S, Path); }
24350b57cec5SDimitry Andric };
24360b57cec5SDimitry Andric 
24370b57cec5SDimitry Andric } // namespace
24380b57cec5SDimitry Andric 
24390b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
getWithPath(ErrorOr<std::unique_ptr<File>> Result,const Twine & P)2440349cc55cSDimitry Andric File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
244181ad6265SDimitry Andric   // See \c getRedirectedFileStatus - don't update path if it's exposing an
244281ad6265SDimitry Andric   // external path.
244381ad6265SDimitry Andric   if (!Result || (*Result)->status()->ExposesExternalVFSPath)
2444349cc55cSDimitry Andric     return Result;
2445e8d8bef9SDimitry Andric 
2446349cc55cSDimitry Andric   ErrorOr<std::unique_ptr<File>> F = std::move(*Result);
2447349cc55cSDimitry Andric   auto Name = F->get()->getName();
2448349cc55cSDimitry Andric   if (Name && Name.get() != P.str())
2449349cc55cSDimitry Andric     F->get()->setPath(P);
2450349cc55cSDimitry Andric   return F;
2451349cc55cSDimitry Andric }
2452349cc55cSDimitry Andric 
2453349cc55cSDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & OriginalPath)2454349cc55cSDimitry Andric RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) {
2455349cc55cSDimitry Andric   SmallString<256> CanonicalPath;
2456349cc55cSDimitry Andric   OriginalPath.toVector(CanonicalPath);
2457349cc55cSDimitry Andric 
2458349cc55cSDimitry Andric   if (std::error_code EC = makeCanonical(CanonicalPath))
2459e8d8bef9SDimitry Andric     return EC;
2460e8d8bef9SDimitry Andric 
246181ad6265SDimitry Andric   if (Redirection == RedirectKind::Fallback) {
246281ad6265SDimitry Andric     // Attempt to find the original file first, only falling back to the
246381ad6265SDimitry Andric     // mapped file if that fails.
246481ad6265SDimitry Andric     auto F = File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
246581ad6265SDimitry Andric                                OriginalPath);
246681ad6265SDimitry Andric     if (F)
246781ad6265SDimitry Andric       return F;
246881ad6265SDimitry Andric   }
246981ad6265SDimitry Andric 
2470349cc55cSDimitry Andric   ErrorOr<RedirectingFileSystem::LookupResult> Result =
2471349cc55cSDimitry Andric       lookupPath(CanonicalPath);
2472fe6060f1SDimitry Andric   if (!Result) {
247381ad6265SDimitry Andric     // Was not able to map file, fallthrough to using the original path if
247481ad6265SDimitry Andric     // that was the specified redirection type.
247581ad6265SDimitry Andric     if (Redirection == RedirectKind::Fallthrough &&
247681ad6265SDimitry Andric         isFileNotFound(Result.getError()))
2477349cc55cSDimitry Andric       return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2478349cc55cSDimitry Andric                                OriginalPath);
2479fe6060f1SDimitry Andric     return Result.getError();
24800b57cec5SDimitry Andric   }
24810b57cec5SDimitry Andric 
2482fe6060f1SDimitry Andric   if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
24830b57cec5SDimitry Andric     return make_error_code(llvm::errc::invalid_argument);
24840b57cec5SDimitry Andric 
2485fe6060f1SDimitry Andric   StringRef ExtRedirect = *Result->getExternalRedirect();
2486349cc55cSDimitry Andric   SmallString<256> CanonicalRemappedPath(ExtRedirect.str());
2487349cc55cSDimitry Andric   if (std::error_code EC = makeCanonical(CanonicalRemappedPath))
2488349cc55cSDimitry Andric     return EC;
2489349cc55cSDimitry Andric 
2490fe6060f1SDimitry Andric   auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
24910b57cec5SDimitry Andric 
2492349cc55cSDimitry Andric   auto ExternalFile = File::getWithPath(
2493349cc55cSDimitry Andric       ExternalFS->openFileForRead(CanonicalRemappedPath), ExtRedirect);
2494fe6060f1SDimitry Andric   if (!ExternalFile) {
249581ad6265SDimitry Andric     if (Redirection == RedirectKind::Fallthrough &&
249681ad6265SDimitry Andric         isFileNotFound(ExternalFile.getError(), Result->E)) {
249781ad6265SDimitry Andric       // Mapped the file but it wasn't found in the underlying filesystem,
249881ad6265SDimitry Andric       // fallthrough to using the original path if that was the specified
249981ad6265SDimitry Andric       // redirection type.
2500349cc55cSDimitry Andric       return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2501349cc55cSDimitry Andric                                OriginalPath);
250281ad6265SDimitry Andric     }
2503fe6060f1SDimitry Andric     return ExternalFile;
2504fe6060f1SDimitry Andric   }
2505fe6060f1SDimitry Andric 
2506fe6060f1SDimitry Andric   auto ExternalStatus = (*ExternalFile)->status();
25070b57cec5SDimitry Andric   if (!ExternalStatus)
25080b57cec5SDimitry Andric     return ExternalStatus.getError();
25090b57cec5SDimitry Andric 
251081ad6265SDimitry Andric   // Otherwise, the file was successfully remapped. Mark it as such. Also
251181ad6265SDimitry Andric   // replace the underlying path if the external name is being used.
2512fe6060f1SDimitry Andric   Status S = getRedirectedFileStatus(
2513349cc55cSDimitry Andric       OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus);
25140b57cec5SDimitry Andric   return std::unique_ptr<File>(
2515fe6060f1SDimitry Andric       std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
25160b57cec5SDimitry Andric }
25170b57cec5SDimitry Andric 
25180b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & OriginalPath,SmallVectorImpl<char> & Output) const251981ad6265SDimitry Andric RedirectingFileSystem::getRealPath(const Twine &OriginalPath,
25200b57cec5SDimitry Andric                                    SmallVectorImpl<char> &Output) const {
252181ad6265SDimitry Andric   SmallString<256> CanonicalPath;
252281ad6265SDimitry Andric   OriginalPath.toVector(CanonicalPath);
2523e8d8bef9SDimitry Andric 
252481ad6265SDimitry Andric   if (std::error_code EC = makeCanonical(CanonicalPath))
2525e8d8bef9SDimitry Andric     return EC;
2526e8d8bef9SDimitry Andric 
252781ad6265SDimitry Andric   if (Redirection == RedirectKind::Fallback) {
252881ad6265SDimitry Andric     // Attempt to find the original file first, only falling back to the
252981ad6265SDimitry Andric     // mapped file if that fails.
253081ad6265SDimitry Andric     std::error_code EC = ExternalFS->getRealPath(CanonicalPath, Output);
253181ad6265SDimitry Andric     if (!EC)
253281ad6265SDimitry Andric       return EC;
253381ad6265SDimitry Andric   }
253481ad6265SDimitry Andric 
253581ad6265SDimitry Andric   ErrorOr<RedirectingFileSystem::LookupResult> Result =
253681ad6265SDimitry Andric       lookupPath(CanonicalPath);
25370b57cec5SDimitry Andric   if (!Result) {
253881ad6265SDimitry Andric     // Was not able to map file, fallthrough to using the original path if
253981ad6265SDimitry Andric     // that was the specified redirection type.
254081ad6265SDimitry Andric     if (Redirection == RedirectKind::Fallthrough &&
254181ad6265SDimitry Andric         isFileNotFound(Result.getError()))
254281ad6265SDimitry Andric       return ExternalFS->getRealPath(CanonicalPath, Output);
25430b57cec5SDimitry Andric     return Result.getError();
25440b57cec5SDimitry Andric   }
25450b57cec5SDimitry Andric 
2546fe6060f1SDimitry Andric   // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2547fe6060f1SDimitry Andric   // path in the external file system.
2548fe6060f1SDimitry Andric   if (auto ExtRedirect = Result->getExternalRedirect()) {
2549fe6060f1SDimitry Andric     auto P = ExternalFS->getRealPath(*ExtRedirect, Output);
255081ad6265SDimitry Andric     if (P && Redirection == RedirectKind::Fallthrough &&
255181ad6265SDimitry Andric         isFileNotFound(P, Result->E)) {
255281ad6265SDimitry Andric       // Mapped the file but it wasn't found in the underlying filesystem,
255381ad6265SDimitry Andric       // fallthrough to using the original path if that was the specified
255481ad6265SDimitry Andric       // redirection type.
255581ad6265SDimitry Andric       return ExternalFS->getRealPath(CanonicalPath, Output);
25560b57cec5SDimitry Andric     }
2557fe6060f1SDimitry Andric     return P;
2558fe6060f1SDimitry Andric   }
2559fe6060f1SDimitry Andric 
2560fe013be4SDimitry Andric   // We found a DirectoryEntry, which does not have a single external contents
2561fe013be4SDimitry Andric   // path. Use the canonical virtual path.
2562fe013be4SDimitry Andric   if (Redirection == RedirectKind::Fallthrough) {
2563fe013be4SDimitry Andric     Result->getPath(Output);
2564fe013be4SDimitry Andric     return {};
2565fe013be4SDimitry Andric   }
256681ad6265SDimitry Andric   return llvm::errc::invalid_argument;
25670b57cec5SDimitry Andric }
25680b57cec5SDimitry Andric 
2569e8d8bef9SDimitry Andric std::unique_ptr<FileSystem>
getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)25700b57cec5SDimitry Andric vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
25710b57cec5SDimitry Andric                     SourceMgr::DiagHandlerTy DiagHandler,
25720b57cec5SDimitry Andric                     StringRef YAMLFilePath, void *DiagContext,
25730b57cec5SDimitry Andric                     IntrusiveRefCntPtr<FileSystem> ExternalFS) {
25740b57cec5SDimitry Andric   return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
25750b57cec5SDimitry Andric                                        YAMLFilePath, DiagContext,
25760b57cec5SDimitry Andric                                        std::move(ExternalFS));
25770b57cec5SDimitry Andric }
25780b57cec5SDimitry Andric 
getVFSEntries(RedirectingFileSystem::Entry * SrcE,SmallVectorImpl<StringRef> & Path,SmallVectorImpl<YAMLVFSEntry> & Entries)25790b57cec5SDimitry Andric static void getVFSEntries(RedirectingFileSystem::Entry *SrcE,
25800b57cec5SDimitry Andric                           SmallVectorImpl<StringRef> &Path,
25810b57cec5SDimitry Andric                           SmallVectorImpl<YAMLVFSEntry> &Entries) {
25820b57cec5SDimitry Andric   auto Kind = SrcE->getKind();
25830b57cec5SDimitry Andric   if (Kind == RedirectingFileSystem::EK_Directory) {
2584fe6060f1SDimitry Andric     auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
25850b57cec5SDimitry Andric     assert(DE && "Must be a directory");
25860b57cec5SDimitry Andric     for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
25870b57cec5SDimitry Andric          llvm::make_range(DE->contents_begin(), DE->contents_end())) {
25880b57cec5SDimitry Andric       Path.push_back(SubEntry->getName());
25890b57cec5SDimitry Andric       getVFSEntries(SubEntry.get(), Path, Entries);
25900b57cec5SDimitry Andric       Path.pop_back();
25910b57cec5SDimitry Andric     }
25920b57cec5SDimitry Andric     return;
25930b57cec5SDimitry Andric   }
25940b57cec5SDimitry Andric 
2595fe6060f1SDimitry Andric   if (Kind == RedirectingFileSystem::EK_DirectoryRemap) {
2596fe6060f1SDimitry Andric     auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
2597fe6060f1SDimitry Andric     assert(DR && "Must be a directory remap");
2598fe6060f1SDimitry Andric     SmallString<128> VPath;
2599fe6060f1SDimitry Andric     for (auto &Comp : Path)
2600fe6060f1SDimitry Andric       llvm::sys::path::append(VPath, Comp);
2601fe6060f1SDimitry Andric     Entries.push_back(
2602fe6060f1SDimitry Andric         YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
2603fe6060f1SDimitry Andric     return;
2604fe6060f1SDimitry Andric   }
2605fe6060f1SDimitry Andric 
26060b57cec5SDimitry Andric   assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
2607fe6060f1SDimitry Andric   auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
26080b57cec5SDimitry Andric   assert(FE && "Must be a file");
26090b57cec5SDimitry Andric   SmallString<128> VPath;
26100b57cec5SDimitry Andric   for (auto &Comp : Path)
26110b57cec5SDimitry Andric     llvm::sys::path::append(VPath, Comp);
26120b57cec5SDimitry Andric   Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
26130b57cec5SDimitry Andric }
26140b57cec5SDimitry Andric 
collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,SmallVectorImpl<YAMLVFSEntry> & CollectedEntries,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)26150b57cec5SDimitry Andric void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
26160b57cec5SDimitry Andric                              SourceMgr::DiagHandlerTy DiagHandler,
26170b57cec5SDimitry Andric                              StringRef YAMLFilePath,
26180b57cec5SDimitry Andric                              SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
26190b57cec5SDimitry Andric                              void *DiagContext,
26200b57cec5SDimitry Andric                              IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2621e8d8bef9SDimitry Andric   std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create(
26220b57cec5SDimitry Andric       std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
26230b57cec5SDimitry Andric       std::move(ExternalFS));
2624fe6060f1SDimitry Andric   if (!VFS)
2625fe6060f1SDimitry Andric     return;
2626fe6060f1SDimitry Andric   ErrorOr<RedirectingFileSystem::LookupResult> RootResult =
2627fe6060f1SDimitry Andric       VFS->lookupPath("/");
2628fe6060f1SDimitry Andric   if (!RootResult)
26290b57cec5SDimitry Andric     return;
26300b57cec5SDimitry Andric   SmallVector<StringRef, 8> Components;
26310b57cec5SDimitry Andric   Components.push_back("/");
2632fe6060f1SDimitry Andric   getVFSEntries(RootResult->E, Components, CollectedEntries);
26330b57cec5SDimitry Andric }
26340b57cec5SDimitry Andric 
getNextVirtualUniqueID()26350b57cec5SDimitry Andric UniqueID vfs::getNextVirtualUniqueID() {
26360b57cec5SDimitry Andric   static std::atomic<unsigned> UID;
26370b57cec5SDimitry Andric   unsigned ID = ++UID;
26380b57cec5SDimitry Andric   // The following assumes that uint64_t max will never collide with a real
26390b57cec5SDimitry Andric   // dev_t value from the OS.
26400b57cec5SDimitry Andric   return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
26410b57cec5SDimitry Andric }
26420b57cec5SDimitry Andric 
addEntry(StringRef VirtualPath,StringRef RealPath,bool IsDirectory)26435ffd83dbSDimitry Andric void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath,
26445ffd83dbSDimitry Andric                              bool IsDirectory) {
26450b57cec5SDimitry Andric   assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
26460b57cec5SDimitry Andric   assert(sys::path::is_absolute(RealPath) && "real path not absolute");
26470b57cec5SDimitry Andric   assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
26485ffd83dbSDimitry Andric   Mappings.emplace_back(VirtualPath, RealPath, IsDirectory);
26495ffd83dbSDimitry Andric }
26505ffd83dbSDimitry Andric 
addFileMapping(StringRef VirtualPath,StringRef RealPath)26515ffd83dbSDimitry Andric void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
26525ffd83dbSDimitry Andric   addEntry(VirtualPath, RealPath, /*IsDirectory=*/false);
26535ffd83dbSDimitry Andric }
26545ffd83dbSDimitry Andric 
addDirectoryMapping(StringRef VirtualPath,StringRef RealPath)26555ffd83dbSDimitry Andric void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath,
26565ffd83dbSDimitry Andric                                         StringRef RealPath) {
26575ffd83dbSDimitry Andric   addEntry(VirtualPath, RealPath, /*IsDirectory=*/true);
26580b57cec5SDimitry Andric }
26590b57cec5SDimitry Andric 
26600b57cec5SDimitry Andric namespace {
26610b57cec5SDimitry Andric 
26620b57cec5SDimitry Andric class JSONWriter {
26630b57cec5SDimitry Andric   llvm::raw_ostream &OS;
26640b57cec5SDimitry Andric   SmallVector<StringRef, 16> DirStack;
26650b57cec5SDimitry Andric 
getDirIndent()26660b57cec5SDimitry Andric   unsigned getDirIndent() { return 4 * DirStack.size(); }
getFileIndent()26670b57cec5SDimitry Andric   unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
26680b57cec5SDimitry Andric   bool containedIn(StringRef Parent, StringRef Path);
26690b57cec5SDimitry Andric   StringRef containedPart(StringRef Parent, StringRef Path);
26700b57cec5SDimitry Andric   void startDirectory(StringRef Path);
26710b57cec5SDimitry Andric   void endDirectory();
26720b57cec5SDimitry Andric   void writeEntry(StringRef VPath, StringRef RPath);
26730b57cec5SDimitry Andric 
26740b57cec5SDimitry Andric public:
JSONWriter(llvm::raw_ostream & OS)26750b57cec5SDimitry Andric   JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
26760b57cec5SDimitry Andric 
2677bdd1243dSDimitry Andric   void write(ArrayRef<YAMLVFSEntry> Entries,
2678bdd1243dSDimitry Andric              std::optional<bool> UseExternalNames,
2679bdd1243dSDimitry Andric              std::optional<bool> IsCaseSensitive,
2680bdd1243dSDimitry Andric              std::optional<bool> IsOverlayRelative, StringRef OverlayDir);
26810b57cec5SDimitry Andric };
26820b57cec5SDimitry Andric 
26830b57cec5SDimitry Andric } // namespace
26840b57cec5SDimitry Andric 
containedIn(StringRef Parent,StringRef Path)26850b57cec5SDimitry Andric bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
26860b57cec5SDimitry Andric   using namespace llvm::sys;
26870b57cec5SDimitry Andric 
26880b57cec5SDimitry Andric   // Compare each path component.
26890b57cec5SDimitry Andric   auto IParent = path::begin(Parent), EParent = path::end(Parent);
26900b57cec5SDimitry Andric   for (auto IChild = path::begin(Path), EChild = path::end(Path);
26910b57cec5SDimitry Andric        IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
26920b57cec5SDimitry Andric     if (*IParent != *IChild)
26930b57cec5SDimitry Andric       return false;
26940b57cec5SDimitry Andric   }
26950b57cec5SDimitry Andric   // Have we exhausted the parent path?
26960b57cec5SDimitry Andric   return IParent == EParent;
26970b57cec5SDimitry Andric }
26980b57cec5SDimitry Andric 
containedPart(StringRef Parent,StringRef Path)26990b57cec5SDimitry Andric StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
27000b57cec5SDimitry Andric   assert(!Parent.empty());
27010b57cec5SDimitry Andric   assert(containedIn(Parent, Path));
27020b57cec5SDimitry Andric   return Path.slice(Parent.size() + 1, StringRef::npos);
27030b57cec5SDimitry Andric }
27040b57cec5SDimitry Andric 
startDirectory(StringRef Path)27050b57cec5SDimitry Andric void JSONWriter::startDirectory(StringRef Path) {
27060b57cec5SDimitry Andric   StringRef Name =
27070b57cec5SDimitry Andric       DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
27080b57cec5SDimitry Andric   DirStack.push_back(Path);
27090b57cec5SDimitry Andric   unsigned Indent = getDirIndent();
27100b57cec5SDimitry Andric   OS.indent(Indent) << "{\n";
27110b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'type': 'directory',\n";
27120b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
27130b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'contents': [\n";
27140b57cec5SDimitry Andric }
27150b57cec5SDimitry Andric 
endDirectory()27160b57cec5SDimitry Andric void JSONWriter::endDirectory() {
27170b57cec5SDimitry Andric   unsigned Indent = getDirIndent();
27180b57cec5SDimitry Andric   OS.indent(Indent + 2) << "]\n";
27190b57cec5SDimitry Andric   OS.indent(Indent) << "}";
27200b57cec5SDimitry Andric 
27210b57cec5SDimitry Andric   DirStack.pop_back();
27220b57cec5SDimitry Andric }
27230b57cec5SDimitry Andric 
writeEntry(StringRef VPath,StringRef RPath)27240b57cec5SDimitry Andric void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
27250b57cec5SDimitry Andric   unsigned Indent = getFileIndent();
27260b57cec5SDimitry Andric   OS.indent(Indent) << "{\n";
27270b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'type': 'file',\n";
27280b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
27290b57cec5SDimitry Andric   OS.indent(Indent + 2) << "'external-contents': \""
27300b57cec5SDimitry Andric                         << llvm::yaml::escape(RPath) << "\"\n";
27310b57cec5SDimitry Andric   OS.indent(Indent) << "}";
27320b57cec5SDimitry Andric }
27330b57cec5SDimitry Andric 
write(ArrayRef<YAMLVFSEntry> Entries,std::optional<bool> UseExternalNames,std::optional<bool> IsCaseSensitive,std::optional<bool> IsOverlayRelative,StringRef OverlayDir)27340b57cec5SDimitry Andric void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
2735bdd1243dSDimitry Andric                        std::optional<bool> UseExternalNames,
2736bdd1243dSDimitry Andric                        std::optional<bool> IsCaseSensitive,
2737bdd1243dSDimitry Andric                        std::optional<bool> IsOverlayRelative,
27380b57cec5SDimitry Andric                        StringRef OverlayDir) {
27390b57cec5SDimitry Andric   using namespace llvm::sys;
27400b57cec5SDimitry Andric 
27410b57cec5SDimitry Andric   OS << "{\n"
27420b57cec5SDimitry Andric         "  'version': 0,\n";
274381ad6265SDimitry Andric   if (IsCaseSensitive)
2744bdd1243dSDimitry Andric     OS << "  'case-sensitive': '" << (*IsCaseSensitive ? "true" : "false")
2745bdd1243dSDimitry Andric        << "',\n";
274681ad6265SDimitry Andric   if (UseExternalNames)
2747bdd1243dSDimitry Andric     OS << "  'use-external-names': '" << (*UseExternalNames ? "true" : "false")
2748bdd1243dSDimitry Andric        << "',\n";
27490b57cec5SDimitry Andric   bool UseOverlayRelative = false;
275081ad6265SDimitry Andric   if (IsOverlayRelative) {
2751bdd1243dSDimitry Andric     UseOverlayRelative = *IsOverlayRelative;
27520b57cec5SDimitry Andric     OS << "  'overlay-relative': '" << (UseOverlayRelative ? "true" : "false")
27530b57cec5SDimitry Andric        << "',\n";
27540b57cec5SDimitry Andric   }
27550b57cec5SDimitry Andric   OS << "  'roots': [\n";
27560b57cec5SDimitry Andric 
27570b57cec5SDimitry Andric   if (!Entries.empty()) {
27580b57cec5SDimitry Andric     const YAMLVFSEntry &Entry = Entries.front();
27595ffd83dbSDimitry Andric 
27605ffd83dbSDimitry Andric     startDirectory(
27615ffd83dbSDimitry Andric       Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath)
27625ffd83dbSDimitry Andric     );
27630b57cec5SDimitry Andric 
27640b57cec5SDimitry Andric     StringRef RPath = Entry.RPath;
27650b57cec5SDimitry Andric     if (UseOverlayRelative) {
27660b57cec5SDimitry Andric       unsigned OverlayDirLen = OverlayDir.size();
27670b57cec5SDimitry Andric       assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
27680b57cec5SDimitry Andric              "Overlay dir must be contained in RPath");
27690b57cec5SDimitry Andric       RPath = RPath.slice(OverlayDirLen, RPath.size());
27700b57cec5SDimitry Andric     }
27710b57cec5SDimitry Andric 
27725ffd83dbSDimitry Andric     bool IsCurrentDirEmpty = true;
27735ffd83dbSDimitry Andric     if (!Entry.IsDirectory) {
27740b57cec5SDimitry Andric       writeEntry(path::filename(Entry.VPath), RPath);
27755ffd83dbSDimitry Andric       IsCurrentDirEmpty = false;
27765ffd83dbSDimitry Andric     }
27770b57cec5SDimitry Andric 
27780b57cec5SDimitry Andric     for (const auto &Entry : Entries.slice(1)) {
27795ffd83dbSDimitry Andric       StringRef Dir =
27805ffd83dbSDimitry Andric           Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath);
27815ffd83dbSDimitry Andric       if (Dir == DirStack.back()) {
27825ffd83dbSDimitry Andric         if (!IsCurrentDirEmpty) {
27830b57cec5SDimitry Andric           OS << ",\n";
27845ffd83dbSDimitry Andric         }
27855ffd83dbSDimitry Andric       } else {
27865ffd83dbSDimitry Andric         bool IsDirPoppedFromStack = false;
27870b57cec5SDimitry Andric         while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
27880b57cec5SDimitry Andric           OS << "\n";
27890b57cec5SDimitry Andric           endDirectory();
27905ffd83dbSDimitry Andric           IsDirPoppedFromStack = true;
27910b57cec5SDimitry Andric         }
27925ffd83dbSDimitry Andric         if (IsDirPoppedFromStack || !IsCurrentDirEmpty) {
27930b57cec5SDimitry Andric           OS << ",\n";
27945ffd83dbSDimitry Andric         }
27950b57cec5SDimitry Andric         startDirectory(Dir);
27965ffd83dbSDimitry Andric         IsCurrentDirEmpty = true;
27970b57cec5SDimitry Andric       }
27980b57cec5SDimitry Andric       StringRef RPath = Entry.RPath;
27990b57cec5SDimitry Andric       if (UseOverlayRelative) {
28000b57cec5SDimitry Andric         unsigned OverlayDirLen = OverlayDir.size();
28010b57cec5SDimitry Andric         assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
28020b57cec5SDimitry Andric                "Overlay dir must be contained in RPath");
28030b57cec5SDimitry Andric         RPath = RPath.slice(OverlayDirLen, RPath.size());
28040b57cec5SDimitry Andric       }
28055ffd83dbSDimitry Andric       if (!Entry.IsDirectory) {
28060b57cec5SDimitry Andric         writeEntry(path::filename(Entry.VPath), RPath);
28075ffd83dbSDimitry Andric         IsCurrentDirEmpty = false;
28085ffd83dbSDimitry Andric       }
28090b57cec5SDimitry Andric     }
28100b57cec5SDimitry Andric 
28110b57cec5SDimitry Andric     while (!DirStack.empty()) {
28120b57cec5SDimitry Andric       OS << "\n";
28130b57cec5SDimitry Andric       endDirectory();
28140b57cec5SDimitry Andric     }
28150b57cec5SDimitry Andric     OS << "\n";
28160b57cec5SDimitry Andric   }
28170b57cec5SDimitry Andric 
28180b57cec5SDimitry Andric   OS << "  ]\n"
28190b57cec5SDimitry Andric      << "}\n";
28200b57cec5SDimitry Andric }
28210b57cec5SDimitry Andric 
write(llvm::raw_ostream & OS)28220b57cec5SDimitry Andric void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
28230b57cec5SDimitry Andric   llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
28240b57cec5SDimitry Andric     return LHS.VPath < RHS.VPath;
28250b57cec5SDimitry Andric   });
28260b57cec5SDimitry Andric 
28270b57cec5SDimitry Andric   JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
28280b57cec5SDimitry Andric                        IsOverlayRelative, OverlayDir);
28290b57cec5SDimitry Andric }
28300b57cec5SDimitry Andric 
recursive_directory_iterator(FileSystem & FS_,const Twine & Path,std::error_code & EC)28310b57cec5SDimitry Andric vfs::recursive_directory_iterator::recursive_directory_iterator(
28320b57cec5SDimitry Andric     FileSystem &FS_, const Twine &Path, std::error_code &EC)
28330b57cec5SDimitry Andric     : FS(&FS_) {
28340b57cec5SDimitry Andric   directory_iterator I = FS->dir_begin(Path, EC);
28350b57cec5SDimitry Andric   if (I != directory_iterator()) {
28360b57cec5SDimitry Andric     State = std::make_shared<detail::RecDirIterState>();
28370b57cec5SDimitry Andric     State->Stack.push(I);
28380b57cec5SDimitry Andric   }
28390b57cec5SDimitry Andric }
28400b57cec5SDimitry Andric 
28410b57cec5SDimitry Andric vfs::recursive_directory_iterator &
increment(std::error_code & EC)28420b57cec5SDimitry Andric recursive_directory_iterator::increment(std::error_code &EC) {
28430b57cec5SDimitry Andric   assert(FS && State && !State->Stack.empty() && "incrementing past end");
28440b57cec5SDimitry Andric   assert(!State->Stack.top()->path().empty() && "non-canonical end iterator");
28450b57cec5SDimitry Andric   vfs::directory_iterator End;
28460b57cec5SDimitry Andric 
28470b57cec5SDimitry Andric   if (State->HasNoPushRequest)
28480b57cec5SDimitry Andric     State->HasNoPushRequest = false;
28490b57cec5SDimitry Andric   else {
28500b57cec5SDimitry Andric     if (State->Stack.top()->type() == sys::fs::file_type::directory_file) {
28510b57cec5SDimitry Andric       vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC);
28520b57cec5SDimitry Andric       if (I != End) {
28530b57cec5SDimitry Andric         State->Stack.push(I);
28540b57cec5SDimitry Andric         return *this;
28550b57cec5SDimitry Andric       }
28560b57cec5SDimitry Andric     }
28570b57cec5SDimitry Andric   }
28580b57cec5SDimitry Andric 
28590b57cec5SDimitry Andric   while (!State->Stack.empty() && State->Stack.top().increment(EC) == End)
28600b57cec5SDimitry Andric     State->Stack.pop();
28610b57cec5SDimitry Andric 
28620b57cec5SDimitry Andric   if (State->Stack.empty())
28630b57cec5SDimitry Andric     State.reset(); // end iterator
28640b57cec5SDimitry Andric 
28650b57cec5SDimitry Andric   return *this;
28660b57cec5SDimitry Andric }
2867