brintos

brintos / llvm-project-archived public Read only

0
0
Text · 100.6 KiB · c754b30 Raw
2988 lines · cpp
1//===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the VirtualFileSystem interface.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/Support/VirtualFileSystem.h"14#include "llvm/ADT/ArrayRef.h"15#include "llvm/ADT/DenseMap.h"16#include "llvm/ADT/IntrusiveRefCntPtr.h"17#include "llvm/ADT/STLExtras.h"18#include "llvm/ADT/SmallString.h"19#include "llvm/ADT/SmallVector.h"20#include "llvm/ADT/StringRef.h"21#include "llvm/ADT/StringSet.h"22#include "llvm/ADT/Twine.h"23#include "llvm/ADT/iterator_range.h"24#include "llvm/Config/llvm-config.h"25#include "llvm/Support/Casting.h"26#include "llvm/Support/Chrono.h"27#include "llvm/Support/Compiler.h"28#include "llvm/Support/Debug.h"29#include "llvm/Support/Errc.h"30#include "llvm/Support/ErrorHandling.h"31#include "llvm/Support/ErrorOr.h"32#include "llvm/Support/FileSystem.h"33#include "llvm/Support/FileSystem/UniqueID.h"34#include "llvm/Support/MemoryBuffer.h"35#include "llvm/Support/Path.h"36#include "llvm/Support/SMLoc.h"37#include "llvm/Support/SourceMgr.h"38#include "llvm/Support/YAMLParser.h"39#include "llvm/Support/raw_ostream.h"40#include <atomic>41#include <cassert>42#include <cstdint>43#include <iterator>44#include <limits>45#include <map>46#include <memory>47#include <optional>48#include <string>49#include <system_error>50#include <utility>51#include <vector>52 53using namespace llvm;54using namespace llvm::vfs;55 56using llvm::sys::fs::file_t;57using llvm::sys::fs::file_status;58using llvm::sys::fs::file_type;59using llvm::sys::fs::kInvalidFile;60using llvm::sys::fs::perms;61using llvm::sys::fs::UniqueID;62 63Status::Status(const file_status &Status)64    : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),65      User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),66      Type(Status.type()), Perms(Status.permissions()) {}67 68Status::Status(const Twine &Name, UniqueID UID, sys::TimePoint<> MTime,69               uint32_t User, uint32_t Group, uint64_t Size, file_type Type,70               perms Perms)71    : Name(Name.str()), UID(UID), MTime(MTime), User(User), Group(Group),72      Size(Size), Type(Type), Perms(Perms) {}73 74Status Status::copyWithNewSize(const Status &In, uint64_t NewSize) {75  return Status(In.getName(), In.getUniqueID(), In.getLastModificationTime(),76                In.getUser(), In.getGroup(), NewSize, In.getType(),77                In.getPermissions());78}79 80Status Status::copyWithNewName(const Status &In, const Twine &NewName) {81  return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),82                In.getUser(), In.getGroup(), In.getSize(), In.getType(),83                In.getPermissions());84}85 86Status Status::copyWithNewName(const file_status &In, const Twine &NewName) {87  return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),88                In.getUser(), In.getGroup(), In.getSize(), In.type(),89                In.permissions());90}91 92bool Status::equivalent(const Status &Other) const {93  assert(isStatusKnown() && Other.isStatusKnown());94  return getUniqueID() == Other.getUniqueID();95}96 97bool Status::isDirectory() const { return Type == file_type::directory_file; }98 99bool Status::isRegularFile() const { return Type == file_type::regular_file; }100 101bool Status::isOther() const {102  return exists() && !isRegularFile() && !isDirectory() && !isSymlink();103}104 105bool Status::isSymlink() const { return Type == file_type::symlink_file; }106 107bool Status::isStatusKnown() const { return Type != file_type::status_error; }108 109bool Status::exists() const {110  return isStatusKnown() && Type != file_type::file_not_found;111}112 113File::~File() = default;114 115FileSystem::~FileSystem() = default;116 117ErrorOr<std::unique_ptr<MemoryBuffer>>118FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,119                             bool RequiresNullTerminator, bool IsVolatile,120                             bool IsText) {121  auto F = IsText ? openFileForRead(Name) : openFileForReadBinary(Name);122  if (!F)123    return F.getError();124 125  return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);126}127 128std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {129  if (llvm::sys::path::is_absolute(Path))130    return {};131 132  auto WorkingDir = getCurrentWorkingDirectory();133  if (!WorkingDir)134    return WorkingDir.getError();135 136  sys::path::make_absolute(WorkingDir.get(), Path);137  return {};138}139 140std::error_code FileSystem::getRealPath(const Twine &Path,141                                        SmallVectorImpl<char> &Output) {142  return errc::operation_not_permitted;143}144 145std::error_code FileSystem::isLocal(const Twine &Path, bool &Result) {146  return errc::operation_not_permitted;147}148 149bool FileSystem::exists(const Twine &Path) {150  auto Status = status(Path);151  return Status && Status->exists();152}153 154llvm::ErrorOr<bool> FileSystem::equivalent(const Twine &A, const Twine &B) {155  auto StatusA = status(A);156  if (!StatusA)157    return StatusA.getError();158  auto StatusB = status(B);159  if (!StatusB)160    return StatusB.getError();161  return StatusA->equivalent(*StatusB);162}163 164#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)165void FileSystem::dump() const { print(dbgs(), PrintType::RecursiveContents); }166#endif167 168#ifndef NDEBUG169static bool isTraversalComponent(StringRef Component) {170  return Component == ".." || Component == ".";171}172 173static bool pathHasTraversal(StringRef Path) {174  using namespace llvm::sys;175 176  for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))177    if (isTraversalComponent(Comp))178      return true;179  return false;180}181#endif182 183//===-----------------------------------------------------------------------===/184// RealFileSystem implementation185//===-----------------------------------------------------------------------===/186 187namespace {188 189/// Wrapper around a raw file descriptor.190class RealFile : public File {191  friend class RealFileSystem;192 193  file_t FD;194  Status S;195  std::string RealName;196 197  RealFile(file_t RawFD, StringRef NewName, StringRef NewRealPathName)198      : FD(RawFD), S(NewName, {}, {}, {}, {}, {},199                     llvm::sys::fs::file_type::status_error, {}),200        RealName(NewRealPathName.str()) {201    assert(FD != kInvalidFile && "Invalid or inactive file descriptor");202  }203 204public:205  ~RealFile() override;206 207  ErrorOr<Status> status() override;208  ErrorOr<std::string> getName() override;209  ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,210                                                   int64_t FileSize,211                                                   bool RequiresNullTerminator,212                                                   bool IsVolatile) override;213  std::error_code close() override;214  void setPath(const Twine &Path) override;215};216 217} // namespace218 219RealFile::~RealFile() { close(); }220 221ErrorOr<Status> RealFile::status() {222  assert(FD != kInvalidFile && "cannot stat closed file");223  if (!S.isStatusKnown()) {224    file_status RealStatus;225    if (std::error_code EC = sys::fs::status(FD, RealStatus))226      return EC;227    S = Status::copyWithNewName(RealStatus, S.getName());228  }229  return S;230}231 232ErrorOr<std::string> RealFile::getName() {233  return RealName.empty() ? S.getName().str() : RealName;234}235 236ErrorOr<std::unique_ptr<MemoryBuffer>>237RealFile::getBuffer(const Twine &Name, int64_t FileSize,238                    bool RequiresNullTerminator, bool IsVolatile) {239  assert(FD != kInvalidFile && "cannot get buffer for closed file");240  return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,241                                   IsVolatile);242}243 244std::error_code RealFile::close() {245  std::error_code EC = sys::fs::closeFile(FD);246  FD = kInvalidFile;247  return EC;248}249 250void RealFile::setPath(const Twine &Path) {251  RealName = Path.str();252  if (auto Status = status())253    S = Status.get().copyWithNewName(Status.get(), Path);254}255 256namespace {257 258/// A file system according to your operating system.259/// This may be linked to the process's working directory, or maintain its own.260///261/// Currently, its own working directory is emulated by storing the path and262/// sending absolute paths to llvm::sys::fs:: functions.263/// A more principled approach would be to push this down a level, modelling264/// the working dir as an llvm::sys::fs::WorkingDir or similar.265/// This would enable the use of openat()-style functions on some platforms.266class RealFileSystem : public FileSystem {267public:268  explicit RealFileSystem(bool LinkCWDToProcess) {269    if (!LinkCWDToProcess) {270      SmallString<128> PWD, RealPWD;271      if (std::error_code EC = llvm::sys::fs::current_path(PWD))272        WD = EC;273      else if (llvm::sys::fs::real_path(PWD, RealPWD))274        WD = WorkingDirectory{PWD, PWD};275      else276        WD = WorkingDirectory{PWD, RealPWD};277    }278  }279 280  ErrorOr<Status> status(const Twine &Path) override;281  ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;282  ErrorOr<std::unique_ptr<File>>283  openFileForReadBinary(const Twine &Path) override;284  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;285 286  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;287  std::error_code setCurrentWorkingDirectory(const Twine &Path) override;288  std::error_code isLocal(const Twine &Path, bool &Result) override;289  std::error_code getRealPath(const Twine &Path,290                              SmallVectorImpl<char> &Output) override;291 292protected:293  void printImpl(raw_ostream &OS, PrintType Type,294                 unsigned IndentLevel) const override;295 296private:297  // If this FS has its own working dir, use it to make Path absolute.298  // The returned twine is safe to use as long as both Storage and Path live.299  Twine adjustPath(const Twine &Path, SmallVectorImpl<char> &Storage) const {300    if (!WD || !*WD)301      return Path;302    Path.toVector(Storage);303    sys::path::make_absolute(WD->get().Resolved, Storage);304    return Storage;305  }306 307  ErrorOr<std::unique_ptr<File>>308  openFileForReadWithFlags(const Twine &Name, sys::fs::OpenFlags Flags) {309    SmallString<256> RealName, Storage;310    Expected<file_t> FDOrErr = sys::fs::openNativeFileForRead(311        adjustPath(Name, Storage), Flags, &RealName);312    if (!FDOrErr)313      return errorToErrorCode(FDOrErr.takeError());314    return std::unique_ptr<File>(315        new RealFile(*FDOrErr, Name.str(), RealName.str()));316  }317 318  struct WorkingDirectory {319    // The current working directory, without symlinks resolved. (echo $PWD).320    SmallString<128> Specified;321    // The current working directory, with links resolved. (readlink .).322    SmallString<128> Resolved;323  };324  std::optional<llvm::ErrorOr<WorkingDirectory>> WD;325};326 327} // namespace328 329ErrorOr<Status> RealFileSystem::status(const Twine &Path) {330  SmallString<256> Storage;331  sys::fs::file_status RealStatus;332  if (std::error_code EC =333          sys::fs::status(adjustPath(Path, Storage), RealStatus))334    return EC;335  return Status::copyWithNewName(RealStatus, Path);336}337 338ErrorOr<std::unique_ptr<File>>339RealFileSystem::openFileForRead(const Twine &Name) {340  return openFileForReadWithFlags(Name, sys::fs::OF_Text);341}342 343ErrorOr<std::unique_ptr<File>>344RealFileSystem::openFileForReadBinary(const Twine &Name) {345  return openFileForReadWithFlags(Name, sys::fs::OF_None);346}347 348llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {349  if (WD && *WD)350    return std::string(WD->get().Specified);351  if (WD)352    return WD->getError();353 354  SmallString<128> Dir;355  if (std::error_code EC = llvm::sys::fs::current_path(Dir))356    return EC;357  return std::string(Dir);358}359 360std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {361  if (!WD)362    return llvm::sys::fs::set_current_path(Path);363 364  SmallString<128> Absolute, Resolved, Storage;365  adjustPath(Path, Storage).toVector(Absolute);366  bool IsDir;367  if (auto Err = llvm::sys::fs::is_directory(Absolute, IsDir))368    return Err;369  if (!IsDir)370    return std::make_error_code(std::errc::not_a_directory);371  if (auto Err = llvm::sys::fs::real_path(Absolute, Resolved))372    return Err;373  WD = WorkingDirectory{Absolute, Resolved};374  return std::error_code();375}376 377std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) {378  SmallString<256> Storage;379  return llvm::sys::fs::is_local(adjustPath(Path, Storage), Result);380}381 382std::error_code RealFileSystem::getRealPath(const Twine &Path,383                                            SmallVectorImpl<char> &Output) {384  SmallString<256> Storage;385  return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output);386}387 388void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type,389                               unsigned IndentLevel) const {390  printIndent(OS, IndentLevel);391  OS << "RealFileSystem using ";392  if (WD)393    OS << "own";394  else395    OS << "process";396  OS << " CWD\n";397}398 399IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {400  static IntrusiveRefCntPtr<FileSystem> FS =401      makeIntrusiveRefCnt<RealFileSystem>(true);402  return FS;403}404 405std::unique_ptr<FileSystem> vfs::createPhysicalFileSystem() {406  return std::make_unique<RealFileSystem>(false);407}408 409namespace {410 411class RealFSDirIter : public llvm::vfs::detail::DirIterImpl {412  llvm::sys::fs::directory_iterator Iter;413 414public:415  RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {416    if (Iter != llvm::sys::fs::directory_iterator())417      CurrentEntry = directory_entry(Iter->path(), Iter->type());418  }419 420  std::error_code increment() override {421    std::error_code EC;422    Iter.increment(EC);423    CurrentEntry = (Iter == llvm::sys::fs::directory_iterator())424                       ? directory_entry()425                       : directory_entry(Iter->path(), Iter->type());426    return EC;427  }428};429 430} // namespace431 432directory_iterator RealFileSystem::dir_begin(const Twine &Dir,433                                             std::error_code &EC) {434  SmallString<128> Storage;435  return directory_iterator(436      std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC));437}438 439//===-----------------------------------------------------------------------===/440// OverlayFileSystem implementation441//===-----------------------------------------------------------------------===/442 443OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {444  FSList.push_back(std::move(BaseFS));445}446 447void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {448  FSList.push_back(FS);449  // Synchronize added file systems by duplicating the working directory from450  // the first one in the list.451  FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());452}453 454ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {455  // FIXME: handle symlinks that cross file systems456  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {457    ErrorOr<Status> Status = (*I)->status(Path);458    if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)459      return Status;460  }461  return make_error_code(llvm::errc::no_such_file_or_directory);462}463 464bool OverlayFileSystem::exists(const Twine &Path) {465  // FIXME: handle symlinks that cross file systems466  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {467    if ((*I)->exists(Path))468      return true;469  }470  return false;471}472 473ErrorOr<std::unique_ptr<File>>474OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {475  // FIXME: handle symlinks that cross file systems476  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {477    auto Result = (*I)->openFileForRead(Path);478    if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)479      return Result;480  }481  return make_error_code(llvm::errc::no_such_file_or_directory);482}483 484llvm::ErrorOr<std::string>485OverlayFileSystem::getCurrentWorkingDirectory() const {486  // All file systems are synchronized, just take the first working directory.487  return FSList.front()->getCurrentWorkingDirectory();488}489 490std::error_code491OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {492  for (auto &FS : FSList)493    if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))494      return EC;495  return {};496}497 498std::error_code OverlayFileSystem::isLocal(const Twine &Path, bool &Result) {499  for (auto &FS : FSList)500    if (FS->exists(Path))501      return FS->isLocal(Path, Result);502  return errc::no_such_file_or_directory;503}504 505std::error_code OverlayFileSystem::getRealPath(const Twine &Path,506                                               SmallVectorImpl<char> &Output) {507  for (const auto &FS : FSList)508    if (FS->exists(Path))509      return FS->getRealPath(Path, Output);510  return errc::no_such_file_or_directory;511}512 513void OverlayFileSystem::visitChildFileSystems(VisitCallbackTy Callback) {514  for (IntrusiveRefCntPtr<FileSystem> FS : overlays_range()) {515    Callback(*FS);516    FS->visitChildFileSystems(Callback);517  }518}519 520void OverlayFileSystem::printImpl(raw_ostream &OS, PrintType Type,521                                  unsigned IndentLevel) const {522  printIndent(OS, IndentLevel);523  OS << "OverlayFileSystem\n";524  if (Type == PrintType::Summary)525    return;526 527  if (Type == PrintType::Contents)528    Type = PrintType::Summary;529  for (const auto &FS : overlays_range())530    FS->print(OS, Type, IndentLevel + 1);531}532 533llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;534 535namespace {536 537/// Combines and deduplicates directory entries across multiple file systems.538class CombiningDirIterImpl : public llvm::vfs::detail::DirIterImpl {539  using FileSystemPtr = llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>;540 541  /// Iterators to combine, processed in reverse order.542  SmallVector<directory_iterator, 8> IterList;543  /// The iterator currently being traversed.544  directory_iterator CurrentDirIter;545  /// The set of names already returned as entries.546  llvm::StringSet<> SeenNames;547 548  /// Sets \c CurrentDirIter to the next iterator in the list, or leaves it as549  /// is (at its end position) if we've already gone through them all.550  std::error_code incrementIter(bool IsFirstTime) {551    while (!IterList.empty()) {552      CurrentDirIter = IterList.back();553      IterList.pop_back();554      if (CurrentDirIter != directory_iterator())555        break; // found556    }557 558    if (IsFirstTime && CurrentDirIter == directory_iterator())559      return errc::no_such_file_or_directory;560    return {};561  }562 563  std::error_code incrementDirIter(bool IsFirstTime) {564    assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&565           "incrementing past end");566    std::error_code EC;567    if (!IsFirstTime)568      CurrentDirIter.increment(EC);569    if (!EC && CurrentDirIter == directory_iterator())570      EC = incrementIter(IsFirstTime);571    return EC;572  }573 574  std::error_code incrementImpl(bool IsFirstTime) {575    while (true) {576      std::error_code EC = incrementDirIter(IsFirstTime);577      if (EC || CurrentDirIter == directory_iterator()) {578        CurrentEntry = directory_entry();579        return EC;580      }581      CurrentEntry = *CurrentDirIter;582      StringRef Name = llvm::sys::path::filename(CurrentEntry.path());583      if (SeenNames.insert(Name).second)584        return EC; // name not seen before585    }586    llvm_unreachable("returned above");587  }588 589public:590  CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems, std::string Dir,591                       std::error_code &EC) {592    for (const auto &FS : FileSystems) {593      std::error_code FEC;594      directory_iterator Iter = FS->dir_begin(Dir, FEC);595      if (FEC && FEC != errc::no_such_file_or_directory) {596        EC = FEC;597        return;598      }599      if (!FEC)600        IterList.push_back(Iter);601    }602    EC = incrementImpl(true);603  }604 605  CombiningDirIterImpl(ArrayRef<directory_iterator> DirIters,606                       std::error_code &EC)607      : IterList(DirIters) {608    EC = incrementImpl(true);609  }610 611  std::error_code increment() override { return incrementImpl(false); }612};613 614} // namespace615 616directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,617                                                std::error_code &EC) {618  directory_iterator Combined = directory_iterator(619      std::make_shared<CombiningDirIterImpl>(FSList, Dir.str(), EC));620  if (EC)621    return {};622  return Combined;623}624 625void ProxyFileSystem::anchor() {}626 627namespace llvm {628namespace vfs {629 630namespace detail {631 632enum InMemoryNodeKind {633  IME_File,634  IME_Directory,635  IME_HardLink,636  IME_SymbolicLink,637};638 639/// The in memory file system is a tree of Nodes. Every node can either be a640/// file, symlink, hardlink or a directory.641class InMemoryNode {642  InMemoryNodeKind Kind;643  std::string FileName;644 645public:646  InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind)647      : Kind(Kind), FileName(std::string(llvm::sys::path::filename(FileName))) {648  }649  virtual ~InMemoryNode() = default;650 651  /// Return the \p Status for this node. \p RequestedName should be the name652  /// through which the caller referred to this node. It will override653  /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.654  virtual Status getStatus(const Twine &RequestedName) const = 0;655 656  /// Get the filename of this node (the name without the directory part).657  StringRef getFileName() const { return FileName; }658  InMemoryNodeKind getKind() const { return Kind; }659  virtual std::string toString(unsigned Indent) const = 0;660};661 662class InMemoryFile : public InMemoryNode {663  Status Stat;664  std::unique_ptr<llvm::MemoryBuffer> Buffer;665 666public:667  InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)668      : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)),669        Buffer(std::move(Buffer)) {}670 671  Status getStatus(const Twine &RequestedName) const override {672    return Status::copyWithNewName(Stat, RequestedName);673  }674  llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); }675 676  std::string toString(unsigned Indent) const override {677    return (std::string(Indent, ' ') + Stat.getName() + "\n").str();678  }679 680  static bool classof(const InMemoryNode *N) {681    return N->getKind() == IME_File;682  }683};684 685namespace {686 687class InMemoryHardLink : public InMemoryNode {688  const InMemoryFile &ResolvedFile;689 690public:691  InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile)692      : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {}693  const InMemoryFile &getResolvedFile() const { return ResolvedFile; }694 695  Status getStatus(const Twine &RequestedName) const override {696    return ResolvedFile.getStatus(RequestedName);697  }698 699  std::string toString(unsigned Indent) const override {700    return std::string(Indent, ' ') + "HardLink to -> " +701           ResolvedFile.toString(0);702  }703 704  static bool classof(const InMemoryNode *N) {705    return N->getKind() == IME_HardLink;706  }707};708 709class InMemorySymbolicLink : public InMemoryNode {710  std::string TargetPath;711  Status Stat;712 713public:714  InMemorySymbolicLink(StringRef Path, StringRef TargetPath, Status Stat)715      : InMemoryNode(Path, IME_SymbolicLink), TargetPath(std::move(TargetPath)),716        Stat(Stat) {}717 718  std::string toString(unsigned Indent) const override {719    return std::string(Indent, ' ') + "SymbolicLink to -> " + TargetPath;720  }721 722  Status getStatus(const Twine &RequestedName) const override {723    return Status::copyWithNewName(Stat, RequestedName);724  }725 726  StringRef getTargetPath() const { return TargetPath; }727 728  static bool classof(const InMemoryNode *N) {729    return N->getKind() == IME_SymbolicLink;730  }731};732 733/// Adapt a InMemoryFile for VFS' File interface.  The goal is to make734/// \p InMemoryFileAdaptor mimic as much as possible the behavior of735/// \p RealFile.736class InMemoryFileAdaptor : public File {737  const InMemoryFile &Node;738  /// The name to use when returning a Status for this file.739  std::string RequestedName;740 741public:742  explicit InMemoryFileAdaptor(const InMemoryFile &Node,743                               std::string RequestedName)744      : Node(Node), RequestedName(std::move(RequestedName)) {}745 746  llvm::ErrorOr<Status> status() override {747    return Node.getStatus(RequestedName);748  }749 750  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>751  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,752            bool IsVolatile) override {753    llvm::MemoryBuffer *Buf = Node.getBuffer();754    return llvm::MemoryBuffer::getMemBuffer(755        Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);756  }757 758  std::error_code close() override { return {}; }759 760  void setPath(const Twine &Path) override { RequestedName = Path.str(); }761};762} // namespace763 764class InMemoryDirectory : public InMemoryNode {765  Status Stat;766  std::map<std::string, std::unique_ptr<InMemoryNode>, std::less<>> Entries;767 768public:769  InMemoryDirectory(Status Stat)770      : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {}771 772  /// Return the \p Status for this node. \p RequestedName should be the name773  /// through which the caller referred to this node. It will override774  /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.775  Status getStatus(const Twine &RequestedName) const override {776    return Status::copyWithNewName(Stat, RequestedName);777  }778 779  UniqueID getUniqueID() const { return Stat.getUniqueID(); }780 781  InMemoryNode *getChild(StringRef Name) const {782    auto I = Entries.find(Name);783    if (I != Entries.end())784      return I->second.get();785    return nullptr;786  }787 788  InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {789    return Entries.emplace(Name, std::move(Child)).first->second.get();790  }791 792  using const_iterator = decltype(Entries)::const_iterator;793 794  const_iterator begin() const { return Entries.begin(); }795  const_iterator end() const { return Entries.end(); }796 797  std::string toString(unsigned Indent) const override {798    std::string Result =799        (std::string(Indent, ' ') + Stat.getName() + "\n").str();800    for (const auto &Entry : Entries)801      Result += Entry.second->toString(Indent + 2);802    return Result;803  }804 805  static bool classof(const InMemoryNode *N) {806    return N->getKind() == IME_Directory;807  }808};809 810} // namespace detail811 812// The UniqueID of in-memory files is derived from path and content.813// This avoids difficulties in creating exactly equivalent in-memory FSes,814// as often needed in multithreaded programs.815static sys::fs::UniqueID getUniqueID(hash_code Hash) {816  return sys::fs::UniqueID(std::numeric_limits<uint64_t>::max(),817                           uint64_t(size_t(Hash)));818}819static sys::fs::UniqueID getFileID(sys::fs::UniqueID Parent,820                                   llvm::StringRef Name,821                                   llvm::StringRef Contents) {822  return getUniqueID(llvm::hash_combine(Parent.getFile(), Name, Contents));823}824static sys::fs::UniqueID getDirectoryID(sys::fs::UniqueID Parent,825                                        llvm::StringRef Name) {826  return getUniqueID(llvm::hash_combine(Parent.getFile(), Name));827}828 829Status detail::NewInMemoryNodeInfo::makeStatus() const {830  UniqueID UID =831      (Type == sys::fs::file_type::directory_file)832          ? getDirectoryID(DirUID, Name)833          : getFileID(DirUID, Name, Buffer ? Buffer->getBuffer() : "");834 835  return Status(Path, UID, llvm::sys::toTimePoint(ModificationTime), User,836                Group, Buffer ? Buffer->getBufferSize() : 0, Type, Perms);837}838 839InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)840    : Root(new detail::InMemoryDirectory(841          Status("", getDirectoryID(llvm::sys::fs::UniqueID(), ""),842                 llvm::sys::TimePoint<>(), 0, 0, 0,843                 llvm::sys::fs::file_type::directory_file,844                 llvm::sys::fs::perms::all_all))),845      UseNormalizedPaths(UseNormalizedPaths) {}846 847InMemoryFileSystem::~InMemoryFileSystem() = default;848 849std::string InMemoryFileSystem::toString() const {850  return Root->toString(/*Indent=*/0);851}852 853bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,854                                 std::unique_ptr<llvm::MemoryBuffer> Buffer,855                                 std::optional<uint32_t> User,856                                 std::optional<uint32_t> Group,857                                 std::optional<llvm::sys::fs::file_type> Type,858                                 std::optional<llvm::sys::fs::perms> Perms,859                                 MakeNodeFn MakeNode) {860  SmallString<128> Path;861  P.toVector(Path);862 863  // Fix up relative paths. This just prepends the current working directory.864  std::error_code EC = makeAbsolute(Path);865  assert(!EC);866  (void)EC;867 868  if (useNormalizedPaths())869    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);870 871  if (Path.empty())872    return false;873 874  detail::InMemoryDirectory *Dir = Root.get();875  auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path);876  const auto ResolvedUser = User.value_or(0);877  const auto ResolvedGroup = Group.value_or(0);878  const auto ResolvedType = Type.value_or(sys::fs::file_type::regular_file);879  const auto ResolvedPerms = Perms.value_or(sys::fs::all_all);880  // Any intermediate directories we create should be accessible by881  // the owner, even if Perms says otherwise for the final path.882  const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all;883 884  StringRef Name = *I;885  while (true) {886    Name = *I;887    ++I;888    if (I == E)889      break;890    detail::InMemoryNode *Node = Dir->getChild(Name);891    if (!Node) {892      // This isn't the last element, so we create a new directory.893      Status Stat(894          StringRef(Path.str().begin(), Name.end() - Path.str().begin()),895          getDirectoryID(Dir->getUniqueID(), Name),896          llvm::sys::toTimePoint(ModificationTime), ResolvedUser, ResolvedGroup,897          0, sys::fs::file_type::directory_file, NewDirectoryPerms);898      Dir = cast<detail::InMemoryDirectory>(Dir->addChild(899          Name, std::make_unique<detail::InMemoryDirectory>(std::move(Stat))));900      continue;901    }902    // Creating file under another file.903    if (!isa<detail::InMemoryDirectory>(Node))904      return false;905    Dir = cast<detail::InMemoryDirectory>(Node);906  }907  detail::InMemoryNode *Node = Dir->getChild(Name);908  if (!Node) {909    Dir->addChild(Name,910                  MakeNode({Dir->getUniqueID(), Path, Name, ModificationTime,911                            std::move(Buffer), ResolvedUser, ResolvedGroup,912                            ResolvedType, ResolvedPerms}));913    return true;914  }915  if (isa<detail::InMemoryDirectory>(Node))916    return ResolvedType == sys::fs::file_type::directory_file;917 918  assert((isa<detail::InMemoryFile>(Node) ||919          isa<detail::InMemoryHardLink>(Node)) &&920         "Must be either file, hardlink or directory!");921 922  // Return false only if the new file is different from the existing one.923  if (auto *Link = dyn_cast<detail::InMemoryHardLink>(Node)) {924    return Link->getResolvedFile().getBuffer()->getBuffer() ==925           Buffer->getBuffer();926  }927  return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==928         Buffer->getBuffer();929}930 931bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,932                                 std::unique_ptr<llvm::MemoryBuffer> Buffer,933                                 std::optional<uint32_t> User,934                                 std::optional<uint32_t> Group,935                                 std::optional<llvm::sys::fs::file_type> Type,936                                 std::optional<llvm::sys::fs::perms> Perms) {937  return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type,938                 Perms,939                 [](detail::NewInMemoryNodeInfo NNI)940                     -> std::unique_ptr<detail::InMemoryNode> {941                   Status Stat = NNI.makeStatus();942                   if (Stat.getType() == sys::fs::file_type::directory_file)943                     return std::make_unique<detail::InMemoryDirectory>(Stat);944                   return std::make_unique<detail::InMemoryFile>(945                       Stat, std::move(NNI.Buffer));946                 });947}948 949bool InMemoryFileSystem::addFileNoOwn(950    const Twine &P, time_t ModificationTime,951    const llvm::MemoryBufferRef &Buffer, std::optional<uint32_t> User,952    std::optional<uint32_t> Group, std::optional<llvm::sys::fs::file_type> Type,953    std::optional<llvm::sys::fs::perms> Perms) {954  return addFile(P, ModificationTime, llvm::MemoryBuffer::getMemBuffer(Buffer),955                 std::move(User), std::move(Group), std::move(Type),956                 std::move(Perms),957                 [](detail::NewInMemoryNodeInfo NNI)958                     -> std::unique_ptr<detail::InMemoryNode> {959                   Status Stat = NNI.makeStatus();960                   if (Stat.getType() == sys::fs::file_type::directory_file)961                     return std::make_unique<detail::InMemoryDirectory>(Stat);962                   return std::make_unique<detail::InMemoryFile>(963                       Stat, std::move(NNI.Buffer));964                 });965}966 967detail::NamedNodeOrError968InMemoryFileSystem::lookupNode(const Twine &P, bool FollowFinalSymlink,969                               size_t SymlinkDepth) const {970  SmallString<128> Path;971  P.toVector(Path);972 973  // Fix up relative paths. This just prepends the current working directory.974  std::error_code EC = makeAbsolute(Path);975  assert(!EC);976  (void)EC;977 978  if (useNormalizedPaths())979    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);980 981  const detail::InMemoryDirectory *Dir = Root.get();982  if (Path.empty())983    return detail::NamedNodeOrError(Path, Dir);984 985  auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);986  while (true) {987    detail::InMemoryNode *Node = Dir->getChild(*I);988    ++I;989    if (!Node)990      return errc::no_such_file_or_directory;991 992    if (auto Symlink = dyn_cast<detail::InMemorySymbolicLink>(Node)) {993      // If we're at the end of the path, and we're not following through994      // terminal symlinks, then we're done.995      if (I == E && !FollowFinalSymlink)996        return detail::NamedNodeOrError(Path, Symlink);997 998      if (SymlinkDepth > InMemoryFileSystem::MaxSymlinkDepth)999        return errc::no_such_file_or_directory;1000 1001      SmallString<128> TargetPath = Symlink->getTargetPath();1002      if (std::error_code EC = makeAbsolute(TargetPath))1003        return EC;1004 1005      // Keep going with the target. We always want to follow symlinks here1006      // because we're either at the end of a path that we want to follow, or1007      // not at the end of a path, in which case we need to follow the symlink1008      // regardless.1009      auto Target =1010          lookupNode(TargetPath, /*FollowFinalSymlink=*/true, SymlinkDepth + 1);1011      if (!Target || I == E)1012        return Target;1013 1014      if (!isa<detail::InMemoryDirectory>(*Target))1015        return errc::no_such_file_or_directory;1016 1017      // Otherwise, continue on the search in the symlinked directory.1018      Dir = cast<detail::InMemoryDirectory>(*Target);1019      continue;1020    }1021 1022    // Return the file if it's at the end of the path.1023    if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {1024      if (I == E)1025        return detail::NamedNodeOrError(Path, File);1026      return errc::no_such_file_or_directory;1027    }1028 1029    // If Node is HardLink then return the resolved file.1030    if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) {1031      if (I == E)1032        return detail::NamedNodeOrError(Path, &File->getResolvedFile());1033      return errc::no_such_file_or_directory;1034    }1035    // Traverse directories.1036    Dir = cast<detail::InMemoryDirectory>(Node);1037    if (I == E)1038      return detail::NamedNodeOrError(Path, Dir);1039  }1040}1041 1042bool InMemoryFileSystem::addHardLink(const Twine &NewLink,1043                                     const Twine &Target) {1044  auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);1045  // Whether symlinks in the hardlink target are followed is1046  // implementation-defined in POSIX.1047  // We're following symlinks here to be consistent with macOS.1048  auto TargetNode = lookupNode(Target, /*FollowFinalSymlink=*/true);1049  // FromPath must not have been added before. ToPath must have been added1050  // before. Resolved ToPath must be a File.1051  if (!TargetNode || NewLinkNode || !isa<detail::InMemoryFile>(*TargetNode))1052    return false;1053  return addFile(NewLink, 0, nullptr, std::nullopt, std::nullopt, std::nullopt,1054                 std::nullopt, [&](detail::NewInMemoryNodeInfo NNI) {1055                   return std::make_unique<detail::InMemoryHardLink>(1056                       NNI.Path.str(),1057                       *cast<detail::InMemoryFile>(*TargetNode));1058                 });1059}1060 1061bool InMemoryFileSystem::addSymbolicLink(1062    const Twine &NewLink, const Twine &Target, time_t ModificationTime,1063    std::optional<uint32_t> User, std::optional<uint32_t> Group,1064    std::optional<llvm::sys::fs::perms> Perms) {1065  auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);1066  if (NewLinkNode)1067    return false;1068 1069  SmallString<128> NewLinkStr, TargetStr;1070  NewLink.toVector(NewLinkStr);1071  Target.toVector(TargetStr);1072 1073  return addFile(NewLinkStr, ModificationTime, nullptr, User, Group,1074                 sys::fs::file_type::symlink_file, Perms,1075                 [&](detail::NewInMemoryNodeInfo NNI) {1076                   return std::make_unique<detail::InMemorySymbolicLink>(1077                       NewLinkStr, TargetStr, NNI.makeStatus());1078                 });1079}1080 1081llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {1082  auto Node = lookupNode(Path, /*FollowFinalSymlink=*/true);1083  if (Node)1084    return (*Node)->getStatus(Path);1085  return Node.getError();1086}1087 1088llvm::ErrorOr<std::unique_ptr<File>>1089InMemoryFileSystem::openFileForRead(const Twine &Path) {1090  auto Node = lookupNode(Path,/*FollowFinalSymlink=*/true);1091  if (!Node)1092    return Node.getError();1093 1094  // When we have a file provide a heap-allocated wrapper for the memory buffer1095  // to match the ownership semantics for File.1096  if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))1097    return std::unique_ptr<File>(1098        new detail::InMemoryFileAdaptor(*F, Path.str()));1099 1100  // FIXME: errc::not_a_file?1101  return make_error_code(llvm::errc::invalid_argument);1102}1103 1104/// Adaptor from InMemoryDir::iterator to directory_iterator.1105class InMemoryFileSystem::DirIterator : public llvm::vfs::detail::DirIterImpl {1106  const InMemoryFileSystem *FS;1107  detail::InMemoryDirectory::const_iterator I;1108  detail::InMemoryDirectory::const_iterator E;1109  std::string RequestedDirName;1110 1111  void setCurrentEntry() {1112    if (I != E) {1113      SmallString<256> Path(RequestedDirName);1114      llvm::sys::path::append(Path, I->second->getFileName());1115      sys::fs::file_type Type = sys::fs::file_type::type_unknown;1116      switch (I->second->getKind()) {1117      case detail::IME_File:1118      case detail::IME_HardLink:1119        Type = sys::fs::file_type::regular_file;1120        break;1121      case detail::IME_Directory:1122        Type = sys::fs::file_type::directory_file;1123        break;1124      case detail::IME_SymbolicLink:1125        if (auto SymlinkTarget =1126                FS->lookupNode(Path, /*FollowFinalSymlink=*/true)) {1127          Path = SymlinkTarget.getName();1128          Type = (*SymlinkTarget)->getStatus(Path).getType();1129        }1130        break;1131      }1132      CurrentEntry = directory_entry(std::string(Path), Type);1133    } else {1134      // When we're at the end, make CurrentEntry invalid and DirIterImpl will1135      // do the rest.1136      CurrentEntry = directory_entry();1137    }1138  }1139 1140public:1141  DirIterator() = default;1142 1143  DirIterator(const InMemoryFileSystem *FS,1144              const detail::InMemoryDirectory &Dir,1145              std::string RequestedDirName)1146      : FS(FS), I(Dir.begin()), E(Dir.end()),1147        RequestedDirName(std::move(RequestedDirName)) {1148    setCurrentEntry();1149  }1150 1151  std::error_code increment() override {1152    ++I;1153    setCurrentEntry();1154    return {};1155  }1156};1157 1158directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,1159                                                 std::error_code &EC) {1160  auto Node = lookupNode(Dir, /*FollowFinalSymlink=*/true);1161  if (!Node) {1162    EC = Node.getError();1163    return directory_iterator(std::make_shared<DirIterator>());1164  }1165 1166  if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))1167    return directory_iterator(1168        std::make_shared<DirIterator>(this, *DirNode, Dir.str()));1169 1170  EC = make_error_code(llvm::errc::not_a_directory);1171  return directory_iterator(std::make_shared<DirIterator>());1172}1173 1174std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) {1175  SmallString<128> Path;1176  P.toVector(Path);1177 1178  // Fix up relative paths. This just prepends the current working directory.1179  std::error_code EC = makeAbsolute(Path);1180  assert(!EC);1181  (void)EC;1182 1183  if (useNormalizedPaths())1184    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);1185 1186  if (!Path.empty())1187    WorkingDirectory = std::string(Path);1188  return {};1189}1190 1191std::error_code InMemoryFileSystem::getRealPath(const Twine &Path,1192                                                SmallVectorImpl<char> &Output) {1193  auto CWD = getCurrentWorkingDirectory();1194  if (!CWD || CWD->empty())1195    return errc::operation_not_permitted;1196  Path.toVector(Output);1197  if (auto EC = makeAbsolute(Output))1198    return EC;1199  llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true);1200  return {};1201}1202 1203std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {1204  Result = false;1205  return {};1206}1207 1208void InMemoryFileSystem::printImpl(raw_ostream &OS, PrintType PrintContents,1209                                   unsigned IndentLevel) const {1210  printIndent(OS, IndentLevel);1211  OS << "InMemoryFileSystem\n";1212}1213 1214} // namespace vfs1215} // namespace llvm1216 1217//===-----------------------------------------------------------------------===/1218// RedirectingFileSystem implementation1219//===-----------------------------------------------------------------------===/1220 1221namespace {1222 1223static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) {1224  // Detect the path style in use by checking the first separator.1225  llvm::sys::path::Style style = llvm::sys::path::Style::native;1226  const size_t n = Path.find_first_of("/\\");1227  // Can't distinguish between posix and windows_slash here.1228  if (n != static_cast<size_t>(-1))1229    style = (Path[n] == '/') ? llvm::sys::path::Style::posix1230                             : llvm::sys::path::Style::windows_backslash;1231  return style;1232}1233 1234/// Removes leading "./" as well as path components like ".." and ".".1235static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {1236  // First detect the path style in use by checking the first separator.1237  llvm::sys::path::Style style = getExistingStyle(Path);1238 1239  // Now remove the dots.  Explicitly specifying the path style prevents the1240  // direction of the slashes from changing.1241  llvm::SmallString<256> result =1242      llvm::sys::path::remove_leading_dotslash(Path, style);1243  llvm::sys::path::remove_dots(result, /*remove_dot_dot=*/true, style);1244  return result;1245}1246 1247/// Whether the error and entry specify a file/directory that was not found.1248static bool isFileNotFound(std::error_code EC,1249                           RedirectingFileSystem::Entry *E = nullptr) {1250  if (E && !isa<RedirectingFileSystem::DirectoryRemapEntry>(E))1251    return false;1252  return EC == llvm::errc::no_such_file_or_directory;1253}1254 1255} // anonymous namespace1256 1257 1258RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)1259    : ExternalFS(std::move(FS)) {1260  if (ExternalFS)1261    if (auto ExternalWorkingDirectory =1262            ExternalFS->getCurrentWorkingDirectory()) {1263      WorkingDirectory = *ExternalWorkingDirectory;1264    }1265}1266 1267/// Directory iterator implementation for \c RedirectingFileSystem's1268/// directory entries.1269class llvm::vfs::RedirectingFSDirIterImpl1270    : public llvm::vfs::detail::DirIterImpl {1271  std::string Dir;1272  RedirectingFileSystem::DirectoryEntry::iterator Current, End;1273 1274  std::error_code incrementImpl(bool IsFirstTime) {1275    assert((IsFirstTime || Current != End) && "cannot iterate past end");1276    if (!IsFirstTime)1277      ++Current;1278    if (Current != End) {1279      SmallString<128> PathStr(Dir);1280      llvm::sys::path::append(PathStr, (*Current)->getName());1281      sys::fs::file_type Type = sys::fs::file_type::type_unknown;1282      switch ((*Current)->getKind()) {1283      case RedirectingFileSystem::EK_Directory:1284        [[fallthrough]];1285      case RedirectingFileSystem::EK_DirectoryRemap:1286        Type = sys::fs::file_type::directory_file;1287        break;1288      case RedirectingFileSystem::EK_File:1289        Type = sys::fs::file_type::regular_file;1290        break;1291      }1292      CurrentEntry = directory_entry(std::string(PathStr), Type);1293    } else {1294      CurrentEntry = directory_entry();1295    }1296    return {};1297  };1298 1299public:1300  RedirectingFSDirIterImpl(1301      const Twine &Path, RedirectingFileSystem::DirectoryEntry::iterator Begin,1302      RedirectingFileSystem::DirectoryEntry::iterator End, std::error_code &EC)1303      : Dir(Path.str()), Current(Begin), End(End) {1304    EC = incrementImpl(/*IsFirstTime=*/true);1305  }1306 1307  std::error_code increment() override {1308    return incrementImpl(/*IsFirstTime=*/false);1309  }1310};1311 1312namespace {1313/// Directory iterator implementation for \c RedirectingFileSystem's1314/// directory remap entries that maps the paths reported by the external1315/// file system's directory iterator back to the virtual directory's path.1316class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl {1317  std::string Dir;1318  llvm::sys::path::Style DirStyle;1319  llvm::vfs::directory_iterator ExternalIter;1320 1321public:1322  RedirectingFSDirRemapIterImpl(std::string DirPath,1323                                llvm::vfs::directory_iterator ExtIter)1324      : Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)),1325        ExternalIter(ExtIter) {1326    if (ExternalIter != llvm::vfs::directory_iterator())1327      setCurrentEntry();1328  }1329 1330  void setCurrentEntry() {1331    StringRef ExternalPath = ExternalIter->path();1332    llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath);1333    StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle);1334 1335    SmallString<128> NewPath(Dir);1336    llvm::sys::path::append(NewPath, DirStyle, File);1337 1338    CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type());1339  }1340 1341  std::error_code increment() override {1342    std::error_code EC;1343    ExternalIter.increment(EC);1344    if (!EC && ExternalIter != llvm::vfs::directory_iterator())1345      setCurrentEntry();1346    else1347      CurrentEntry = directory_entry();1348    return EC;1349  }1350};1351} // namespace1352 1353llvm::ErrorOr<std::string>1354RedirectingFileSystem::getCurrentWorkingDirectory() const {1355  return WorkingDirectory;1356}1357 1358std::error_code1359RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) {1360  // Don't change the working directory if the path doesn't exist.1361  if (!exists(Path))1362    return errc::no_such_file_or_directory;1363 1364  SmallString<128> AbsolutePath;1365  Path.toVector(AbsolutePath);1366  if (std::error_code EC = makeAbsolute(AbsolutePath))1367    return EC;1368  WorkingDirectory = std::string(AbsolutePath);1369  return {};1370}1371 1372std::error_code RedirectingFileSystem::isLocal(const Twine &Path_,1373                                               bool &Result) {1374  SmallString<256> Path;1375  Path_.toVector(Path);1376 1377  if (makeAbsolute(Path))1378    return {};1379 1380  return ExternalFS->isLocal(Path, Result);1381}1382 1383std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {1384  // is_absolute(..., Style::windows_*) accepts paths with both slash types.1385  if (llvm::sys::path::is_absolute(Path, llvm::sys::path::Style::posix) ||1386      llvm::sys::path::is_absolute(Path,1387                                   llvm::sys::path::Style::windows_backslash))1388    // This covers windows absolute path with forward slash as well, as the1389    // forward slashes are treated as path separation in llvm::path1390    // regardless of what path::Style is used.1391    return {};1392 1393  auto WorkingDir = getCurrentWorkingDirectory();1394  if (!WorkingDir)1395    return WorkingDir.getError();1396 1397  return makeAbsolute(WorkingDir.get(), Path);1398}1399 1400std::error_code1401RedirectingFileSystem::makeAbsolute(StringRef WorkingDir,1402                                    SmallVectorImpl<char> &Path) const {1403  // We can't use sys::fs::make_absolute because that assumes the path style1404  // is native and there is no way to override that.  Since we know WorkingDir1405  // is absolute, we can use it to determine which style we actually have and1406  // append Path ourselves.1407  if (!WorkingDir.empty() &&1408      !sys::path::is_absolute(WorkingDir, sys::path::Style::posix) &&1409      !sys::path::is_absolute(WorkingDir,1410                              sys::path::Style::windows_backslash)) {1411    return std::error_code();1412  }1413  sys::path::Style style = sys::path::Style::windows_backslash;1414  if (sys::path::is_absolute(WorkingDir, sys::path::Style::posix)) {1415    style = sys::path::Style::posix;1416  } else {1417    // Distinguish between windows_backslash and windows_slash; getExistingStyle1418    // returns posix for a path with windows_slash.1419    if (getExistingStyle(WorkingDir) != sys::path::Style::windows_backslash)1420      style = sys::path::Style::windows_slash;1421  }1422 1423  std::string Result = std::string(WorkingDir);1424  StringRef Dir(Result);1425  if (!Dir.ends_with(sys::path::get_separator(style))) {1426    Result += sys::path::get_separator(style);1427  }1428  // backslashes '\' are legit path charactors under POSIX. Windows APIs1429  // like CreateFile accepts forward slashes '/' as path1430  // separator (even when mixed with backslashes). Therefore,1431  // `Path` should be directly appended to `WorkingDir` without converting1432  // path separator.1433  Result.append(Path.data(), Path.size());1434  Path.assign(Result.begin(), Result.end());1435 1436  return {};1437}1438 1439directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,1440                                                    std::error_code &EC) {1441  SmallString<256> Path;1442  Dir.toVector(Path);1443 1444  EC = makeAbsolute(Path);1445  if (EC)1446    return {};1447 1448  ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);1449  if (!Result) {1450    if (Redirection != RedirectKind::RedirectOnly &&1451        isFileNotFound(Result.getError()))1452      return ExternalFS->dir_begin(Path, EC);1453 1454    EC = Result.getError();1455    return {};1456  }1457 1458  // Use status to make sure the path exists and refers to a directory.1459  ErrorOr<Status> S = status(Path, Dir, *Result);1460  if (!S) {1461    if (Redirection != RedirectKind::RedirectOnly &&1462        isFileNotFound(S.getError(), Result->E))1463      return ExternalFS->dir_begin(Dir, EC);1464 1465    EC = S.getError();1466    return {};1467  }1468 1469  if (!S->isDirectory()) {1470    EC = errc::not_a_directory;1471    return {};1472  }1473 1474  // Create the appropriate directory iterator based on whether we found a1475  // DirectoryRemapEntry or DirectoryEntry.1476  directory_iterator RedirectIter;1477  std::error_code RedirectEC;1478  if (auto ExtRedirect = Result->getExternalRedirect()) {1479    auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);1480    RedirectIter = ExternalFS->dir_begin(*ExtRedirect, RedirectEC);1481 1482    if (!RE->useExternalName(UseExternalNames)) {1483      // Update the paths in the results to use the virtual directory's path.1484      RedirectIter =1485          directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>(1486              std::string(Path), RedirectIter));1487    }1488  } else {1489    auto DE = cast<DirectoryEntry>(Result->E);1490    RedirectIter =1491        directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(1492            Path, DE->contents_begin(), DE->contents_end(), RedirectEC));1493  }1494 1495  if (RedirectEC) {1496    if (RedirectEC != errc::no_such_file_or_directory) {1497      EC = RedirectEC;1498      return {};1499    }1500    RedirectIter = {};1501  }1502 1503  if (Redirection == RedirectKind::RedirectOnly) {1504    EC = RedirectEC;1505    return RedirectIter;1506  }1507 1508  std::error_code ExternalEC;1509  directory_iterator ExternalIter = ExternalFS->dir_begin(Path, ExternalEC);1510  if (ExternalEC) {1511    if (ExternalEC != errc::no_such_file_or_directory) {1512      EC = ExternalEC;1513      return {};1514    }1515    ExternalIter = {};1516  }1517 1518  SmallVector<directory_iterator, 2> Iters;1519  switch (Redirection) {1520  case RedirectKind::Fallthrough:1521    Iters.push_back(ExternalIter);1522    Iters.push_back(RedirectIter);1523    break;1524  case RedirectKind::Fallback:1525    Iters.push_back(RedirectIter);1526    Iters.push_back(ExternalIter);1527    break;1528  default:1529    llvm_unreachable("unhandled RedirectKind");1530  }1531 1532  directory_iterator Combined{1533      std::make_shared<CombiningDirIterImpl>(Iters, EC)};1534  if (EC)1535    return {};1536  return Combined;1537}1538 1539void RedirectingFileSystem::setOverlayFileDir(StringRef Dir) {1540  OverlayFileDir = Dir.str();1541}1542 1543StringRef RedirectingFileSystem::getOverlayFileDir() const {1544  return OverlayFileDir;1545}1546 1547void RedirectingFileSystem::setFallthrough(bool Fallthrough) {1548  if (Fallthrough) {1549    Redirection = RedirectingFileSystem::RedirectKind::Fallthrough;1550  } else {1551    Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly;1552  }1553}1554 1555void RedirectingFileSystem::setRedirection(1556    RedirectingFileSystem::RedirectKind Kind) {1557  Redirection = Kind;1558}1559 1560std::vector<StringRef> RedirectingFileSystem::getRoots() const {1561  std::vector<StringRef> R;1562  R.reserve(Roots.size());1563  for (const auto &Root : Roots)1564    R.push_back(Root->getName());1565  return R;1566}1567 1568void RedirectingFileSystem::printImpl(raw_ostream &OS, PrintType Type,1569                                      unsigned IndentLevel) const {1570  printIndent(OS, IndentLevel);1571  OS << "RedirectingFileSystem (UseExternalNames: "1572     << (UseExternalNames ? "true" : "false") << ")\n";1573  if (Type == PrintType::Summary)1574    return;1575 1576  for (const auto &Root : Roots)1577    printEntry(OS, Root.get(), IndentLevel);1578 1579  printIndent(OS, IndentLevel);1580  OS << "ExternalFS:\n";1581  ExternalFS->print(OS, Type == PrintType::Contents ? PrintType::Summary : Type,1582                    IndentLevel + 1);1583}1584 1585void RedirectingFileSystem::printEntry(raw_ostream &OS,1586                                       RedirectingFileSystem::Entry *E,1587                                       unsigned IndentLevel) const {1588  printIndent(OS, IndentLevel);1589  OS << "'" << E->getName() << "'";1590 1591  switch (E->getKind()) {1592  case EK_Directory: {1593    auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(E);1594 1595    OS << "\n";1596    for (std::unique_ptr<Entry> &SubEntry :1597         llvm::make_range(DE->contents_begin(), DE->contents_end()))1598      printEntry(OS, SubEntry.get(), IndentLevel + 1);1599    break;1600  }1601  case EK_DirectoryRemap:1602  case EK_File: {1603    auto *RE = cast<RedirectingFileSystem::RemapEntry>(E);1604    OS << " -> '" << RE->getExternalContentsPath() << "'";1605    switch (RE->getUseName()) {1606    case NK_NotSet:1607      break;1608    case NK_External:1609      OS << " (UseExternalName: true)";1610      break;1611    case NK_Virtual:1612      OS << " (UseExternalName: false)";1613      break;1614    }1615    OS << "\n";1616    break;1617  }1618  }1619}1620 1621void RedirectingFileSystem::visitChildFileSystems(VisitCallbackTy Callback) {1622  if (ExternalFS) {1623    Callback(*ExternalFS);1624    ExternalFS->visitChildFileSystems(Callback);1625  }1626}1627 1628/// A helper class to hold the common YAML parsing state.1629class llvm::vfs::RedirectingFileSystemParser {1630  yaml::Stream &Stream;1631 1632  void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); }1633 1634  // false on error1635  bool parseScalarString(yaml::Node *N, StringRef &Result,1636                         SmallVectorImpl<char> &Storage) {1637    const auto *S = dyn_cast<yaml::ScalarNode>(N);1638 1639    if (!S) {1640      error(N, "expected string");1641      return false;1642    }1643    Result = S->getValue(Storage);1644    return true;1645  }1646 1647  // false on error1648  bool parseScalarBool(yaml::Node *N, bool &Result) {1649    SmallString<5> Storage;1650    StringRef Value;1651    if (!parseScalarString(N, Value, Storage))1652      return false;1653 1654    if (Value.equals_insensitive("true") || Value.equals_insensitive("on") ||1655        Value.equals_insensitive("yes") || Value == "1") {1656      Result = true;1657      return true;1658    } else if (Value.equals_insensitive("false") ||1659               Value.equals_insensitive("off") ||1660               Value.equals_insensitive("no") || Value == "0") {1661      Result = false;1662      return true;1663    }1664 1665    error(N, "expected boolean value");1666    return false;1667  }1668 1669  std::optional<RedirectingFileSystem::RedirectKind>1670  parseRedirectKind(yaml::Node *N) {1671    SmallString<12> Storage;1672    StringRef Value;1673    if (!parseScalarString(N, Value, Storage))1674      return std::nullopt;1675 1676    if (Value.equals_insensitive("fallthrough")) {1677      return RedirectingFileSystem::RedirectKind::Fallthrough;1678    } else if (Value.equals_insensitive("fallback")) {1679      return RedirectingFileSystem::RedirectKind::Fallback;1680    } else if (Value.equals_insensitive("redirect-only")) {1681      return RedirectingFileSystem::RedirectKind::RedirectOnly;1682    }1683    return std::nullopt;1684  }1685 1686  std::optional<RedirectingFileSystem::RootRelativeKind>1687  parseRootRelativeKind(yaml::Node *N) {1688    SmallString<12> Storage;1689    StringRef Value;1690    if (!parseScalarString(N, Value, Storage))1691      return std::nullopt;1692    if (Value.equals_insensitive("cwd")) {1693      return RedirectingFileSystem::RootRelativeKind::CWD;1694    } else if (Value.equals_insensitive("overlay-dir")) {1695      return RedirectingFileSystem::RootRelativeKind::OverlayDir;1696    }1697    return std::nullopt;1698  }1699 1700  struct KeyStatus {1701    bool Required;1702    bool Seen = false;1703 1704    KeyStatus(bool Required = false) : Required(Required) {}1705  };1706 1707  using KeyStatusPair = std::pair<StringRef, KeyStatus>;1708 1709  // false on error1710  bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,1711                                  DenseMap<StringRef, KeyStatus> &Keys) {1712    auto It = Keys.find(Key);1713    if (It == Keys.end()) {1714      error(KeyNode, "unknown key");1715      return false;1716    }1717    KeyStatus &S = It->second;1718    if (S.Seen) {1719      error(KeyNode, Twine("duplicate key '") + Key + "'");1720      return false;1721    }1722    S.Seen = true;1723    return true;1724  }1725 1726  // false on error1727  bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {1728    for (const auto &I : Keys) {1729      if (I.second.Required && !I.second.Seen) {1730        error(Obj, Twine("missing key '") + I.first + "'");1731        return false;1732      }1733    }1734    return true;1735  }1736 1737public:1738  static RedirectingFileSystem::Entry *1739  lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,1740                      RedirectingFileSystem::Entry *ParentEntry = nullptr) {1741    if (!ParentEntry) { // Look for a existent root1742      for (const auto &Root : FS->Roots) {1743        if (Name == Root->getName()) {1744          ParentEntry = Root.get();1745          return ParentEntry;1746        }1747      }1748    } else { // Advance to the next component1749      auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);1750      for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :1751           llvm::make_range(DE->contents_begin(), DE->contents_end())) {1752        auto *DirContent =1753            dyn_cast<RedirectingFileSystem::DirectoryEntry>(Content.get());1754        if (DirContent && Name == Content->getName())1755          return DirContent;1756      }1757    }1758 1759    // ... or create a new one1760    std::unique_ptr<RedirectingFileSystem::Entry> E =1761        std::make_unique<RedirectingFileSystem::DirectoryEntry>(1762            Name, Status("", getNextVirtualUniqueID(),1763                         std::chrono::system_clock::now(), 0, 0, 0,1764                         file_type::directory_file, sys::fs::all_all));1765 1766    if (!ParentEntry) { // Add a new root to the overlay1767      FS->Roots.push_back(std::move(E));1768      ParentEntry = FS->Roots.back().get();1769      return ParentEntry;1770    }1771 1772    auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);1773    DE->addContent(std::move(E));1774    return DE->getLastContent();1775  }1776 1777private:1778  void uniqueOverlayTree(RedirectingFileSystem *FS,1779                         RedirectingFileSystem::Entry *SrcE,1780                         RedirectingFileSystem::Entry *NewParentE = nullptr) {1781    StringRef Name = SrcE->getName();1782    switch (SrcE->getKind()) {1783    case RedirectingFileSystem::EK_Directory: {1784      auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(SrcE);1785      // Empty directories could be present in the YAML as a way to1786      // describe a file for a current directory after some of its subdir1787      // is parsed. This only leads to redundant walks, ignore it.1788      if (!Name.empty())1789        NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);1790      for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :1791           llvm::make_range(DE->contents_begin(), DE->contents_end()))1792        uniqueOverlayTree(FS, SubEntry.get(), NewParentE);1793      break;1794    }1795    case RedirectingFileSystem::EK_DirectoryRemap: {1796      assert(NewParentE && "Parent entry must exist");1797      auto *DR = cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);1798      auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);1799      DE->addContent(1800          std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(1801              Name, DR->getExternalContentsPath(), DR->getUseName()));1802      break;1803    }1804    case RedirectingFileSystem::EK_File: {1805      assert(NewParentE && "Parent entry must exist");1806      auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);1807      auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);1808      DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>(1809          Name, FE->getExternalContentsPath(), FE->getUseName()));1810      break;1811    }1812    }1813  }1814 1815  std::unique_ptr<RedirectingFileSystem::Entry>1816  parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) {1817    auto *M = dyn_cast<yaml::MappingNode>(N);1818    if (!M) {1819      error(N, "expected mapping node for file or directory entry");1820      return nullptr;1821    }1822 1823    KeyStatusPair Fields[] = {1824        KeyStatusPair("name", true),1825        KeyStatusPair("type", true),1826        KeyStatusPair("contents", false),1827        KeyStatusPair("external-contents", false),1828        KeyStatusPair("use-external-name", false),1829    };1830 1831    DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));1832 1833    enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;1834    std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>1835        EntryArrayContents;1836    SmallString<256> ExternalContentsPath;1837    SmallString<256> Name;1838    yaml::Node *NameValueNode = nullptr;1839    auto UseExternalName = RedirectingFileSystem::NK_NotSet;1840    RedirectingFileSystem::EntryKind Kind;1841 1842    for (auto &I : *M) {1843      StringRef Key;1844      // Reuse the buffer for key and value, since we don't look at key after1845      // parsing value.1846      SmallString<256> Buffer;1847      if (!parseScalarString(I.getKey(), Key, Buffer))1848        return nullptr;1849 1850      if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))1851        return nullptr;1852 1853      StringRef Value;1854      if (Key == "name") {1855        if (!parseScalarString(I.getValue(), Value, Buffer))1856          return nullptr;1857 1858        NameValueNode = I.getValue();1859        // Guarantee that old YAML files containing paths with ".." and "."1860        // are properly canonicalized before read into the VFS.1861        Name = canonicalize(Value).str();1862      } else if (Key == "type") {1863        if (!parseScalarString(I.getValue(), Value, Buffer))1864          return nullptr;1865        if (Value == "file")1866          Kind = RedirectingFileSystem::EK_File;1867        else if (Value == "directory")1868          Kind = RedirectingFileSystem::EK_Directory;1869        else if (Value == "directory-remap")1870          Kind = RedirectingFileSystem::EK_DirectoryRemap;1871        else {1872          error(I.getValue(), "unknown value for 'type'");1873          return nullptr;1874        }1875      } else if (Key == "contents") {1876        if (ContentsField != CF_NotSet) {1877          error(I.getKey(),1878                "entry already has 'contents' or 'external-contents'");1879          return nullptr;1880        }1881        ContentsField = CF_List;1882        auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());1883        if (!Contents) {1884          // FIXME: this is only for directories, what about files?1885          error(I.getValue(), "expected array");1886          return nullptr;1887        }1888 1889        for (auto &I : *Contents) {1890          if (std::unique_ptr<RedirectingFileSystem::Entry> E =1891                  parseEntry(&I, FS, /*IsRootEntry*/ false))1892            EntryArrayContents.push_back(std::move(E));1893          else1894            return nullptr;1895        }1896      } else if (Key == "external-contents") {1897        if (ContentsField != CF_NotSet) {1898          error(I.getKey(),1899                "entry already has 'contents' or 'external-contents'");1900          return nullptr;1901        }1902        ContentsField = CF_External;1903        if (!parseScalarString(I.getValue(), Value, Buffer))1904          return nullptr;1905 1906        SmallString<256> FullPath;1907        if (FS->IsRelativeOverlay) {1908          FullPath = FS->getOverlayFileDir();1909          assert(!FullPath.empty() &&1910                 "External contents prefix directory must exist");1911          SmallString<256> AbsFullPath = Value;1912          if (FS->makeAbsolute(FullPath, AbsFullPath)) {1913            error(N, "failed to make 'external-contents' absolute");1914            return nullptr;1915          }1916          FullPath = AbsFullPath;1917        } else {1918          FullPath = Value;1919        }1920 1921        // Guarantee that old YAML files containing paths with ".." and "."1922        // are properly canonicalized before read into the VFS.1923        FullPath = canonicalize(FullPath);1924        ExternalContentsPath = FullPath.str();1925      } else if (Key == "use-external-name") {1926        bool Val;1927        if (!parseScalarBool(I.getValue(), Val))1928          return nullptr;1929        UseExternalName = Val ? RedirectingFileSystem::NK_External1930                              : RedirectingFileSystem::NK_Virtual;1931      } else {1932        llvm_unreachable("key missing from Keys");1933      }1934    }1935 1936    if (Stream.failed())1937      return nullptr;1938 1939    // check for missing keys1940    if (ContentsField == CF_NotSet) {1941      error(N, "missing key 'contents' or 'external-contents'");1942      return nullptr;1943    }1944    if (!checkMissingKeys(N, Keys))1945      return nullptr;1946 1947    // check invalid configuration1948    if (Kind == RedirectingFileSystem::EK_Directory &&1949        UseExternalName != RedirectingFileSystem::NK_NotSet) {1950      error(N, "'use-external-name' is not supported for 'directory' entries");1951      return nullptr;1952    }1953 1954    if (Kind == RedirectingFileSystem::EK_DirectoryRemap &&1955        ContentsField == CF_List) {1956      error(N, "'contents' is not supported for 'directory-remap' entries");1957      return nullptr;1958    }1959 1960    sys::path::Style path_style = sys::path::Style::native;1961    if (IsRootEntry) {1962      // VFS root entries may be in either Posix or Windows style.  Figure out1963      // which style we have, and use it consistently.1964      if (sys::path::is_absolute(Name, sys::path::Style::posix)) {1965        path_style = sys::path::Style::posix;1966      } else if (sys::path::is_absolute(Name,1967                                        sys::path::Style::windows_backslash)) {1968        path_style = sys::path::Style::windows_backslash;1969      } else {1970        // Relative VFS root entries are made absolute to either the overlay1971        // directory, or the current working directory, then we can determine1972        // the path style from that.1973        std::error_code EC;1974        if (FS->RootRelative ==1975            RedirectingFileSystem::RootRelativeKind::OverlayDir) {1976          StringRef FullPath = FS->getOverlayFileDir();1977          assert(!FullPath.empty() && "Overlay file directory must exist");1978          EC = FS->makeAbsolute(FullPath, Name);1979          Name = canonicalize(Name);1980        } else {1981          EC = FS->makeAbsolute(Name);1982        }1983        if (EC) {1984          assert(NameValueNode && "Name presence should be checked earlier");1985          error(1986              NameValueNode,1987              "entry with relative path at the root level is not discoverable");1988          return nullptr;1989        }1990        path_style = sys::path::is_absolute(Name, sys::path::Style::posix)1991                         ? sys::path::Style::posix1992                         : sys::path::Style::windows_backslash;1993      }1994      // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will1995      // return true even if `Name` is using forward slashes. Distinguish1996      // between windows_backslash and windows_slash.1997      if (path_style == sys::path::Style::windows_backslash &&1998          getExistingStyle(Name) != sys::path::Style::windows_backslash)1999        path_style = sys::path::Style::windows_slash;2000    }2001 2002    // Remove trailing slash(es), being careful not to remove the root path2003    StringRef Trimmed = Name;2004    size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();2005    while (Trimmed.size() > RootPathLen &&2006           sys::path::is_separator(Trimmed.back(), path_style))2007      Trimmed = Trimmed.slice(0, Trimmed.size() - 1);2008 2009    // Get the last component2010    StringRef LastComponent = sys::path::filename(Trimmed, path_style);2011 2012    std::unique_ptr<RedirectingFileSystem::Entry> Result;2013    switch (Kind) {2014    case RedirectingFileSystem::EK_File:2015      Result = std::make_unique<RedirectingFileSystem::FileEntry>(2016          LastComponent, std::move(ExternalContentsPath), UseExternalName);2017      break;2018    case RedirectingFileSystem::EK_DirectoryRemap:2019      Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(2020          LastComponent, std::move(ExternalContentsPath), UseExternalName);2021      break;2022    case RedirectingFileSystem::EK_Directory:2023      Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(2024          LastComponent, std::move(EntryArrayContents),2025          Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),2026                 0, 0, 0, file_type::directory_file, sys::fs::all_all));2027      break;2028    }2029 2030    StringRef Parent = sys::path::parent_path(Trimmed, path_style);2031    if (Parent.empty())2032      return Result;2033 2034    // if 'name' contains multiple components, create implicit directory entries2035    for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style),2036                                     E = sys::path::rend(Parent);2037         I != E; ++I) {2038      std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;2039      Entries.push_back(std::move(Result));2040      Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(2041          *I, std::move(Entries),2042          Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),2043                 0, 0, 0, file_type::directory_file, sys::fs::all_all));2044    }2045    return Result;2046  }2047 2048public:2049  RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}2050 2051  // false on error2052  bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {2053    auto *Top = dyn_cast<yaml::MappingNode>(Root);2054    if (!Top) {2055      error(Root, "expected mapping node");2056      return false;2057    }2058 2059    KeyStatusPair Fields[] = {2060        KeyStatusPair("version", true),2061        KeyStatusPair("case-sensitive", false),2062        KeyStatusPair("use-external-names", false),2063        KeyStatusPair("root-relative", false),2064        KeyStatusPair("overlay-relative", false),2065        KeyStatusPair("fallthrough", false),2066        KeyStatusPair("redirecting-with", false),2067        KeyStatusPair("roots", true),2068    };2069 2070    DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));2071    std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries;2072 2073    // Parse configuration and 'roots'2074    for (auto &I : *Top) {2075      SmallString<10> KeyBuffer;2076      StringRef Key;2077      if (!parseScalarString(I.getKey(), Key, KeyBuffer))2078        return false;2079 2080      if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))2081        return false;2082 2083      if (Key == "roots") {2084        auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue());2085        if (!Roots) {2086          error(I.getValue(), "expected array");2087          return false;2088        }2089 2090        for (auto &I : *Roots) {2091          if (std::unique_ptr<RedirectingFileSystem::Entry> E =2092                  parseEntry(&I, FS, /*IsRootEntry*/ true))2093            RootEntries.push_back(std::move(E));2094          else2095            return false;2096        }2097      } else if (Key == "version") {2098        StringRef VersionString;2099        SmallString<4> Storage;2100        if (!parseScalarString(I.getValue(), VersionString, Storage))2101          return false;2102        int Version;2103        if (VersionString.getAsInteger<int>(10, Version)) {2104          error(I.getValue(), "expected integer");2105          return false;2106        }2107        if (Version < 0) {2108          error(I.getValue(), "invalid version number");2109          return false;2110        }2111        if (Version != 0) {2112          error(I.getValue(), "version mismatch, expected 0");2113          return false;2114        }2115      } else if (Key == "case-sensitive") {2116        if (!parseScalarBool(I.getValue(), FS->CaseSensitive))2117          return false;2118      } else if (Key == "overlay-relative") {2119        if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay))2120          return false;2121      } else if (Key == "use-external-names") {2122        if (!parseScalarBool(I.getValue(), FS->UseExternalNames))2123          return false;2124      } else if (Key == "fallthrough") {2125        if (Keys["redirecting-with"].Seen) {2126          error(I.getValue(),2127                "'fallthrough' and 'redirecting-with' are mutually exclusive");2128          return false;2129        }2130 2131        bool ShouldFallthrough = false;2132        if (!parseScalarBool(I.getValue(), ShouldFallthrough))2133          return false;2134 2135        if (ShouldFallthrough) {2136          FS->Redirection = RedirectingFileSystem::RedirectKind::Fallthrough;2137        } else {2138          FS->Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly;2139        }2140      } else if (Key == "redirecting-with") {2141        if (Keys["fallthrough"].Seen) {2142          error(I.getValue(),2143                "'fallthrough' and 'redirecting-with' are mutually exclusive");2144          return false;2145        }2146 2147        if (auto Kind = parseRedirectKind(I.getValue())) {2148          FS->Redirection = *Kind;2149        } else {2150          error(I.getValue(), "expected valid redirect kind");2151          return false;2152        }2153      } else if (Key == "root-relative") {2154        if (auto Kind = parseRootRelativeKind(I.getValue())) {2155          FS->RootRelative = *Kind;2156        } else {2157          error(I.getValue(), "expected valid root-relative kind");2158          return false;2159        }2160      } else {2161        llvm_unreachable("key missing from Keys");2162      }2163    }2164 2165    if (Stream.failed())2166      return false;2167 2168    if (!checkMissingKeys(Top, Keys))2169      return false;2170 2171    // Now that we sucessefully parsed the YAML file, canonicalize the internal2172    // representation to a proper directory tree so that we can search faster2173    // inside the VFS.2174    for (auto &E : RootEntries)2175      uniqueOverlayTree(FS, E.get());2176 2177    return true;2178  }2179};2180 2181std::unique_ptr<RedirectingFileSystem>2182RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,2183                              SourceMgr::DiagHandlerTy DiagHandler,2184                              StringRef YAMLFilePath, void *DiagContext,2185                              IntrusiveRefCntPtr<FileSystem> ExternalFS) {2186  SourceMgr SM;2187  yaml::Stream Stream(Buffer->getMemBufferRef(), SM);2188 2189  SM.setDiagHandler(DiagHandler, DiagContext);2190  yaml::document_iterator DI = Stream.begin();2191  yaml::Node *Root = DI->getRoot();2192  if (DI == Stream.end() || !Root) {2193    SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");2194    return nullptr;2195  }2196 2197  RedirectingFileSystemParser P(Stream);2198 2199  std::unique_ptr<RedirectingFileSystem> FS(2200      new RedirectingFileSystem(ExternalFS));2201 2202  if (!YAMLFilePath.empty()) {2203    // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed2204    // to each 'external-contents' path.2205    //2206    // Example:2207    //    -ivfsoverlay dummy.cache/vfs/vfs.yaml2208    // yields:2209    //  FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs2210    //2211    SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);2212    std::error_code EC = FS->makeAbsolute(OverlayAbsDir);2213    assert(!EC && "Overlay dir final path must be absolute");2214    (void)EC;2215    FS->setOverlayFileDir(OverlayAbsDir);2216  }2217 2218  if (!P.parse(Root, FS.get()))2219    return nullptr;2220 2221  return FS;2222}2223 2224std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(2225    ArrayRef<std::pair<std::string, std::string>> RemappedFiles,2226    bool UseExternalNames, llvm::IntrusiveRefCntPtr<FileSystem> ExternalFS) {2227  std::unique_ptr<RedirectingFileSystem> FS(2228      new RedirectingFileSystem(ExternalFS));2229  FS->UseExternalNames = UseExternalNames;2230 2231  StringMap<RedirectingFileSystem::Entry *> Entries;2232 2233  for (auto &Mapping : llvm::reverse(RemappedFiles)) {2234    SmallString<128> From = StringRef(Mapping.first);2235    SmallString<128> To = StringRef(Mapping.second);2236    {2237      auto EC = ExternalFS->makeAbsolute(From);2238      (void)EC;2239      assert(!EC && "Could not make absolute path");2240    }2241 2242    // Check if we've already mapped this file. The first one we see (in the2243    // reverse iteration) wins.2244    RedirectingFileSystem::Entry *&ToEntry = Entries[From];2245    if (ToEntry)2246      continue;2247 2248    // Add parent directories.2249    RedirectingFileSystem::Entry *Parent = nullptr;2250    StringRef FromDirectory = llvm::sys::path::parent_path(From);2251    for (auto I = llvm::sys::path::begin(FromDirectory),2252              E = llvm::sys::path::end(FromDirectory);2253         I != E; ++I) {2254      Parent = RedirectingFileSystemParser::lookupOrCreateEntry(FS.get(), *I,2255                                                                Parent);2256    }2257    assert(Parent && "File without a directory?");2258    {2259      auto EC = ExternalFS->makeAbsolute(To);2260      (void)EC;2261      assert(!EC && "Could not make absolute path");2262    }2263 2264    // Add the file.2265    auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(2266        llvm::sys::path::filename(From), To,2267        UseExternalNames ? RedirectingFileSystem::NK_External2268                         : RedirectingFileSystem::NK_Virtual);2269    ToEntry = NewFile.get();2270    cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(2271        std::move(NewFile));2272  }2273 2274  return FS;2275}2276 2277RedirectingFileSystem::LookupResult::LookupResult(2278    Entry *E, sys::path::const_iterator Start, sys::path::const_iterator End)2279    : E(E) {2280  assert(E != nullptr);2281  // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the2282  // path of the directory it maps to in the external file system plus any2283  // remaining path components in the provided iterator.2284  if (auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(E)) {2285    SmallString<256> Redirect(DRE->getExternalContentsPath());2286    sys::path::append(Redirect, Start, End,2287                      getExistingStyle(DRE->getExternalContentsPath()));2288    ExternalRedirect = std::string(Redirect);2289  }2290}2291 2292void RedirectingFileSystem::LookupResult::getPath(2293    llvm::SmallVectorImpl<char> &Result) const {2294  Result.clear();2295  for (Entry *Parent : Parents)2296    llvm::sys::path::append(Result, Parent->getName());2297  llvm::sys::path::append(Result, E->getName());2298}2299 2300std::error_code RedirectingFileSystem::makeCanonicalForLookup(2301    SmallVectorImpl<char> &Path) const {2302  if (std::error_code EC = makeAbsolute(Path))2303    return EC;2304 2305  llvm::SmallString<256> CanonicalPath =2306      canonicalize(StringRef(Path.data(), Path.size()));2307  if (CanonicalPath.empty())2308    return make_error_code(llvm::errc::invalid_argument);2309 2310  Path.assign(CanonicalPath.begin(), CanonicalPath.end());2311  return {};2312}2313 2314ErrorOr<RedirectingFileSystem::LookupResult>2315RedirectingFileSystem::lookupPath(StringRef Path) const {2316  llvm::SmallString<128> CanonicalPath(Path);2317  if (std::error_code EC = makeCanonicalForLookup(CanonicalPath))2318    return EC;2319 2320  // RedirectOnly means the VFS is always used.2321  if (UsageTrackingActive && Redirection == RedirectKind::RedirectOnly)2322    HasBeenUsed = true;2323 2324  sys::path::const_iterator Start = sys::path::begin(CanonicalPath);2325  sys::path::const_iterator End = sys::path::end(CanonicalPath);2326  llvm::SmallVector<Entry *, 32> Entries;2327  for (const auto &Root : Roots) {2328    ErrorOr<RedirectingFileSystem::LookupResult> Result =2329        lookupPathImpl(Start, End, Root.get(), Entries);2330    if (UsageTrackingActive && Result && isa<RemapEntry>(Result->E))2331      HasBeenUsed = true;2332    if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) {2333      Result->Parents = std::move(Entries);2334      return Result;2335    }2336  }2337  return make_error_code(llvm::errc::no_such_file_or_directory);2338}2339 2340ErrorOr<RedirectingFileSystem::LookupResult>2341RedirectingFileSystem::lookupPathImpl(2342    sys::path::const_iterator Start, sys::path::const_iterator End,2343    RedirectingFileSystem::Entry *From,2344    llvm::SmallVectorImpl<Entry *> &Entries) const {2345  assert(!isTraversalComponent(*Start) &&2346         !isTraversalComponent(From->getName()) &&2347         "Paths should not contain traversal components");2348 2349  StringRef FromName = From->getName();2350 2351  // Forward the search to the next component in case this is an empty one.2352  if (!FromName.empty()) {2353    if (!pathComponentMatches(*Start, FromName))2354      return make_error_code(llvm::errc::no_such_file_or_directory);2355 2356    ++Start;2357 2358    if (Start == End) {2359      // Match!2360      return LookupResult(From, Start, End);2361    }2362  }2363 2364  if (isa<RedirectingFileSystem::FileEntry>(From))2365    return make_error_code(llvm::errc::not_a_directory);2366 2367  if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From))2368    return LookupResult(From, Start, End);2369 2370  auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From);2371  for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :2372       llvm::make_range(DE->contents_begin(), DE->contents_end())) {2373    Entries.push_back(From);2374    ErrorOr<RedirectingFileSystem::LookupResult> Result =2375        lookupPathImpl(Start, End, DirEntry.get(), Entries);2376    if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)2377      return Result;2378    Entries.pop_back();2379  }2380 2381  return make_error_code(llvm::errc::no_such_file_or_directory);2382}2383 2384static Status getRedirectedFileStatus(const Twine &OriginalPath,2385                                      bool UseExternalNames,2386                                      Status ExternalStatus) {2387  // The path has been mapped by some nested VFS and exposes an external path,2388  // don't override it with the original path.2389  if (ExternalStatus.ExposesExternalVFSPath)2390    return ExternalStatus;2391 2392  Status S = ExternalStatus;2393  if (!UseExternalNames)2394    S = Status::copyWithNewName(S, OriginalPath);2395  else2396    S.ExposesExternalVFSPath = true;2397  return S;2398}2399 2400ErrorOr<Status> RedirectingFileSystem::status(2401    const Twine &LookupPath, const Twine &OriginalPath,2402    const RedirectingFileSystem::LookupResult &Result) {2403  if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {2404    SmallString<256> RemappedPath((*ExtRedirect).str());2405    if (std::error_code EC = makeAbsolute(RemappedPath))2406      return EC;2407 2408    ErrorOr<Status> S = ExternalFS->status(RemappedPath);2409    if (!S)2410      return S;2411    S = Status::copyWithNewName(*S, *ExtRedirect);2412    auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E);2413    return getRedirectedFileStatus(OriginalPath,2414                                   RE->useExternalName(UseExternalNames), *S);2415  }2416 2417  auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);2418  return Status::copyWithNewName(DE->getStatus(), LookupPath);2419}2420 2421ErrorOr<Status>2422RedirectingFileSystem::getExternalStatus(const Twine &LookupPath,2423                                         const Twine &OriginalPath) const {2424  auto Result = ExternalFS->status(LookupPath);2425 2426  // The path has been mapped by some nested VFS, don't override it with the2427  // original path.2428  if (!Result || Result->ExposesExternalVFSPath)2429    return Result;2430  return Status::copyWithNewName(Result.get(), OriginalPath);2431}2432 2433ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {2434  SmallString<256> Path;2435  OriginalPath.toVector(Path);2436 2437  if (std::error_code EC = makeAbsolute(Path))2438    return EC;2439 2440  if (Redirection == RedirectKind::Fallback) {2441    // Attempt to find the original file first, only falling back to the2442    // mapped file if that fails.2443    ErrorOr<Status> S = getExternalStatus(Path, OriginalPath);2444    if (S)2445      return S;2446  }2447 2448  ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);2449  if (!Result) {2450    // Was not able to map file, fallthrough to using the original path if2451    // that was the specified redirection type.2452    if (Redirection == RedirectKind::Fallthrough &&2453        isFileNotFound(Result.getError()))2454      return getExternalStatus(Path, OriginalPath);2455    return Result.getError();2456  }2457 2458  ErrorOr<Status> S = status(Path, OriginalPath, *Result);2459  if (!S && Redirection == RedirectKind::Fallthrough &&2460      isFileNotFound(S.getError(), Result->E)) {2461    // Mapped the file but it wasn't found in the underlying filesystem,2462    // fallthrough to using the original path if that was the specified2463    // redirection type.2464    return getExternalStatus(Path, OriginalPath);2465  }2466 2467  return S;2468}2469 2470bool RedirectingFileSystem::exists(const Twine &OriginalPath) {2471  SmallString<256> Path;2472  OriginalPath.toVector(Path);2473 2474  if (makeAbsolute(Path))2475    return false;2476 2477  if (Redirection == RedirectKind::Fallback) {2478    // Attempt to find the original file first, only falling back to the2479    // mapped file if that fails.2480    if (ExternalFS->exists(Path))2481      return true;2482  }2483 2484  ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);2485  if (!Result) {2486    // Was not able to map file, fallthrough to using the original path if2487    // that was the specified redirection type.2488    if (Redirection == RedirectKind::Fallthrough &&2489        isFileNotFound(Result.getError()))2490      return ExternalFS->exists(Path);2491    return false;2492  }2493 2494  std::optional<StringRef> ExtRedirect = Result->getExternalRedirect();2495  if (!ExtRedirect) {2496    assert(isa<RedirectingFileSystem::DirectoryEntry>(Result->E));2497    return true;2498  }2499 2500  SmallString<256> RemappedPath((*ExtRedirect).str());2501  if (makeAbsolute(RemappedPath))2502    return false;2503 2504  if (ExternalFS->exists(RemappedPath))2505    return true;2506 2507  if (Redirection == RedirectKind::Fallthrough) {2508    // Mapped the file but it wasn't found in the underlying filesystem,2509    // fallthrough to using the original path if that was the specified2510    // redirection type.2511    return ExternalFS->exists(Path);2512  }2513 2514  return false;2515}2516 2517namespace {2518 2519/// Provide a file wrapper with an overriden status.2520class FileWithFixedStatus : public File {2521  std::unique_ptr<File> InnerFile;2522  Status S;2523 2524public:2525  FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)2526      : InnerFile(std::move(InnerFile)), S(std::move(S)) {}2527 2528  ErrorOr<Status> status() override { return S; }2529  ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>2530 2531  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,2532            bool IsVolatile) override {2533    return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,2534                                IsVolatile);2535  }2536 2537  std::error_code close() override { return InnerFile->close(); }2538 2539  void setPath(const Twine &Path) override { S = S.copyWithNewName(S, Path); }2540};2541 2542} // namespace2543 2544ErrorOr<std::unique_ptr<File>>2545File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {2546  // See \c getRedirectedFileStatus - don't update path if it's exposing an2547  // external path.2548  if (!Result || (*Result)->status()->ExposesExternalVFSPath)2549    return Result;2550 2551  ErrorOr<std::unique_ptr<File>> F = std::move(*Result);2552  auto Name = F->get()->getName();2553  if (Name && Name.get() != P.str())2554    F->get()->setPath(P);2555  return F;2556}2557 2558ErrorOr<std::unique_ptr<File>>2559RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) {2560  SmallString<256> Path;2561  OriginalPath.toVector(Path);2562 2563  if (std::error_code EC = makeAbsolute(Path))2564    return EC;2565 2566  if (Redirection == RedirectKind::Fallback) {2567    // Attempt to find the original file first, only falling back to the2568    // mapped file if that fails.2569    auto F = File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);2570    if (F)2571      return F;2572  }2573 2574  ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);2575  if (!Result) {2576    // Was not able to map file, fallthrough to using the original path if2577    // that was the specified redirection type.2578    if (Redirection == RedirectKind::Fallthrough &&2579        isFileNotFound(Result.getError()))2580      return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);2581    return Result.getError();2582  }2583 2584  if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?2585    return make_error_code(llvm::errc::invalid_argument);2586 2587  StringRef ExtRedirect = *Result->getExternalRedirect();2588  SmallString<256> RemappedPath(ExtRedirect.str());2589  if (std::error_code EC = makeAbsolute(RemappedPath))2590    return EC;2591 2592  auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);2593 2594  auto ExternalFile =2595      File::getWithPath(ExternalFS->openFileForRead(RemappedPath), ExtRedirect);2596  if (!ExternalFile) {2597    if (Redirection == RedirectKind::Fallthrough &&2598        isFileNotFound(ExternalFile.getError(), Result->E)) {2599      // Mapped the file but it wasn't found in the underlying filesystem,2600      // fallthrough to using the original path if that was the specified2601      // redirection type.2602      return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);2603    }2604    return ExternalFile;2605  }2606 2607  auto ExternalStatus = (*ExternalFile)->status();2608  if (!ExternalStatus)2609    return ExternalStatus.getError();2610 2611  // Otherwise, the file was successfully remapped. Mark it as such. Also2612  // replace the underlying path if the external name is being used.2613  Status S = getRedirectedFileStatus(2614      OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus);2615  return std::unique_ptr<File>(2616      std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));2617}2618 2619std::error_code2620RedirectingFileSystem::getRealPath(const Twine &OriginalPath,2621                                   SmallVectorImpl<char> &Output) {2622  SmallString<256> Path;2623  OriginalPath.toVector(Path);2624 2625  if (std::error_code EC = makeAbsolute(Path))2626    return EC;2627 2628  if (Redirection == RedirectKind::Fallback) {2629    // Attempt to find the original file first, only falling back to the2630    // mapped file if that fails.2631    std::error_code EC = ExternalFS->getRealPath(Path, Output);2632    if (!EC)2633      return EC;2634  }2635 2636  ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);2637  if (!Result) {2638    // Was not able to map file, fallthrough to using the original path if2639    // that was the specified redirection type.2640    if (Redirection == RedirectKind::Fallthrough &&2641        isFileNotFound(Result.getError()))2642      return ExternalFS->getRealPath(Path, Output);2643    return Result.getError();2644  }2645 2646  // If we found FileEntry or DirectoryRemapEntry, look up the mapped2647  // path in the external file system.2648  if (auto ExtRedirect = Result->getExternalRedirect()) {2649    auto P = ExternalFS->getRealPath(*ExtRedirect, Output);2650    if (P && Redirection == RedirectKind::Fallthrough &&2651        isFileNotFound(P, Result->E)) {2652      // Mapped the file but it wasn't found in the underlying filesystem,2653      // fallthrough to using the original path if that was the specified2654      // redirection type.2655      return ExternalFS->getRealPath(Path, Output);2656    }2657    return P;2658  }2659 2660  // We found a DirectoryEntry, which does not have a single external contents2661  // path. Use the canonical virtual path.2662  if (Redirection == RedirectKind::Fallthrough) {2663    Result->getPath(Output);2664    return {};2665  }2666  return llvm::errc::invalid_argument;2667}2668 2669std::unique_ptr<FileSystem>2670vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,2671                    SourceMgr::DiagHandlerTy DiagHandler,2672                    StringRef YAMLFilePath, void *DiagContext,2673                    IntrusiveRefCntPtr<FileSystem> ExternalFS) {2674  return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,2675                                       YAMLFilePath, DiagContext,2676                                       std::move(ExternalFS));2677}2678 2679static void getVFSEntries(RedirectingFileSystem::Entry *SrcE,2680                          SmallVectorImpl<StringRef> &Path,2681                          SmallVectorImpl<YAMLVFSEntry> &Entries) {2682  auto Kind = SrcE->getKind();2683  if (Kind == RedirectingFileSystem::EK_Directory) {2684    auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(SrcE);2685    assert(DE && "Must be a directory");2686    for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :2687         llvm::make_range(DE->contents_begin(), DE->contents_end())) {2688      Path.push_back(SubEntry->getName());2689      getVFSEntries(SubEntry.get(), Path, Entries);2690      Path.pop_back();2691    }2692    return;2693  }2694 2695  if (Kind == RedirectingFileSystem::EK_DirectoryRemap) {2696    auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);2697    assert(DR && "Must be a directory remap");2698    SmallString<128> VPath;2699    for (auto &Comp : Path)2700      llvm::sys::path::append(VPath, Comp);2701    Entries.push_back(2702        YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));2703    return;2704  }2705 2706  assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");2707  auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);2708  assert(FE && "Must be a file");2709  SmallString<128> VPath;2710  for (auto &Comp : Path)2711    llvm::sys::path::append(VPath, Comp);2712  Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));2713}2714 2715void vfs::collectVFSEntries(RedirectingFileSystem &VFS,2716                            SmallVectorImpl<YAMLVFSEntry> &CollectedEntries) {2717  ErrorOr<RedirectingFileSystem::LookupResult> RootResult = VFS.lookupPath("/");2718  if (!RootResult)2719    return;2720  SmallVector<StringRef, 8> Components;2721  Components.push_back("/");2722  getVFSEntries(RootResult->E, Components, CollectedEntries);2723}2724 2725UniqueID vfs::getNextVirtualUniqueID() {2726  static std::atomic<unsigned> UID;2727  unsigned ID = ++UID;2728  // The following assumes that uint64_t max will never collide with a real2729  // dev_t value from the OS.2730  return UniqueID(std::numeric_limits<uint64_t>::max(), ID);2731}2732 2733void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath,2734                             bool IsDirectory) {2735  assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");2736  assert(sys::path::is_absolute(RealPath) && "real path not absolute");2737  assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");2738  Mappings.emplace_back(VirtualPath, RealPath, IsDirectory);2739}2740 2741void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {2742  addEntry(VirtualPath, RealPath, /*IsDirectory=*/false);2743}2744 2745void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath,2746                                        StringRef RealPath) {2747  addEntry(VirtualPath, RealPath, /*IsDirectory=*/true);2748}2749 2750namespace {2751 2752class JSONWriter {2753  llvm::raw_ostream &OS;2754  SmallVector<StringRef, 16> DirStack;2755 2756  unsigned getDirIndent() { return 4 * DirStack.size(); }2757  unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }2758  bool containedIn(StringRef Parent, StringRef Path);2759  StringRef containedPart(StringRef Parent, StringRef Path);2760  void startDirectory(StringRef Path);2761  void endDirectory();2762  void writeEntry(StringRef VPath, StringRef RPath);2763 2764public:2765  JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}2766 2767  void write(ArrayRef<YAMLVFSEntry> Entries,2768             std::optional<bool> UseExternalNames,2769             std::optional<bool> IsCaseSensitive,2770             std::optional<bool> IsOverlayRelative, StringRef OverlayDir);2771};2772 2773} // namespace2774 2775bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {2776  using namespace llvm::sys;2777 2778  // Compare each path component.2779  auto IParent = path::begin(Parent), EParent = path::end(Parent);2780  for (auto IChild = path::begin(Path), EChild = path::end(Path);2781       IParent != EParent && IChild != EChild; ++IParent, ++IChild) {2782    if (*IParent != *IChild)2783      return false;2784  }2785  // Have we exhausted the parent path?2786  return IParent == EParent;2787}2788 2789StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {2790  assert(!Parent.empty());2791  assert(containedIn(Parent, Path));2792  return Path.substr(Parent.size() + 1);2793}2794 2795void JSONWriter::startDirectory(StringRef Path) {2796  StringRef Name =2797      DirStack.empty() ? Path : containedPart(DirStack.back(), Path);2798  DirStack.push_back(Path);2799  unsigned Indent = getDirIndent();2800  OS.indent(Indent) << "{\n";2801  OS.indent(Indent + 2) << "'type': 'directory',\n";2802  OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";2803  OS.indent(Indent + 2) << "'contents': [\n";2804}2805 2806void JSONWriter::endDirectory() {2807  unsigned Indent = getDirIndent();2808  OS.indent(Indent + 2) << "]\n";2809  OS.indent(Indent) << "}";2810 2811  DirStack.pop_back();2812}2813 2814void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {2815  unsigned Indent = getFileIndent();2816  OS.indent(Indent) << "{\n";2817  OS.indent(Indent + 2) << "'type': 'file',\n";2818  OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";2819  OS.indent(Indent + 2) << "'external-contents': \""2820                        << llvm::yaml::escape(RPath) << "\"\n";2821  OS.indent(Indent) << "}";2822}2823 2824void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,2825                       std::optional<bool> UseExternalNames,2826                       std::optional<bool> IsCaseSensitive,2827                       std::optional<bool> IsOverlayRelative,2828                       StringRef OverlayDir) {2829  using namespace llvm::sys;2830 2831  OS << "{\n"2832        "  'version': 0,\n";2833  if (IsCaseSensitive)2834    OS << "  'case-sensitive': '" << (*IsCaseSensitive ? "true" : "false")2835       << "',\n";2836  if (UseExternalNames)2837    OS << "  'use-external-names': '" << (*UseExternalNames ? "true" : "false")2838       << "',\n";2839  bool UseOverlayRelative = false;2840  if (IsOverlayRelative) {2841    UseOverlayRelative = *IsOverlayRelative;2842    OS << "  'overlay-relative': '" << (UseOverlayRelative ? "true" : "false")2843       << "',\n";2844  }2845  OS << "  'roots': [\n";2846 2847  if (!Entries.empty()) {2848    const YAMLVFSEntry &Entry = Entries.front();2849 2850    startDirectory(2851      Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath)2852    );2853 2854    StringRef RPath = Entry.RPath;2855    if (UseOverlayRelative) {2856      assert(RPath.starts_with(OverlayDir) &&2857             "Overlay dir must be contained in RPath");2858      RPath = RPath.substr(OverlayDir.size());2859    }2860 2861    bool IsCurrentDirEmpty = true;2862    if (!Entry.IsDirectory) {2863      writeEntry(path::filename(Entry.VPath), RPath);2864      IsCurrentDirEmpty = false;2865    }2866 2867    for (const auto &Entry : Entries.slice(1)) {2868      StringRef Dir =2869          Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath);2870      if (Dir == DirStack.back()) {2871        if (!IsCurrentDirEmpty) {2872          OS << ",\n";2873        }2874      } else {2875        bool IsDirPoppedFromStack = false;2876        while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {2877          OS << "\n";2878          endDirectory();2879          IsDirPoppedFromStack = true;2880        }2881        if (IsDirPoppedFromStack || !IsCurrentDirEmpty) {2882          OS << ",\n";2883        }2884        startDirectory(Dir);2885        IsCurrentDirEmpty = true;2886      }2887      StringRef RPath = Entry.RPath;2888      if (UseOverlayRelative) {2889        assert(RPath.starts_with(OverlayDir) &&2890               "Overlay dir must be contained in RPath");2891        RPath = RPath.substr(OverlayDir.size());2892      }2893      if (!Entry.IsDirectory) {2894        writeEntry(path::filename(Entry.VPath), RPath);2895        IsCurrentDirEmpty = false;2896      }2897    }2898 2899    while (!DirStack.empty()) {2900      OS << "\n";2901      endDirectory();2902    }2903    OS << "\n";2904  }2905 2906  OS << "  ]\n"2907     << "}\n";2908}2909 2910void YAMLVFSWriter::write(llvm::raw_ostream &OS) {2911  llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {2912    return LHS.VPath < RHS.VPath;2913  });2914 2915  JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,2916                       IsOverlayRelative, OverlayDir);2917}2918 2919vfs::recursive_directory_iterator::recursive_directory_iterator(2920    FileSystem &FS_, const Twine &Path, std::error_code &EC)2921    : FS(&FS_) {2922  directory_iterator I = FS->dir_begin(Path, EC);2923  if (I != directory_iterator()) {2924    State = std::make_shared<detail::RecDirIterState>();2925    State->Stack.push_back(I);2926  }2927}2928 2929vfs::recursive_directory_iterator &2930recursive_directory_iterator::increment(std::error_code &EC) {2931  assert(FS && State && !State->Stack.empty() && "incrementing past end");2932  assert(!State->Stack.back()->path().empty() && "non-canonical end iterator");2933  vfs::directory_iterator End;2934 2935  if (State->HasNoPushRequest)2936    State->HasNoPushRequest = false;2937  else {2938    if (State->Stack.back()->type() == sys::fs::file_type::directory_file) {2939      vfs::directory_iterator I =2940          FS->dir_begin(State->Stack.back()->path(), EC);2941      if (I != End) {2942        State->Stack.push_back(I);2943        return *this;2944      }2945    }2946  }2947 2948  while (!State->Stack.empty() && State->Stack.back().increment(EC) == End)2949    State->Stack.pop_back();2950 2951  if (State->Stack.empty())2952    State.reset(); // end iterator2953 2954  return *this;2955}2956 2957void TracingFileSystem::printImpl(raw_ostream &OS, PrintType Type,2958                                  unsigned IndentLevel) const {2959  printIndent(OS, IndentLevel);2960  OS << "TracingFileSystem\n";2961  if (Type == PrintType::Summary)2962    return;2963 2964  printIndent(OS, IndentLevel);2965  OS << "NumStatusCalls=" << NumStatusCalls << "\n";2966  printIndent(OS, IndentLevel);2967  OS << "NumOpenFileForReadCalls=" << NumOpenFileForReadCalls << "\n";2968  printIndent(OS, IndentLevel);2969  OS << "NumDirBeginCalls=" << NumDirBeginCalls << "\n";2970  printIndent(OS, IndentLevel);2971  OS << "NumGetRealPathCalls=" << NumGetRealPathCalls << "\n";2972  printIndent(OS, IndentLevel);2973  OS << "NumExistsCalls=" << NumExistsCalls << "\n";2974  printIndent(OS, IndentLevel);2975  OS << "NumIsLocalCalls=" << NumIsLocalCalls << "\n";2976 2977  if (Type == PrintType::Contents)2978    Type = PrintType::Summary;2979  getUnderlyingFS().print(OS, Type, IndentLevel + 1);2980}2981 2982const char FileSystem::ID = 0;2983const char OverlayFileSystem::ID = 0;2984const char ProxyFileSystem::ID = 0;2985const char InMemoryFileSystem::ID = 0;2986const char RedirectingFileSystem::ID = 0;2987const char TracingFileSystem::ID = 0;2988