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