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