VirtualFileSystem.cpp revision 360784
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-exception
6//
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/None.h"
18#include "llvm/ADT/Optional.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/ADT/SmallString.h"
21#include "llvm/ADT/SmallVector.h"
22#include "llvm/ADT/StringRef.h"
23#include "llvm/ADT/StringSet.h"
24#include "llvm/ADT/Twine.h"
25#include "llvm/ADT/iterator_range.h"
26#include "llvm/Config/llvm-config.h"
27#include "llvm/Support/Casting.h"
28#include "llvm/Support/Chrono.h"
29#include "llvm/Support/Compiler.h"
30#include "llvm/Support/Debug.h"
31#include "llvm/Support/Errc.h"
32#include "llvm/Support/ErrorHandling.h"
33#include "llvm/Support/ErrorOr.h"
34#include "llvm/Support/FileSystem.h"
35#include "llvm/Support/MemoryBuffer.h"
36#include "llvm/Support/Path.h"
37#include "llvm/Support/Process.h"
38#include "llvm/Support/SMLoc.h"
39#include "llvm/Support/SourceMgr.h"
40#include "llvm/Support/YAMLParser.h"
41#include "llvm/Support/raw_ostream.h"
42#include <algorithm>
43#include <atomic>
44#include <cassert>
45#include <cstdint>
46#include <iterator>
47#include <limits>
48#include <map>
49#include <memory>
50#include <mutex>
51#include <string>
52#include <system_error>
53#include <utility>
54#include <vector>
55
56using namespace llvm;
57using namespace llvm::vfs;
58
59using llvm::sys::fs::file_t;
60using llvm::sys::fs::file_status;
61using llvm::sys::fs::file_type;
62using llvm::sys::fs::kInvalidFile;
63using llvm::sys::fs::perms;
64using llvm::sys::fs::UniqueID;
65
66Status::Status(const file_status &Status)
67    : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
68      User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
69      Type(Status.type()), Perms(Status.permissions()) {}
70
71Status::Status(const Twine &Name, UniqueID UID, sys::TimePoint<> MTime,
72               uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
73               perms Perms)
74    : Name(Name.str()), UID(UID), MTime(MTime), User(User), Group(Group),
75      Size(Size), Type(Type), Perms(Perms) {}
76
77Status Status::copyWithNewName(const Status &In, const Twine &NewName) {
78  return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
79                In.getUser(), In.getGroup(), In.getSize(), In.getType(),
80                In.getPermissions());
81}
82
83Status Status::copyWithNewName(const file_status &In, const Twine &NewName) {
84  return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
85                In.getUser(), In.getGroup(), In.getSize(), In.type(),
86                In.permissions());
87}
88
89bool Status::equivalent(const Status &Other) const {
90  assert(isStatusKnown() && Other.isStatusKnown());
91  return getUniqueID() == Other.getUniqueID();
92}
93
94bool Status::isDirectory() const { return Type == file_type::directory_file; }
95
96bool Status::isRegularFile() const { return Type == file_type::regular_file; }
97
98bool Status::isOther() const {
99  return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
100}
101
102bool Status::isSymlink() const { return Type == file_type::symlink_file; }
103
104bool Status::isStatusKnown() const { return Type != file_type::status_error; }
105
106bool Status::exists() const {
107  return isStatusKnown() && Type != file_type::file_not_found;
108}
109
110File::~File() = default;
111
112FileSystem::~FileSystem() = default;
113
114ErrorOr<std::unique_ptr<MemoryBuffer>>
115FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
116                             bool RequiresNullTerminator, bool IsVolatile) {
117  auto F = openFileForRead(Name);
118  if (!F)
119    return F.getError();
120
121  return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
122}
123
124std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
125  if (llvm::sys::path::is_absolute(Path))
126    return {};
127
128  auto WorkingDir = getCurrentWorkingDirectory();
129  if (!WorkingDir)
130    return WorkingDir.getError();
131
132  llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
133  return {};
134}
135
136std::error_code FileSystem::getRealPath(const Twine &Path,
137                                        SmallVectorImpl<char> &Output) const {
138  return errc::operation_not_permitted;
139}
140
141std::error_code FileSystem::isLocal(const Twine &Path, bool &Result) {
142  return errc::operation_not_permitted;
143}
144
145bool FileSystem::exists(const Twine &Path) {
146  auto Status = status(Path);
147  return Status && Status->exists();
148}
149
150#ifndef NDEBUG
151static bool isTraversalComponent(StringRef Component) {
152  return Component.equals("..") || Component.equals(".");
153}
154
155static bool pathHasTraversal(StringRef Path) {
156  using namespace llvm::sys;
157
158  for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
159    if (isTraversalComponent(Comp))
160      return true;
161  return false;
162}
163#endif
164
165//===-----------------------------------------------------------------------===/
166// RealFileSystem implementation
167//===-----------------------------------------------------------------------===/
168
169namespace {
170
171/// Wrapper around a raw file descriptor.
172class RealFile : public File {
173  friend class RealFileSystem;
174
175  file_t FD;
176  Status S;
177  std::string RealName;
178
179  RealFile(file_t RawFD, StringRef NewName, StringRef NewRealPathName)
180      : FD(RawFD), S(NewName, {}, {}, {}, {}, {},
181                     llvm::sys::fs::file_type::status_error, {}),
182        RealName(NewRealPathName.str()) {
183    assert(FD != kInvalidFile && "Invalid or inactive file descriptor");
184  }
185
186public:
187  ~RealFile() override;
188
189  ErrorOr<Status> status() override;
190  ErrorOr<std::string> getName() override;
191  ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
192                                                   int64_t FileSize,
193                                                   bool RequiresNullTerminator,
194                                                   bool IsVolatile) override;
195  std::error_code close() override;
196};
197
198} // namespace
199
200RealFile::~RealFile() { close(); }
201
202ErrorOr<Status> RealFile::status() {
203  assert(FD != kInvalidFile && "cannot stat closed file");
204  if (!S.isStatusKnown()) {
205    file_status RealStatus;
206    if (std::error_code EC = sys::fs::status(FD, RealStatus))
207      return EC;
208    S = Status::copyWithNewName(RealStatus, S.getName());
209  }
210  return S;
211}
212
213ErrorOr<std::string> RealFile::getName() {
214  return RealName.empty() ? S.getName().str() : RealName;
215}
216
217ErrorOr<std::unique_ptr<MemoryBuffer>>
218RealFile::getBuffer(const Twine &Name, int64_t FileSize,
219                    bool RequiresNullTerminator, bool IsVolatile) {
220  assert(FD != kInvalidFile && "cannot get buffer for closed file");
221  return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
222                                   IsVolatile);
223}
224
225std::error_code RealFile::close() {
226  std::error_code EC = sys::fs::closeFile(FD);
227  FD = kInvalidFile;
228  return EC;
229}
230
231namespace {
232
233/// A file system according to your operating system.
234/// This may be linked to the process's working directory, or maintain its own.
235///
236/// Currently, its own working directory is emulated by storing the path and
237/// sending absolute paths to llvm::sys::fs:: functions.
238/// A more principled approach would be to push this down a level, modelling
239/// the working dir as an llvm::sys::fs::WorkingDir or similar.
240/// This would enable the use of openat()-style functions on some platforms.
241class RealFileSystem : public FileSystem {
242public:
243  explicit RealFileSystem(bool LinkCWDToProcess) {
244    if (!LinkCWDToProcess) {
245      SmallString<128> PWD, RealPWD;
246      if (llvm::sys::fs::current_path(PWD))
247        return; // Awful, but nothing to do here.
248      if (llvm::sys::fs::real_path(PWD, RealPWD))
249        WD = {PWD, PWD};
250      else
251        WD = {PWD, RealPWD};
252    }
253  }
254
255  ErrorOr<Status> status(const Twine &Path) override;
256  ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
257  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
258
259  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
260  std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
261  std::error_code isLocal(const Twine &Path, bool &Result) override;
262  std::error_code getRealPath(const Twine &Path,
263                              SmallVectorImpl<char> &Output) const override;
264
265private:
266  // If this FS has its own working dir, use it to make Path absolute.
267  // The returned twine is safe to use as long as both Storage and Path live.
268  Twine adjustPath(const Twine &Path, SmallVectorImpl<char> &Storage) const {
269    if (!WD)
270      return Path;
271    Path.toVector(Storage);
272    sys::fs::make_absolute(WD->Resolved, Storage);
273    return Storage;
274  }
275
276  struct WorkingDirectory {
277    // The current working directory, without symlinks resolved. (echo $PWD).
278    SmallString<128> Specified;
279    // The current working directory, with links resolved. (readlink .).
280    SmallString<128> Resolved;
281  };
282  Optional<WorkingDirectory> WD;
283};
284
285} // namespace
286
287ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
288  SmallString<256> Storage;
289  sys::fs::file_status RealStatus;
290  if (std::error_code EC =
291          sys::fs::status(adjustPath(Path, Storage), RealStatus))
292    return EC;
293  return Status::copyWithNewName(RealStatus, Path);
294}
295
296ErrorOr<std::unique_ptr<File>>
297RealFileSystem::openFileForRead(const Twine &Name) {
298  SmallString<256> RealName, Storage;
299  Expected<file_t> FDOrErr = sys::fs::openNativeFileForRead(
300      adjustPath(Name, Storage), sys::fs::OF_None, &RealName);
301  if (!FDOrErr)
302    return errorToErrorCode(FDOrErr.takeError());
303  return std::unique_ptr<File>(
304      new RealFile(*FDOrErr, Name.str(), RealName.str()));
305}
306
307llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
308  if (WD)
309    return WD->Specified.str();
310
311  SmallString<128> Dir;
312  if (std::error_code EC = llvm::sys::fs::current_path(Dir))
313    return EC;
314  return Dir.str();
315}
316
317std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
318  if (!WD)
319    return llvm::sys::fs::set_current_path(Path);
320
321  SmallString<128> Absolute, Resolved, Storage;
322  adjustPath(Path, Storage).toVector(Absolute);
323  bool IsDir;
324  if (auto Err = llvm::sys::fs::is_directory(Absolute, IsDir))
325    return Err;
326  if (!IsDir)
327    return std::make_error_code(std::errc::not_a_directory);
328  if (auto Err = llvm::sys::fs::real_path(Absolute, Resolved))
329    return Err;
330  WD = {Absolute, Resolved};
331  return std::error_code();
332}
333
334std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) {
335  SmallString<256> Storage;
336  return llvm::sys::fs::is_local(adjustPath(Path, Storage), Result);
337}
338
339std::error_code
340RealFileSystem::getRealPath(const Twine &Path,
341                            SmallVectorImpl<char> &Output) const {
342  SmallString<256> Storage;
343  return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output);
344}
345
346IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
347  static IntrusiveRefCntPtr<FileSystem> FS(new RealFileSystem(true));
348  return FS;
349}
350
351std::unique_ptr<FileSystem> vfs::createPhysicalFileSystem() {
352  return std::make_unique<RealFileSystem>(false);
353}
354
355namespace {
356
357class RealFSDirIter : public llvm::vfs::detail::DirIterImpl {
358  llvm::sys::fs::directory_iterator Iter;
359
360public:
361  RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
362    if (Iter != llvm::sys::fs::directory_iterator())
363      CurrentEntry = directory_entry(Iter->path(), Iter->type());
364  }
365
366  std::error_code increment() override {
367    std::error_code EC;
368    Iter.increment(EC);
369    CurrentEntry = (Iter == llvm::sys::fs::directory_iterator())
370                       ? directory_entry()
371                       : directory_entry(Iter->path(), Iter->type());
372    return EC;
373  }
374};
375
376} // namespace
377
378directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
379                                             std::error_code &EC) {
380  SmallString<128> Storage;
381  return directory_iterator(
382      std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC));
383}
384
385//===-----------------------------------------------------------------------===/
386// OverlayFileSystem implementation
387//===-----------------------------------------------------------------------===/
388
389OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
390  FSList.push_back(std::move(BaseFS));
391}
392
393void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
394  FSList.push_back(FS);
395  // Synchronize added file systems by duplicating the working directory from
396  // the first one in the list.
397  FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
398}
399
400ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
401  // FIXME: handle symlinks that cross file systems
402  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
403    ErrorOr<Status> Status = (*I)->status(Path);
404    if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
405      return Status;
406  }
407  return make_error_code(llvm::errc::no_such_file_or_directory);
408}
409
410ErrorOr<std::unique_ptr<File>>
411OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
412  // FIXME: handle symlinks that cross file systems
413  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
414    auto Result = (*I)->openFileForRead(Path);
415    if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
416      return Result;
417  }
418  return make_error_code(llvm::errc::no_such_file_or_directory);
419}
420
421llvm::ErrorOr<std::string>
422OverlayFileSystem::getCurrentWorkingDirectory() const {
423  // All file systems are synchronized, just take the first working directory.
424  return FSList.front()->getCurrentWorkingDirectory();
425}
426
427std::error_code
428OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
429  for (auto &FS : FSList)
430    if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
431      return EC;
432  return {};
433}
434
435std::error_code OverlayFileSystem::isLocal(const Twine &Path, bool &Result) {
436  for (auto &FS : FSList)
437    if (FS->exists(Path))
438      return FS->isLocal(Path, Result);
439  return errc::no_such_file_or_directory;
440}
441
442std::error_code
443OverlayFileSystem::getRealPath(const Twine &Path,
444                               SmallVectorImpl<char> &Output) const {
445  for (auto &FS : FSList)
446    if (FS->exists(Path))
447      return FS->getRealPath(Path, Output);
448  return errc::no_such_file_or_directory;
449}
450
451llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;
452
453namespace {
454
455class OverlayFSDirIterImpl : public llvm::vfs::detail::DirIterImpl {
456  OverlayFileSystem &Overlays;
457  std::string Path;
458  OverlayFileSystem::iterator CurrentFS;
459  directory_iterator CurrentDirIter;
460  llvm::StringSet<> SeenNames;
461
462  std::error_code incrementFS() {
463    assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
464    ++CurrentFS;
465    for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
466      std::error_code EC;
467      CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
468      if (EC && EC != errc::no_such_file_or_directory)
469        return EC;
470      if (CurrentDirIter != directory_iterator())
471        break; // found
472    }
473    return {};
474  }
475
476  std::error_code incrementDirIter(bool IsFirstTime) {
477    assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
478           "incrementing past end");
479    std::error_code EC;
480    if (!IsFirstTime)
481      CurrentDirIter.increment(EC);
482    if (!EC && CurrentDirIter == directory_iterator())
483      EC = incrementFS();
484    return EC;
485  }
486
487  std::error_code incrementImpl(bool IsFirstTime) {
488    while (true) {
489      std::error_code EC = incrementDirIter(IsFirstTime);
490      if (EC || CurrentDirIter == directory_iterator()) {
491        CurrentEntry = directory_entry();
492        return EC;
493      }
494      CurrentEntry = *CurrentDirIter;
495      StringRef Name = llvm::sys::path::filename(CurrentEntry.path());
496      if (SeenNames.insert(Name).second)
497        return EC; // name not seen before
498    }
499    llvm_unreachable("returned above");
500  }
501
502public:
503  OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
504                       std::error_code &EC)
505      : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
506    CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
507    EC = incrementImpl(true);
508  }
509
510  std::error_code increment() override { return incrementImpl(false); }
511};
512
513} // namespace
514
515directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
516                                                std::error_code &EC) {
517  return directory_iterator(
518      std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
519}
520
521void ProxyFileSystem::anchor() {}
522
523namespace llvm {
524namespace vfs {
525
526namespace detail {
527
528enum InMemoryNodeKind { IME_File, IME_Directory, IME_HardLink };
529
530/// The in memory file system is a tree of Nodes. Every node can either be a
531/// file , hardlink or a directory.
532class InMemoryNode {
533  InMemoryNodeKind Kind;
534  std::string FileName;
535
536public:
537  InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind)
538      : Kind(Kind), FileName(llvm::sys::path::filename(FileName)) {}
539  virtual ~InMemoryNode() = default;
540
541  /// Get the filename of this node (the name without the directory part).
542  StringRef getFileName() const { return FileName; }
543  InMemoryNodeKind getKind() const { return Kind; }
544  virtual std::string toString(unsigned Indent) const = 0;
545};
546
547class InMemoryFile : public InMemoryNode {
548  Status Stat;
549  std::unique_ptr<llvm::MemoryBuffer> Buffer;
550
551public:
552  InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
553      : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)),
554        Buffer(std::move(Buffer)) {}
555
556  /// Return the \p Status for this node. \p RequestedName should be the name
557  /// through which the caller referred to this node. It will override
558  /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
559  Status getStatus(const Twine &RequestedName) const {
560    return Status::copyWithNewName(Stat, RequestedName);
561  }
562  llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); }
563
564  std::string toString(unsigned Indent) const override {
565    return (std::string(Indent, ' ') + Stat.getName() + "\n").str();
566  }
567
568  static bool classof(const InMemoryNode *N) {
569    return N->getKind() == IME_File;
570  }
571};
572
573namespace {
574
575class InMemoryHardLink : public InMemoryNode {
576  const InMemoryFile &ResolvedFile;
577
578public:
579  InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile)
580      : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {}
581  const InMemoryFile &getResolvedFile() const { return ResolvedFile; }
582
583  std::string toString(unsigned Indent) const override {
584    return std::string(Indent, ' ') + "HardLink to -> " +
585           ResolvedFile.toString(0);
586  }
587
588  static bool classof(const InMemoryNode *N) {
589    return N->getKind() == IME_HardLink;
590  }
591};
592
593/// Adapt a InMemoryFile for VFS' File interface.  The goal is to make
594/// \p InMemoryFileAdaptor mimic as much as possible the behavior of
595/// \p RealFile.
596class InMemoryFileAdaptor : public File {
597  const InMemoryFile &Node;
598  /// The name to use when returning a Status for this file.
599  std::string RequestedName;
600
601public:
602  explicit InMemoryFileAdaptor(const InMemoryFile &Node,
603                               std::string RequestedName)
604      : Node(Node), RequestedName(std::move(RequestedName)) {}
605
606  llvm::ErrorOr<Status> status() override {
607    return Node.getStatus(RequestedName);
608  }
609
610  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
611  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
612            bool IsVolatile) override {
613    llvm::MemoryBuffer *Buf = Node.getBuffer();
614    return llvm::MemoryBuffer::getMemBuffer(
615        Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
616  }
617
618  std::error_code close() override { return {}; }
619};
620} // namespace
621
622class InMemoryDirectory : public InMemoryNode {
623  Status Stat;
624  llvm::StringMap<std::unique_ptr<InMemoryNode>> Entries;
625
626public:
627  InMemoryDirectory(Status Stat)
628      : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {}
629
630  /// Return the \p Status for this node. \p RequestedName should be the name
631  /// through which the caller referred to this node. It will override
632  /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
633  Status getStatus(const Twine &RequestedName) const {
634    return Status::copyWithNewName(Stat, RequestedName);
635  }
636  InMemoryNode *getChild(StringRef Name) {
637    auto I = Entries.find(Name);
638    if (I != Entries.end())
639      return I->second.get();
640    return nullptr;
641  }
642
643  InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
644    return Entries.insert(make_pair(Name, std::move(Child)))
645        .first->second.get();
646  }
647
648  using const_iterator = decltype(Entries)::const_iterator;
649
650  const_iterator begin() const { return Entries.begin(); }
651  const_iterator end() const { return Entries.end(); }
652
653  std::string toString(unsigned Indent) const override {
654    std::string Result =
655        (std::string(Indent, ' ') + Stat.getName() + "\n").str();
656    for (const auto &Entry : Entries)
657      Result += Entry.second->toString(Indent + 2);
658    return Result;
659  }
660
661  static bool classof(const InMemoryNode *N) {
662    return N->getKind() == IME_Directory;
663  }
664};
665
666namespace {
667Status getNodeStatus(const InMemoryNode *Node, const Twine &RequestedName) {
668  if (auto Dir = dyn_cast<detail::InMemoryDirectory>(Node))
669    return Dir->getStatus(RequestedName);
670  if (auto File = dyn_cast<detail::InMemoryFile>(Node))
671    return File->getStatus(RequestedName);
672  if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node))
673    return Link->getResolvedFile().getStatus(RequestedName);
674  llvm_unreachable("Unknown node type");
675}
676} // namespace
677} // namespace detail
678
679InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
680    : Root(new detail::InMemoryDirectory(
681          Status("", getNextVirtualUniqueID(), llvm::sys::TimePoint<>(), 0, 0,
682                 0, llvm::sys::fs::file_type::directory_file,
683                 llvm::sys::fs::perms::all_all))),
684      UseNormalizedPaths(UseNormalizedPaths) {}
685
686InMemoryFileSystem::~InMemoryFileSystem() = default;
687
688std::string InMemoryFileSystem::toString() const {
689  return Root->toString(/*Indent=*/0);
690}
691
692bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
693                                 std::unique_ptr<llvm::MemoryBuffer> Buffer,
694                                 Optional<uint32_t> User,
695                                 Optional<uint32_t> Group,
696                                 Optional<llvm::sys::fs::file_type> Type,
697                                 Optional<llvm::sys::fs::perms> Perms,
698                                 const detail::InMemoryFile *HardLinkTarget) {
699  SmallString<128> Path;
700  P.toVector(Path);
701
702  // Fix up relative paths. This just prepends the current working directory.
703  std::error_code EC = makeAbsolute(Path);
704  assert(!EC);
705  (void)EC;
706
707  if (useNormalizedPaths())
708    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
709
710  if (Path.empty())
711    return false;
712
713  detail::InMemoryDirectory *Dir = Root.get();
714  auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path);
715  const auto ResolvedUser = User.getValueOr(0);
716  const auto ResolvedGroup = Group.getValueOr(0);
717  const auto ResolvedType = Type.getValueOr(sys::fs::file_type::regular_file);
718  const auto ResolvedPerms = Perms.getValueOr(sys::fs::all_all);
719  assert(!(HardLinkTarget && Buffer) && "HardLink cannot have a buffer");
720  // Any intermediate directories we create should be accessible by
721  // the owner, even if Perms says otherwise for the final path.
722  const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all;
723  while (true) {
724    StringRef Name = *I;
725    detail::InMemoryNode *Node = Dir->getChild(Name);
726    ++I;
727    if (!Node) {
728      if (I == E) {
729        // End of the path.
730        std::unique_ptr<detail::InMemoryNode> Child;
731        if (HardLinkTarget)
732          Child.reset(new detail::InMemoryHardLink(P.str(), *HardLinkTarget));
733        else {
734          // Create a new file or directory.
735          Status Stat(P.str(), getNextVirtualUniqueID(),
736                      llvm::sys::toTimePoint(ModificationTime), ResolvedUser,
737                      ResolvedGroup, Buffer->getBufferSize(), ResolvedType,
738                      ResolvedPerms);
739          if (ResolvedType == sys::fs::file_type::directory_file) {
740            Child.reset(new detail::InMemoryDirectory(std::move(Stat)));
741          } else {
742            Child.reset(
743                new detail::InMemoryFile(std::move(Stat), std::move(Buffer)));
744          }
745        }
746        Dir->addChild(Name, std::move(Child));
747        return true;
748      }
749
750      // Create a new directory. Use the path up to here.
751      Status Stat(
752          StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
753          getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime),
754          ResolvedUser, ResolvedGroup, 0, sys::fs::file_type::directory_file,
755          NewDirectoryPerms);
756      Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
757          Name, std::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
758      continue;
759    }
760
761    if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) {
762      Dir = NewDir;
763    } else {
764      assert((isa<detail::InMemoryFile>(Node) ||
765              isa<detail::InMemoryHardLink>(Node)) &&
766             "Must be either file, hardlink or directory!");
767
768      // Trying to insert a directory in place of a file.
769      if (I != E)
770        return false;
771
772      // Return false only if the new file is different from the existing one.
773      if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) {
774        return Link->getResolvedFile().getBuffer()->getBuffer() ==
775               Buffer->getBuffer();
776      }
777      return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
778             Buffer->getBuffer();
779    }
780  }
781}
782
783bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
784                                 std::unique_ptr<llvm::MemoryBuffer> Buffer,
785                                 Optional<uint32_t> User,
786                                 Optional<uint32_t> Group,
787                                 Optional<llvm::sys::fs::file_type> Type,
788                                 Optional<llvm::sys::fs::perms> Perms) {
789  return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type,
790                 Perms, /*HardLinkTarget=*/nullptr);
791}
792
793bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
794                                      llvm::MemoryBuffer *Buffer,
795                                      Optional<uint32_t> User,
796                                      Optional<uint32_t> Group,
797                                      Optional<llvm::sys::fs::file_type> Type,
798                                      Optional<llvm::sys::fs::perms> Perms) {
799  return addFile(P, ModificationTime,
800                 llvm::MemoryBuffer::getMemBuffer(
801                     Buffer->getBuffer(), Buffer->getBufferIdentifier()),
802                 std::move(User), std::move(Group), std::move(Type),
803                 std::move(Perms));
804}
805
806static ErrorOr<const detail::InMemoryNode *>
807lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir,
808                   const Twine &P) {
809  SmallString<128> Path;
810  P.toVector(Path);
811
812  // Fix up relative paths. This just prepends the current working directory.
813  std::error_code EC = FS.makeAbsolute(Path);
814  assert(!EC);
815  (void)EC;
816
817  if (FS.useNormalizedPaths())
818    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
819
820  if (Path.empty())
821    return Dir;
822
823  auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
824  while (true) {
825    detail::InMemoryNode *Node = Dir->getChild(*I);
826    ++I;
827    if (!Node)
828      return errc::no_such_file_or_directory;
829
830    // Return the file if it's at the end of the path.
831    if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
832      if (I == E)
833        return File;
834      return errc::no_such_file_or_directory;
835    }
836
837    // If Node is HardLink then return the resolved file.
838    if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) {
839      if (I == E)
840        return &File->getResolvedFile();
841      return errc::no_such_file_or_directory;
842    }
843    // Traverse directories.
844    Dir = cast<detail::InMemoryDirectory>(Node);
845    if (I == E)
846      return Dir;
847  }
848}
849
850bool InMemoryFileSystem::addHardLink(const Twine &FromPath,
851                                     const Twine &ToPath) {
852  auto FromNode = lookupInMemoryNode(*this, Root.get(), FromPath);
853  auto ToNode = lookupInMemoryNode(*this, Root.get(), ToPath);
854  // FromPath must not have been added before. ToPath must have been added
855  // before. Resolved ToPath must be a File.
856  if (!ToNode || FromNode || !isa<detail::InMemoryFile>(*ToNode))
857    return false;
858  return this->addFile(FromPath, 0, nullptr, None, None, None, None,
859                       cast<detail::InMemoryFile>(*ToNode));
860}
861
862llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
863  auto Node = lookupInMemoryNode(*this, Root.get(), Path);
864  if (Node)
865    return detail::getNodeStatus(*Node, Path);
866  return Node.getError();
867}
868
869llvm::ErrorOr<std::unique_ptr<File>>
870InMemoryFileSystem::openFileForRead(const Twine &Path) {
871  auto Node = lookupInMemoryNode(*this, Root.get(), Path);
872  if (!Node)
873    return Node.getError();
874
875  // When we have a file provide a heap-allocated wrapper for the memory buffer
876  // to match the ownership semantics for File.
877  if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
878    return std::unique_ptr<File>(
879        new detail::InMemoryFileAdaptor(*F, Path.str()));
880
881  // FIXME: errc::not_a_file?
882  return make_error_code(llvm::errc::invalid_argument);
883}
884
885namespace {
886
887/// Adaptor from InMemoryDir::iterator to directory_iterator.
888class InMemoryDirIterator : public llvm::vfs::detail::DirIterImpl {
889  detail::InMemoryDirectory::const_iterator I;
890  detail::InMemoryDirectory::const_iterator E;
891  std::string RequestedDirName;
892
893  void setCurrentEntry() {
894    if (I != E) {
895      SmallString<256> Path(RequestedDirName);
896      llvm::sys::path::append(Path, I->second->getFileName());
897      sys::fs::file_type Type = sys::fs::file_type::type_unknown;
898      switch (I->second->getKind()) {
899      case detail::IME_File:
900      case detail::IME_HardLink:
901        Type = sys::fs::file_type::regular_file;
902        break;
903      case detail::IME_Directory:
904        Type = sys::fs::file_type::directory_file;
905        break;
906      }
907      CurrentEntry = directory_entry(Path.str(), Type);
908    } else {
909      // When we're at the end, make CurrentEntry invalid and DirIterImpl will
910      // do the rest.
911      CurrentEntry = directory_entry();
912    }
913  }
914
915public:
916  InMemoryDirIterator() = default;
917
918  explicit InMemoryDirIterator(const detail::InMemoryDirectory &Dir,
919                               std::string RequestedDirName)
920      : I(Dir.begin()), E(Dir.end()),
921        RequestedDirName(std::move(RequestedDirName)) {
922    setCurrentEntry();
923  }
924
925  std::error_code increment() override {
926    ++I;
927    setCurrentEntry();
928    return {};
929  }
930};
931
932} // namespace
933
934directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
935                                                 std::error_code &EC) {
936  auto Node = lookupInMemoryNode(*this, Root.get(), Dir);
937  if (!Node) {
938    EC = Node.getError();
939    return directory_iterator(std::make_shared<InMemoryDirIterator>());
940  }
941
942  if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
943    return directory_iterator(
944        std::make_shared<InMemoryDirIterator>(*DirNode, Dir.str()));
945
946  EC = make_error_code(llvm::errc::not_a_directory);
947  return directory_iterator(std::make_shared<InMemoryDirIterator>());
948}
949
950std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) {
951  SmallString<128> Path;
952  P.toVector(Path);
953
954  // Fix up relative paths. This just prepends the current working directory.
955  std::error_code EC = makeAbsolute(Path);
956  assert(!EC);
957  (void)EC;
958
959  if (useNormalizedPaths())
960    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
961
962  if (!Path.empty())
963    WorkingDirectory = Path.str();
964  return {};
965}
966
967std::error_code
968InMemoryFileSystem::getRealPath(const Twine &Path,
969                                SmallVectorImpl<char> &Output) const {
970  auto CWD = getCurrentWorkingDirectory();
971  if (!CWD || CWD->empty())
972    return errc::operation_not_permitted;
973  Path.toVector(Output);
974  if (auto EC = makeAbsolute(Output))
975    return EC;
976  llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true);
977  return {};
978}
979
980std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
981  Result = false;
982  return {};
983}
984
985} // namespace vfs
986} // namespace llvm
987
988//===-----------------------------------------------------------------------===/
989// RedirectingFileSystem implementation
990//===-----------------------------------------------------------------------===/
991
992RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
993    : ExternalFS(std::move(FS)) {
994  if (ExternalFS)
995    if (auto ExternalWorkingDirectory =
996            ExternalFS->getCurrentWorkingDirectory()) {
997      WorkingDirectory = *ExternalWorkingDirectory;
998      ExternalFSValidWD = true;
999    }
1000}
1001
1002// FIXME: reuse implementation common with OverlayFSDirIterImpl as these
1003// iterators are conceptually similar.
1004class llvm::vfs::VFSFromYamlDirIterImpl
1005    : public llvm::vfs::detail::DirIterImpl {
1006  std::string Dir;
1007  RedirectingFileSystem::RedirectingDirectoryEntry::iterator Current, End;
1008
1009  // To handle 'fallthrough' mode we need to iterate at first through
1010  // RedirectingDirectoryEntry and then through ExternalFS. These operations are
1011  // done sequentially, we just need to keep a track of what kind of iteration
1012  // we are currently performing.
1013
1014  /// Flag telling if we should iterate through ExternalFS or stop at the last
1015  /// RedirectingDirectoryEntry::iterator.
1016  bool IterateExternalFS;
1017  /// Flag telling if we have switched to iterating through ExternalFS.
1018  bool IsExternalFSCurrent = false;
1019  FileSystem &ExternalFS;
1020  directory_iterator ExternalDirIter;
1021  llvm::StringSet<> SeenNames;
1022
1023  /// To combine multiple iterations, different methods are responsible for
1024  /// different iteration steps.
1025  /// @{
1026
1027  /// Responsible for dispatching between RedirectingDirectoryEntry iteration
1028  /// and ExternalFS iteration.
1029  std::error_code incrementImpl(bool IsFirstTime);
1030  /// Responsible for RedirectingDirectoryEntry iteration.
1031  std::error_code incrementContent(bool IsFirstTime);
1032  /// Responsible for ExternalFS iteration.
1033  std::error_code incrementExternal();
1034  /// @}
1035
1036public:
1037  VFSFromYamlDirIterImpl(
1038      const Twine &Path,
1039      RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin,
1040      RedirectingFileSystem::RedirectingDirectoryEntry::iterator End,
1041      bool IterateExternalFS, FileSystem &ExternalFS, std::error_code &EC);
1042
1043  std::error_code increment() override;
1044};
1045
1046llvm::ErrorOr<std::string>
1047RedirectingFileSystem::getCurrentWorkingDirectory() const {
1048  return WorkingDirectory;
1049}
1050
1051std::error_code
1052RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
1053  // Don't change the working directory if the path doesn't exist.
1054  if (!exists(Path))
1055    return errc::no_such_file_or_directory;
1056
1057  // Always change the external FS but ignore its result.
1058  if (ExternalFS) {
1059    auto EC = ExternalFS->setCurrentWorkingDirectory(Path);
1060    ExternalFSValidWD = !static_cast<bool>(EC);
1061  }
1062
1063  SmallString<128> AbsolutePath;
1064  Path.toVector(AbsolutePath);
1065  if (std::error_code EC = makeAbsolute(AbsolutePath))
1066    return EC;
1067  WorkingDirectory = AbsolutePath.str();
1068  return {};
1069}
1070
1071std::error_code RedirectingFileSystem::isLocal(const Twine &Path,
1072                                               bool &Result) {
1073  return ExternalFS->isLocal(Path, Result);
1074}
1075
1076std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1077  if (llvm::sys::path::is_absolute(Path, llvm::sys::path::Style::posix) ||
1078      llvm::sys::path::is_absolute(Path, llvm::sys::path::Style::windows))
1079    return {};
1080
1081  auto WorkingDir = getCurrentWorkingDirectory();
1082  if (!WorkingDir)
1083    return WorkingDir.getError();
1084
1085  llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
1086  return {};
1087}
1088
1089directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
1090                                                    std::error_code &EC) {
1091  ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Dir);
1092  if (!E) {
1093    EC = E.getError();
1094    if (shouldUseExternalFS() && EC == errc::no_such_file_or_directory)
1095      return ExternalFS->dir_begin(Dir, EC);
1096    return {};
1097  }
1098  ErrorOr<Status> S = status(Dir, *E);
1099  if (!S) {
1100    EC = S.getError();
1101    return {};
1102  }
1103  if (!S->isDirectory()) {
1104    EC = std::error_code(static_cast<int>(errc::not_a_directory),
1105                         std::system_category());
1106    return {};
1107  }
1108
1109  auto *D = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(*E);
1110  return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(
1111      Dir, D->contents_begin(), D->contents_end(),
1112      /*IterateExternalFS=*/shouldUseExternalFS(), *ExternalFS, EC));
1113}
1114
1115void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) {
1116  ExternalContentsPrefixDir = PrefixDir.str();
1117}
1118
1119StringRef RedirectingFileSystem::getExternalContentsPrefixDir() const {
1120  return ExternalContentsPrefixDir;
1121}
1122
1123void RedirectingFileSystem::dump(raw_ostream &OS) const {
1124  for (const auto &Root : Roots)
1125    dumpEntry(OS, Root.get());
1126}
1127
1128void RedirectingFileSystem::dumpEntry(raw_ostream &OS,
1129                                      RedirectingFileSystem::Entry *E,
1130                                      int NumSpaces) const {
1131  StringRef Name = E->getName();
1132  for (int i = 0, e = NumSpaces; i < e; ++i)
1133    OS << " ";
1134  OS << "'" << Name.str().c_str() << "'"
1135     << "\n";
1136
1137  if (E->getKind() == RedirectingFileSystem::EK_Directory) {
1138    auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(E);
1139    assert(DE && "Should be a directory");
1140
1141    for (std::unique_ptr<Entry> &SubEntry :
1142         llvm::make_range(DE->contents_begin(), DE->contents_end()))
1143      dumpEntry(OS, SubEntry.get(), NumSpaces + 2);
1144  }
1145}
1146
1147#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
1148LLVM_DUMP_METHOD void RedirectingFileSystem::dump() const { dump(dbgs()); }
1149#endif
1150
1151/// A helper class to hold the common YAML parsing state.
1152class llvm::vfs::RedirectingFileSystemParser {
1153  yaml::Stream &Stream;
1154
1155  void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); }
1156
1157  // false on error
1158  bool parseScalarString(yaml::Node *N, StringRef &Result,
1159                         SmallVectorImpl<char> &Storage) {
1160    const auto *S = dyn_cast<yaml::ScalarNode>(N);
1161
1162    if (!S) {
1163      error(N, "expected string");
1164      return false;
1165    }
1166    Result = S->getValue(Storage);
1167    return true;
1168  }
1169
1170  // false on error
1171  bool parseScalarBool(yaml::Node *N, bool &Result) {
1172    SmallString<5> Storage;
1173    StringRef Value;
1174    if (!parseScalarString(N, Value, Storage))
1175      return false;
1176
1177    if (Value.equals_lower("true") || Value.equals_lower("on") ||
1178        Value.equals_lower("yes") || Value == "1") {
1179      Result = true;
1180      return true;
1181    } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
1182               Value.equals_lower("no") || Value == "0") {
1183      Result = false;
1184      return true;
1185    }
1186
1187    error(N, "expected boolean value");
1188    return false;
1189  }
1190
1191  struct KeyStatus {
1192    bool Required;
1193    bool Seen = false;
1194
1195    KeyStatus(bool Required = false) : Required(Required) {}
1196  };
1197
1198  using KeyStatusPair = std::pair<StringRef, KeyStatus>;
1199
1200  // false on error
1201  bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
1202                                  DenseMap<StringRef, KeyStatus> &Keys) {
1203    if (!Keys.count(Key)) {
1204      error(KeyNode, "unknown key");
1205      return false;
1206    }
1207    KeyStatus &S = Keys[Key];
1208    if (S.Seen) {
1209      error(KeyNode, Twine("duplicate key '") + Key + "'");
1210      return false;
1211    }
1212    S.Seen = true;
1213    return true;
1214  }
1215
1216  // false on error
1217  bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
1218    for (const auto &I : Keys) {
1219      if (I.second.Required && !I.second.Seen) {
1220        error(Obj, Twine("missing key '") + I.first + "'");
1221        return false;
1222      }
1223    }
1224    return true;
1225  }
1226
1227  RedirectingFileSystem::Entry *
1228  lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,
1229                      RedirectingFileSystem::Entry *ParentEntry = nullptr) {
1230    if (!ParentEntry) { // Look for a existent root
1231      for (const auto &Root : FS->Roots) {
1232        if (Name.equals(Root->getName())) {
1233          ParentEntry = Root.get();
1234          return ParentEntry;
1235        }
1236      }
1237    } else { // Advance to the next component
1238      auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(
1239          ParentEntry);
1240      for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :
1241           llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1242        auto *DirContent =
1243            dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(
1244                Content.get());
1245        if (DirContent && Name.equals(Content->getName()))
1246          return DirContent;
1247      }
1248    }
1249
1250    // ... or create a new one
1251    std::unique_ptr<RedirectingFileSystem::Entry> E =
1252        std::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>(
1253            Name, Status("", getNextVirtualUniqueID(),
1254                         std::chrono::system_clock::now(), 0, 0, 0,
1255                         file_type::directory_file, sys::fs::all_all));
1256
1257    if (!ParentEntry) { // Add a new root to the overlay
1258      FS->Roots.push_back(std::move(E));
1259      ParentEntry = FS->Roots.back().get();
1260      return ParentEntry;
1261    }
1262
1263    auto *DE =
1264        cast<RedirectingFileSystem::RedirectingDirectoryEntry>(ParentEntry);
1265    DE->addContent(std::move(E));
1266    return DE->getLastContent();
1267  }
1268
1269  void uniqueOverlayTree(RedirectingFileSystem *FS,
1270                         RedirectingFileSystem::Entry *SrcE,
1271                         RedirectingFileSystem::Entry *NewParentE = nullptr) {
1272    StringRef Name = SrcE->getName();
1273    switch (SrcE->getKind()) {
1274    case RedirectingFileSystem::EK_Directory: {
1275      auto *DE = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(SrcE);
1276      // Empty directories could be present in the YAML as a way to
1277      // describe a file for a current directory after some of its subdir
1278      // is parsed. This only leads to redundant walks, ignore it.
1279      if (!Name.empty())
1280        NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
1281      for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
1282           llvm::make_range(DE->contents_begin(), DE->contents_end()))
1283        uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
1284      break;
1285    }
1286    case RedirectingFileSystem::EK_File: {
1287      assert(NewParentE && "Parent entry must exist");
1288      auto *FE = cast<RedirectingFileSystem::RedirectingFileEntry>(SrcE);
1289      auto *DE =
1290          cast<RedirectingFileSystem::RedirectingDirectoryEntry>(NewParentE);
1291      DE->addContent(
1292          std::make_unique<RedirectingFileSystem::RedirectingFileEntry>(
1293              Name, FE->getExternalContentsPath(), FE->getUseName()));
1294      break;
1295    }
1296    }
1297  }
1298
1299  std::unique_ptr<RedirectingFileSystem::Entry>
1300  parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) {
1301    auto *M = dyn_cast<yaml::MappingNode>(N);
1302    if (!M) {
1303      error(N, "expected mapping node for file or directory entry");
1304      return nullptr;
1305    }
1306
1307    KeyStatusPair Fields[] = {
1308        KeyStatusPair("name", true),
1309        KeyStatusPair("type", true),
1310        KeyStatusPair("contents", false),
1311        KeyStatusPair("external-contents", false),
1312        KeyStatusPair("use-external-name", false),
1313    };
1314
1315    DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1316
1317    bool HasContents = false; // external or otherwise
1318    std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
1319        EntryArrayContents;
1320    std::string ExternalContentsPath;
1321    std::string Name;
1322    yaml::Node *NameValueNode = nullptr;
1323    auto UseExternalName =
1324        RedirectingFileSystem::RedirectingFileEntry::NK_NotSet;
1325    RedirectingFileSystem::EntryKind Kind;
1326
1327    for (auto &I : *M) {
1328      StringRef Key;
1329      // Reuse the buffer for key and value, since we don't look at key after
1330      // parsing value.
1331      SmallString<256> Buffer;
1332      if (!parseScalarString(I.getKey(), Key, Buffer))
1333        return nullptr;
1334
1335      if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
1336        return nullptr;
1337
1338      StringRef Value;
1339      if (Key == "name") {
1340        if (!parseScalarString(I.getValue(), Value, Buffer))
1341          return nullptr;
1342
1343        NameValueNode = I.getValue();
1344        if (FS->UseCanonicalizedPaths) {
1345          SmallString<256> Path(Value);
1346          // Guarantee that old YAML files containing paths with ".." and "."
1347          // are properly canonicalized before read into the VFS.
1348          Path = sys::path::remove_leading_dotslash(Path);
1349          sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1350          Name = Path.str();
1351        } else {
1352          Name = Value;
1353        }
1354      } else if (Key == "type") {
1355        if (!parseScalarString(I.getValue(), Value, Buffer))
1356          return nullptr;
1357        if (Value == "file")
1358          Kind = RedirectingFileSystem::EK_File;
1359        else if (Value == "directory")
1360          Kind = RedirectingFileSystem::EK_Directory;
1361        else {
1362          error(I.getValue(), "unknown value for 'type'");
1363          return nullptr;
1364        }
1365      } else if (Key == "contents") {
1366        if (HasContents) {
1367          error(I.getKey(),
1368                "entry already has 'contents' or 'external-contents'");
1369          return nullptr;
1370        }
1371        HasContents = true;
1372        auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
1373        if (!Contents) {
1374          // FIXME: this is only for directories, what about files?
1375          error(I.getValue(), "expected array");
1376          return nullptr;
1377        }
1378
1379        for (auto &I : *Contents) {
1380          if (std::unique_ptr<RedirectingFileSystem::Entry> E =
1381                  parseEntry(&I, FS, /*IsRootEntry*/ false))
1382            EntryArrayContents.push_back(std::move(E));
1383          else
1384            return nullptr;
1385        }
1386      } else if (Key == "external-contents") {
1387        if (HasContents) {
1388          error(I.getKey(),
1389                "entry already has 'contents' or 'external-contents'");
1390          return nullptr;
1391        }
1392        HasContents = true;
1393        if (!parseScalarString(I.getValue(), Value, Buffer))
1394          return nullptr;
1395
1396        SmallString<256> FullPath;
1397        if (FS->IsRelativeOverlay) {
1398          FullPath = FS->getExternalContentsPrefixDir();
1399          assert(!FullPath.empty() &&
1400                 "External contents prefix directory must exist");
1401          llvm::sys::path::append(FullPath, Value);
1402        } else {
1403          FullPath = Value;
1404        }
1405
1406        if (FS->UseCanonicalizedPaths) {
1407          // Guarantee that old YAML files containing paths with ".." and "."
1408          // are properly canonicalized before read into the VFS.
1409          FullPath = sys::path::remove_leading_dotslash(FullPath);
1410          sys::path::remove_dots(FullPath, /*remove_dot_dot=*/true);
1411        }
1412        ExternalContentsPath = FullPath.str();
1413      } else if (Key == "use-external-name") {
1414        bool Val;
1415        if (!parseScalarBool(I.getValue(), Val))
1416          return nullptr;
1417        UseExternalName =
1418            Val ? RedirectingFileSystem::RedirectingFileEntry::NK_External
1419                : RedirectingFileSystem::RedirectingFileEntry::NK_Virtual;
1420      } else {
1421        llvm_unreachable("key missing from Keys");
1422      }
1423    }
1424
1425    if (Stream.failed())
1426      return nullptr;
1427
1428    // check for missing keys
1429    if (!HasContents) {
1430      error(N, "missing key 'contents' or 'external-contents'");
1431      return nullptr;
1432    }
1433    if (!checkMissingKeys(N, Keys))
1434      return nullptr;
1435
1436    // check invalid configuration
1437    if (Kind == RedirectingFileSystem::EK_Directory &&
1438        UseExternalName !=
1439            RedirectingFileSystem::RedirectingFileEntry::NK_NotSet) {
1440      error(N, "'use-external-name' is not supported for directories");
1441      return nullptr;
1442    }
1443
1444    sys::path::Style path_style = sys::path::Style::native;
1445    if (IsRootEntry) {
1446      // VFS root entries may be in either Posix or Windows style.  Figure out
1447      // which style we have, and use it consistently.
1448      if (sys::path::is_absolute(Name, sys::path::Style::posix)) {
1449        path_style = sys::path::Style::posix;
1450      } else if (sys::path::is_absolute(Name, sys::path::Style::windows)) {
1451        path_style = sys::path::Style::windows;
1452      } else {
1453        assert(NameValueNode && "Name presence should be checked earlier");
1454        error(NameValueNode,
1455              "entry with relative path at the root level is not discoverable");
1456        return nullptr;
1457      }
1458    }
1459
1460    // Remove trailing slash(es), being careful not to remove the root path
1461    StringRef Trimmed(Name);
1462    size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();
1463    while (Trimmed.size() > RootPathLen &&
1464           sys::path::is_separator(Trimmed.back(), path_style))
1465      Trimmed = Trimmed.slice(0, Trimmed.size() - 1);
1466
1467    // Get the last component
1468    StringRef LastComponent = sys::path::filename(Trimmed, path_style);
1469
1470    std::unique_ptr<RedirectingFileSystem::Entry> Result;
1471    switch (Kind) {
1472    case RedirectingFileSystem::EK_File:
1473      Result = std::make_unique<RedirectingFileSystem::RedirectingFileEntry>(
1474          LastComponent, std::move(ExternalContentsPath), UseExternalName);
1475      break;
1476    case RedirectingFileSystem::EK_Directory:
1477      Result =
1478          std::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>(
1479              LastComponent, std::move(EntryArrayContents),
1480              Status("", getNextVirtualUniqueID(),
1481                     std::chrono::system_clock::now(), 0, 0, 0,
1482                     file_type::directory_file, sys::fs::all_all));
1483      break;
1484    }
1485
1486    StringRef Parent = sys::path::parent_path(Trimmed, path_style);
1487    if (Parent.empty())
1488      return Result;
1489
1490    // if 'name' contains multiple components, create implicit directory entries
1491    for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style),
1492                                     E = sys::path::rend(Parent);
1493         I != E; ++I) {
1494      std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;
1495      Entries.push_back(std::move(Result));
1496      Result =
1497          std::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>(
1498              *I, std::move(Entries),
1499              Status("", getNextVirtualUniqueID(),
1500                     std::chrono::system_clock::now(), 0, 0, 0,
1501                     file_type::directory_file, sys::fs::all_all));
1502    }
1503    return Result;
1504  }
1505
1506public:
1507  RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
1508
1509  // false on error
1510  bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
1511    auto *Top = dyn_cast<yaml::MappingNode>(Root);
1512    if (!Top) {
1513      error(Root, "expected mapping node");
1514      return false;
1515    }
1516
1517    KeyStatusPair Fields[] = {
1518        KeyStatusPair("version", true),
1519        KeyStatusPair("case-sensitive", false),
1520        KeyStatusPair("use-external-names", false),
1521        KeyStatusPair("overlay-relative", false),
1522        KeyStatusPair("fallthrough", false),
1523        KeyStatusPair("roots", true),
1524    };
1525
1526    DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1527    std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries;
1528
1529    // Parse configuration and 'roots'
1530    for (auto &I : *Top) {
1531      SmallString<10> KeyBuffer;
1532      StringRef Key;
1533      if (!parseScalarString(I.getKey(), Key, KeyBuffer))
1534        return false;
1535
1536      if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
1537        return false;
1538
1539      if (Key == "roots") {
1540        auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue());
1541        if (!Roots) {
1542          error(I.getValue(), "expected array");
1543          return false;
1544        }
1545
1546        for (auto &I : *Roots) {
1547          if (std::unique_ptr<RedirectingFileSystem::Entry> E =
1548                  parseEntry(&I, FS, /*IsRootEntry*/ true))
1549            RootEntries.push_back(std::move(E));
1550          else
1551            return false;
1552        }
1553      } else if (Key == "version") {
1554        StringRef VersionString;
1555        SmallString<4> Storage;
1556        if (!parseScalarString(I.getValue(), VersionString, Storage))
1557          return false;
1558        int Version;
1559        if (VersionString.getAsInteger<int>(10, Version)) {
1560          error(I.getValue(), "expected integer");
1561          return false;
1562        }
1563        if (Version < 0) {
1564          error(I.getValue(), "invalid version number");
1565          return false;
1566        }
1567        if (Version != 0) {
1568          error(I.getValue(), "version mismatch, expected 0");
1569          return false;
1570        }
1571      } else if (Key == "case-sensitive") {
1572        if (!parseScalarBool(I.getValue(), FS->CaseSensitive))
1573          return false;
1574      } else if (Key == "overlay-relative") {
1575        if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay))
1576          return false;
1577      } else if (Key == "use-external-names") {
1578        if (!parseScalarBool(I.getValue(), FS->UseExternalNames))
1579          return false;
1580      } else if (Key == "fallthrough") {
1581        if (!parseScalarBool(I.getValue(), FS->IsFallthrough))
1582          return false;
1583      } else {
1584        llvm_unreachable("key missing from Keys");
1585      }
1586    }
1587
1588    if (Stream.failed())
1589      return false;
1590
1591    if (!checkMissingKeys(Top, Keys))
1592      return false;
1593
1594    // Now that we sucessefully parsed the YAML file, canonicalize the internal
1595    // representation to a proper directory tree so that we can search faster
1596    // inside the VFS.
1597    for (auto &E : RootEntries)
1598      uniqueOverlayTree(FS, E.get());
1599
1600    return true;
1601  }
1602};
1603
1604RedirectingFileSystem *
1605RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
1606                              SourceMgr::DiagHandlerTy DiagHandler,
1607                              StringRef YAMLFilePath, void *DiagContext,
1608                              IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1609  SourceMgr SM;
1610  yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
1611
1612  SM.setDiagHandler(DiagHandler, DiagContext);
1613  yaml::document_iterator DI = Stream.begin();
1614  yaml::Node *Root = DI->getRoot();
1615  if (DI == Stream.end() || !Root) {
1616    SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
1617    return nullptr;
1618  }
1619
1620  RedirectingFileSystemParser P(Stream);
1621
1622  std::unique_ptr<RedirectingFileSystem> FS(
1623      new RedirectingFileSystem(ExternalFS));
1624
1625  if (!YAMLFilePath.empty()) {
1626    // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
1627    // to each 'external-contents' path.
1628    //
1629    // Example:
1630    //    -ivfsoverlay dummy.cache/vfs/vfs.yaml
1631    // yields:
1632    //  FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs
1633    //
1634    SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
1635    std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir);
1636    assert(!EC && "Overlay dir final path must be absolute");
1637    (void)EC;
1638    FS->setExternalContentsPrefixDir(OverlayAbsDir);
1639  }
1640
1641  if (!P.parse(Root, FS.get()))
1642    return nullptr;
1643
1644  return FS.release();
1645}
1646
1647ErrorOr<RedirectingFileSystem::Entry *>
1648RedirectingFileSystem::lookupPath(const Twine &Path_) const {
1649  SmallString<256> Path;
1650  Path_.toVector(Path);
1651
1652  // Handle relative paths
1653  if (std::error_code EC = makeAbsolute(Path))
1654    return EC;
1655
1656  // Canonicalize path by removing ".", "..", "./", etc components. This is
1657  // a VFS request, do bot bother about symlinks in the path components
1658  // but canonicalize in order to perform the correct entry search.
1659  if (UseCanonicalizedPaths) {
1660    Path = sys::path::remove_leading_dotslash(Path);
1661    sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1662  }
1663
1664  if (Path.empty())
1665    return make_error_code(llvm::errc::invalid_argument);
1666
1667  sys::path::const_iterator Start = sys::path::begin(Path);
1668  sys::path::const_iterator End = sys::path::end(Path);
1669  for (const auto &Root : Roots) {
1670    ErrorOr<RedirectingFileSystem::Entry *> Result =
1671        lookupPath(Start, End, Root.get());
1672    if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
1673      return Result;
1674  }
1675  return make_error_code(llvm::errc::no_such_file_or_directory);
1676}
1677
1678ErrorOr<RedirectingFileSystem::Entry *>
1679RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
1680                                  sys::path::const_iterator End,
1681                                  RedirectingFileSystem::Entry *From) const {
1682#ifndef _WIN32
1683  assert(!isTraversalComponent(*Start) &&
1684         !isTraversalComponent(From->getName()) &&
1685         "Paths should not contain traversal components");
1686#else
1687  // FIXME: this is here to support windows, remove it once canonicalized
1688  // paths become globally default.
1689  if (Start->equals("."))
1690    ++Start;
1691#endif
1692
1693  StringRef FromName = From->getName();
1694
1695  // Forward the search to the next component in case this is an empty one.
1696  if (!FromName.empty()) {
1697    if (!pathComponentMatches(*Start, FromName))
1698      return make_error_code(llvm::errc::no_such_file_or_directory);
1699
1700    ++Start;
1701
1702    if (Start == End) {
1703      // Match!
1704      return From;
1705    }
1706  }
1707
1708  auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(From);
1709  if (!DE)
1710    return make_error_code(llvm::errc::not_a_directory);
1711
1712  for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
1713       llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1714    ErrorOr<RedirectingFileSystem::Entry *> Result =
1715        lookupPath(Start, End, DirEntry.get());
1716    if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
1717      return Result;
1718  }
1719
1720  return make_error_code(llvm::errc::no_such_file_or_directory);
1721}
1722
1723static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
1724                                      Status ExternalStatus) {
1725  Status S = ExternalStatus;
1726  if (!UseExternalNames)
1727    S = Status::copyWithNewName(S, Path);
1728  S.IsVFSMapped = true;
1729  return S;
1730}
1731
1732ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path,
1733                                              RedirectingFileSystem::Entry *E) {
1734  assert(E != nullptr);
1735  if (auto *F = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(E)) {
1736    ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
1737    assert(!S || S->getName() == F->getExternalContentsPath());
1738    if (S)
1739      return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
1740                                     *S);
1741    return S;
1742  } else { // directory
1743    auto *DE = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(E);
1744    return Status::copyWithNewName(DE->getStatus(), Path);
1745  }
1746}
1747
1748ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
1749  ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
1750  if (!Result) {
1751    if (shouldUseExternalFS() &&
1752        Result.getError() == llvm::errc::no_such_file_or_directory) {
1753      return ExternalFS->status(Path);
1754    }
1755    return Result.getError();
1756  }
1757  return status(Path, *Result);
1758}
1759
1760namespace {
1761
1762/// Provide a file wrapper with an overriden status.
1763class FileWithFixedStatus : public File {
1764  std::unique_ptr<File> InnerFile;
1765  Status S;
1766
1767public:
1768  FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
1769      : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
1770
1771  ErrorOr<Status> status() override { return S; }
1772  ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
1773
1774  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
1775            bool IsVolatile) override {
1776    return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
1777                                IsVolatile);
1778  }
1779
1780  std::error_code close() override { return InnerFile->close(); }
1781};
1782
1783} // namespace
1784
1785ErrorOr<std::unique_ptr<File>>
1786RedirectingFileSystem::openFileForRead(const Twine &Path) {
1787  ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);
1788  if (!E) {
1789    if (shouldUseExternalFS() &&
1790        E.getError() == llvm::errc::no_such_file_or_directory) {
1791      return ExternalFS->openFileForRead(Path);
1792    }
1793    return E.getError();
1794  }
1795
1796  auto *F = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(*E);
1797  if (!F) // FIXME: errc::not_a_file?
1798    return make_error_code(llvm::errc::invalid_argument);
1799
1800  auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
1801  if (!Result)
1802    return Result;
1803
1804  auto ExternalStatus = (*Result)->status();
1805  if (!ExternalStatus)
1806    return ExternalStatus.getError();
1807
1808  // FIXME: Update the status with the name and VFSMapped.
1809  Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
1810                                     *ExternalStatus);
1811  return std::unique_ptr<File>(
1812      std::make_unique<FileWithFixedStatus>(std::move(*Result), S));
1813}
1814
1815std::error_code
1816RedirectingFileSystem::getRealPath(const Twine &Path,
1817                                   SmallVectorImpl<char> &Output) const {
1818  ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
1819  if (!Result) {
1820    if (shouldUseExternalFS() &&
1821        Result.getError() == llvm::errc::no_such_file_or_directory) {
1822      return ExternalFS->getRealPath(Path, Output);
1823    }
1824    return Result.getError();
1825  }
1826
1827  if (auto *F =
1828          dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(*Result)) {
1829    return ExternalFS->getRealPath(F->getExternalContentsPath(), Output);
1830  }
1831  // Even if there is a directory entry, fall back to ExternalFS if allowed,
1832  // because directories don't have a single external contents path.
1833  return shouldUseExternalFS() ? ExternalFS->getRealPath(Path, Output)
1834                               : llvm::errc::invalid_argument;
1835}
1836
1837IntrusiveRefCntPtr<FileSystem>
1838vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1839                    SourceMgr::DiagHandlerTy DiagHandler,
1840                    StringRef YAMLFilePath, void *DiagContext,
1841                    IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1842  return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
1843                                       YAMLFilePath, DiagContext,
1844                                       std::move(ExternalFS));
1845}
1846
1847static void getVFSEntries(RedirectingFileSystem::Entry *SrcE,
1848                          SmallVectorImpl<StringRef> &Path,
1849                          SmallVectorImpl<YAMLVFSEntry> &Entries) {
1850  auto Kind = SrcE->getKind();
1851  if (Kind == RedirectingFileSystem::EK_Directory) {
1852    auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(SrcE);
1853    assert(DE && "Must be a directory");
1854    for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
1855         llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1856      Path.push_back(SubEntry->getName());
1857      getVFSEntries(SubEntry.get(), Path, Entries);
1858      Path.pop_back();
1859    }
1860    return;
1861  }
1862
1863  assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
1864  auto *FE = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(SrcE);
1865  assert(FE && "Must be a file");
1866  SmallString<128> VPath;
1867  for (auto &Comp : Path)
1868    llvm::sys::path::append(VPath, Comp);
1869  Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
1870}
1871
1872void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1873                             SourceMgr::DiagHandlerTy DiagHandler,
1874                             StringRef YAMLFilePath,
1875                             SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
1876                             void *DiagContext,
1877                             IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1878  RedirectingFileSystem *VFS = RedirectingFileSystem::create(
1879      std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
1880      std::move(ExternalFS));
1881  ErrorOr<RedirectingFileSystem::Entry *> RootE = VFS->lookupPath("/");
1882  if (!RootE)
1883    return;
1884  SmallVector<StringRef, 8> Components;
1885  Components.push_back("/");
1886  getVFSEntries(*RootE, Components, CollectedEntries);
1887}
1888
1889UniqueID vfs::getNextVirtualUniqueID() {
1890  static std::atomic<unsigned> UID;
1891  unsigned ID = ++UID;
1892  // The following assumes that uint64_t max will never collide with a real
1893  // dev_t value from the OS.
1894  return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
1895}
1896
1897void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1898  assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1899  assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1900  assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1901  Mappings.emplace_back(VirtualPath, RealPath);
1902}
1903
1904namespace {
1905
1906class JSONWriter {
1907  llvm::raw_ostream &OS;
1908  SmallVector<StringRef, 16> DirStack;
1909
1910  unsigned getDirIndent() { return 4 * DirStack.size(); }
1911  unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1912  bool containedIn(StringRef Parent, StringRef Path);
1913  StringRef containedPart(StringRef Parent, StringRef Path);
1914  void startDirectory(StringRef Path);
1915  void endDirectory();
1916  void writeEntry(StringRef VPath, StringRef RPath);
1917
1918public:
1919  JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1920
1921  void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> UseExternalNames,
1922             Optional<bool> IsCaseSensitive, Optional<bool> IsOverlayRelative,
1923             StringRef OverlayDir);
1924};
1925
1926} // namespace
1927
1928bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
1929  using namespace llvm::sys;
1930
1931  // Compare each path component.
1932  auto IParent = path::begin(Parent), EParent = path::end(Parent);
1933  for (auto IChild = path::begin(Path), EChild = path::end(Path);
1934       IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
1935    if (*IParent != *IChild)
1936      return false;
1937  }
1938  // Have we exhausted the parent path?
1939  return IParent == EParent;
1940}
1941
1942StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1943  assert(!Parent.empty());
1944  assert(containedIn(Parent, Path));
1945  return Path.slice(Parent.size() + 1, StringRef::npos);
1946}
1947
1948void JSONWriter::startDirectory(StringRef Path) {
1949  StringRef Name =
1950      DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
1951  DirStack.push_back(Path);
1952  unsigned Indent = getDirIndent();
1953  OS.indent(Indent) << "{\n";
1954  OS.indent(Indent + 2) << "'type': 'directory',\n";
1955  OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1956  OS.indent(Indent + 2) << "'contents': [\n";
1957}
1958
1959void JSONWriter::endDirectory() {
1960  unsigned Indent = getDirIndent();
1961  OS.indent(Indent + 2) << "]\n";
1962  OS.indent(Indent) << "}";
1963
1964  DirStack.pop_back();
1965}
1966
1967void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1968  unsigned Indent = getFileIndent();
1969  OS.indent(Indent) << "{\n";
1970  OS.indent(Indent + 2) << "'type': 'file',\n";
1971  OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1972  OS.indent(Indent + 2) << "'external-contents': \""
1973                        << llvm::yaml::escape(RPath) << "\"\n";
1974  OS.indent(Indent) << "}";
1975}
1976
1977void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1978                       Optional<bool> UseExternalNames,
1979                       Optional<bool> IsCaseSensitive,
1980                       Optional<bool> IsOverlayRelative,
1981                       StringRef OverlayDir) {
1982  using namespace llvm::sys;
1983
1984  OS << "{\n"
1985        "  'version': 0,\n";
1986  if (IsCaseSensitive.hasValue())
1987    OS << "  'case-sensitive': '"
1988       << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
1989  if (UseExternalNames.hasValue())
1990    OS << "  'use-external-names': '"
1991       << (UseExternalNames.getValue() ? "true" : "false") << "',\n";
1992  bool UseOverlayRelative = false;
1993  if (IsOverlayRelative.hasValue()) {
1994    UseOverlayRelative = IsOverlayRelative.getValue();
1995    OS << "  'overlay-relative': '" << (UseOverlayRelative ? "true" : "false")
1996       << "',\n";
1997  }
1998  OS << "  'roots': [\n";
1999
2000  if (!Entries.empty()) {
2001    const YAMLVFSEntry &Entry = Entries.front();
2002    startDirectory(path::parent_path(Entry.VPath));
2003
2004    StringRef RPath = Entry.RPath;
2005    if (UseOverlayRelative) {
2006      unsigned OverlayDirLen = OverlayDir.size();
2007      assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
2008             "Overlay dir must be contained in RPath");
2009      RPath = RPath.slice(OverlayDirLen, RPath.size());
2010    }
2011
2012    writeEntry(path::filename(Entry.VPath), RPath);
2013
2014    for (const auto &Entry : Entries.slice(1)) {
2015      StringRef Dir = path::parent_path(Entry.VPath);
2016      if (Dir == DirStack.back())
2017        OS << ",\n";
2018      else {
2019        while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
2020          OS << "\n";
2021          endDirectory();
2022        }
2023        OS << ",\n";
2024        startDirectory(Dir);
2025      }
2026      StringRef RPath = Entry.RPath;
2027      if (UseOverlayRelative) {
2028        unsigned OverlayDirLen = OverlayDir.size();
2029        assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
2030               "Overlay dir must be contained in RPath");
2031        RPath = RPath.slice(OverlayDirLen, RPath.size());
2032      }
2033      writeEntry(path::filename(Entry.VPath), RPath);
2034    }
2035
2036    while (!DirStack.empty()) {
2037      OS << "\n";
2038      endDirectory();
2039    }
2040    OS << "\n";
2041  }
2042
2043  OS << "  ]\n"
2044     << "}\n";
2045}
2046
2047void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
2048  llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
2049    return LHS.VPath < RHS.VPath;
2050  });
2051
2052  JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
2053                       IsOverlayRelative, OverlayDir);
2054}
2055
2056VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(
2057    const Twine &_Path,
2058    RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin,
2059    RedirectingFileSystem::RedirectingDirectoryEntry::iterator End,
2060    bool IterateExternalFS, FileSystem &ExternalFS, std::error_code &EC)
2061    : Dir(_Path.str()), Current(Begin), End(End),
2062      IterateExternalFS(IterateExternalFS), ExternalFS(ExternalFS) {
2063  EC = incrementImpl(/*IsFirstTime=*/true);
2064}
2065
2066std::error_code VFSFromYamlDirIterImpl::increment() {
2067  return incrementImpl(/*IsFirstTime=*/false);
2068}
2069
2070std::error_code VFSFromYamlDirIterImpl::incrementExternal() {
2071  assert(!(IsExternalFSCurrent && ExternalDirIter == directory_iterator()) &&
2072         "incrementing past end");
2073  std::error_code EC;
2074  if (IsExternalFSCurrent) {
2075    ExternalDirIter.increment(EC);
2076  } else if (IterateExternalFS) {
2077    ExternalDirIter = ExternalFS.dir_begin(Dir, EC);
2078    IsExternalFSCurrent = true;
2079    if (EC && EC != errc::no_such_file_or_directory)
2080      return EC;
2081    EC = {};
2082  }
2083  if (EC || ExternalDirIter == directory_iterator()) {
2084    CurrentEntry = directory_entry();
2085  } else {
2086    CurrentEntry = *ExternalDirIter;
2087  }
2088  return EC;
2089}
2090
2091std::error_code VFSFromYamlDirIterImpl::incrementContent(bool IsFirstTime) {
2092  assert((IsFirstTime || Current != End) && "cannot iterate past end");
2093  if (!IsFirstTime)
2094    ++Current;
2095  while (Current != End) {
2096    SmallString<128> PathStr(Dir);
2097    llvm::sys::path::append(PathStr, (*Current)->getName());
2098    sys::fs::file_type Type = sys::fs::file_type::type_unknown;
2099    switch ((*Current)->getKind()) {
2100    case RedirectingFileSystem::EK_Directory:
2101      Type = sys::fs::file_type::directory_file;
2102      break;
2103    case RedirectingFileSystem::EK_File:
2104      Type = sys::fs::file_type::regular_file;
2105      break;
2106    }
2107    CurrentEntry = directory_entry(PathStr.str(), Type);
2108    return {};
2109  }
2110  return incrementExternal();
2111}
2112
2113std::error_code VFSFromYamlDirIterImpl::incrementImpl(bool IsFirstTime) {
2114  while (true) {
2115    std::error_code EC = IsExternalFSCurrent ? incrementExternal()
2116                                             : incrementContent(IsFirstTime);
2117    if (EC || CurrentEntry.path().empty())
2118      return EC;
2119    StringRef Name = llvm::sys::path::filename(CurrentEntry.path());
2120    if (SeenNames.insert(Name).second)
2121      return EC; // name not seen before
2122  }
2123  llvm_unreachable("returned above");
2124}
2125
2126vfs::recursive_directory_iterator::recursive_directory_iterator(
2127    FileSystem &FS_, const Twine &Path, std::error_code &EC)
2128    : FS(&FS_) {
2129  directory_iterator I = FS->dir_begin(Path, EC);
2130  if (I != directory_iterator()) {
2131    State = std::make_shared<detail::RecDirIterState>();
2132    State->Stack.push(I);
2133  }
2134}
2135
2136vfs::recursive_directory_iterator &
2137recursive_directory_iterator::increment(std::error_code &EC) {
2138  assert(FS && State && !State->Stack.empty() && "incrementing past end");
2139  assert(!State->Stack.top()->path().empty() && "non-canonical end iterator");
2140  vfs::directory_iterator End;
2141
2142  if (State->HasNoPushRequest)
2143    State->HasNoPushRequest = false;
2144  else {
2145    if (State->Stack.top()->type() == sys::fs::file_type::directory_file) {
2146      vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC);
2147      if (I != End) {
2148        State->Stack.push(I);
2149        return *this;
2150      }
2151    }
2152  }
2153
2154  while (!State->Stack.empty() && State->Stack.top().increment(EC) == End)
2155    State->Stack.pop();
2156
2157  if (State->Stack.empty())
2158    State.reset(); // end iterator
2159
2160  return *this;
2161}
2162