xref: /llvm-project/llvm/lib/Support/VirtualFileSystem.cpp (revision d44ea7186befe38eb2b3804b15cd1ee1777458ed)
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     if (!Keys.count(Key)) {
1712       error(KeyNode, "unknown key");
1713       return false;
1714     }
1715     KeyStatus &S = Keys[Key];
1716     if (S.Seen) {
1717       error(KeyNode, Twine("duplicate key '") + Key + "'");
1718       return false;
1719     }
1720     S.Seen = true;
1721     return true;
1722   }
1723 
1724   // false on error
1725   bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
1726     for (const auto &I : Keys) {
1727       if (I.second.Required && !I.second.Seen) {
1728         error(Obj, Twine("missing key '") + I.first + "'");
1729         return false;
1730       }
1731     }
1732     return true;
1733   }
1734 
1735 public:
1736   static RedirectingFileSystem::Entry *
1737   lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,
1738                       RedirectingFileSystem::Entry *ParentEntry = nullptr) {
1739     if (!ParentEntry) { // Look for a existent root
1740       for (const auto &Root : FS->Roots) {
1741         if (Name == Root->getName()) {
1742           ParentEntry = Root.get();
1743           return ParentEntry;
1744         }
1745       }
1746     } else { // Advance to the next component
1747       auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
1748       for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :
1749            llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1750         auto *DirContent =
1751             dyn_cast<RedirectingFileSystem::DirectoryEntry>(Content.get());
1752         if (DirContent && Name == Content->getName())
1753           return DirContent;
1754       }
1755     }
1756 
1757     // ... or create a new one
1758     std::unique_ptr<RedirectingFileSystem::Entry> E =
1759         std::make_unique<RedirectingFileSystem::DirectoryEntry>(
1760             Name, Status("", getNextVirtualUniqueID(),
1761                          std::chrono::system_clock::now(), 0, 0, 0,
1762                          file_type::directory_file, sys::fs::all_all));
1763 
1764     if (!ParentEntry) { // Add a new root to the overlay
1765       FS->Roots.push_back(std::move(E));
1766       ParentEntry = FS->Roots.back().get();
1767       return ParentEntry;
1768     }
1769 
1770     auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
1771     DE->addContent(std::move(E));
1772     return DE->getLastContent();
1773   }
1774 
1775 private:
1776   void uniqueOverlayTree(RedirectingFileSystem *FS,
1777                          RedirectingFileSystem::Entry *SrcE,
1778                          RedirectingFileSystem::Entry *NewParentE = nullptr) {
1779     StringRef Name = SrcE->getName();
1780     switch (SrcE->getKind()) {
1781     case RedirectingFileSystem::EK_Directory: {
1782       auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
1783       // Empty directories could be present in the YAML as a way to
1784       // describe a file for a current directory after some of its subdir
1785       // is parsed. This only leads to redundant walks, ignore it.
1786       if (!Name.empty())
1787         NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
1788       for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
1789            llvm::make_range(DE->contents_begin(), DE->contents_end()))
1790         uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
1791       break;
1792     }
1793     case RedirectingFileSystem::EK_DirectoryRemap: {
1794       assert(NewParentE && "Parent entry must exist");
1795       auto *DR = cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
1796       auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1797       DE->addContent(
1798           std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
1799               Name, DR->getExternalContentsPath(), DR->getUseName()));
1800       break;
1801     }
1802     case RedirectingFileSystem::EK_File: {
1803       assert(NewParentE && "Parent entry must exist");
1804       auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);
1805       auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1806       DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>(
1807           Name, FE->getExternalContentsPath(), FE->getUseName()));
1808       break;
1809     }
1810     }
1811   }
1812 
1813   std::unique_ptr<RedirectingFileSystem::Entry>
1814   parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) {
1815     auto *M = dyn_cast<yaml::MappingNode>(N);
1816     if (!M) {
1817       error(N, "expected mapping node for file or directory entry");
1818       return nullptr;
1819     }
1820 
1821     KeyStatusPair Fields[] = {
1822         KeyStatusPair("name", true),
1823         KeyStatusPair("type", true),
1824         KeyStatusPair("contents", false),
1825         KeyStatusPair("external-contents", false),
1826         KeyStatusPair("use-external-name", false),
1827     };
1828 
1829     DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1830 
1831     enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;
1832     std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
1833         EntryArrayContents;
1834     SmallString<256> ExternalContentsPath;
1835     SmallString<256> Name;
1836     yaml::Node *NameValueNode = nullptr;
1837     auto UseExternalName = RedirectingFileSystem::NK_NotSet;
1838     RedirectingFileSystem::EntryKind Kind;
1839 
1840     for (auto &I : *M) {
1841       StringRef Key;
1842       // Reuse the buffer for key and value, since we don't look at key after
1843       // parsing value.
1844       SmallString<256> Buffer;
1845       if (!parseScalarString(I.getKey(), Key, Buffer))
1846         return nullptr;
1847 
1848       if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
1849         return nullptr;
1850 
1851       StringRef Value;
1852       if (Key == "name") {
1853         if (!parseScalarString(I.getValue(), Value, Buffer))
1854           return nullptr;
1855 
1856         NameValueNode = I.getValue();
1857         // Guarantee that old YAML files containing paths with ".." and "."
1858         // are properly canonicalized before read into the VFS.
1859         Name = canonicalize(Value).str();
1860       } else if (Key == "type") {
1861         if (!parseScalarString(I.getValue(), Value, Buffer))
1862           return nullptr;
1863         if (Value == "file")
1864           Kind = RedirectingFileSystem::EK_File;
1865         else if (Value == "directory")
1866           Kind = RedirectingFileSystem::EK_Directory;
1867         else if (Value == "directory-remap")
1868           Kind = RedirectingFileSystem::EK_DirectoryRemap;
1869         else {
1870           error(I.getValue(), "unknown value for 'type'");
1871           return nullptr;
1872         }
1873       } else if (Key == "contents") {
1874         if (ContentsField != CF_NotSet) {
1875           error(I.getKey(),
1876                 "entry already has 'contents' or 'external-contents'");
1877           return nullptr;
1878         }
1879         ContentsField = CF_List;
1880         auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
1881         if (!Contents) {
1882           // FIXME: this is only for directories, what about files?
1883           error(I.getValue(), "expected array");
1884           return nullptr;
1885         }
1886 
1887         for (auto &I : *Contents) {
1888           if (std::unique_ptr<RedirectingFileSystem::Entry> E =
1889                   parseEntry(&I, FS, /*IsRootEntry*/ false))
1890             EntryArrayContents.push_back(std::move(E));
1891           else
1892             return nullptr;
1893         }
1894       } else if (Key == "external-contents") {
1895         if (ContentsField != CF_NotSet) {
1896           error(I.getKey(),
1897                 "entry already has 'contents' or 'external-contents'");
1898           return nullptr;
1899         }
1900         ContentsField = CF_External;
1901         if (!parseScalarString(I.getValue(), Value, Buffer))
1902           return nullptr;
1903 
1904         SmallString<256> FullPath;
1905         if (FS->IsRelativeOverlay) {
1906           FullPath = FS->getOverlayFileDir();
1907           assert(!FullPath.empty() &&
1908                  "External contents prefix directory must exist");
1909           llvm::sys::path::append(FullPath, Value);
1910         } else {
1911           FullPath = Value;
1912         }
1913 
1914         // Guarantee that old YAML files containing paths with ".." and "."
1915         // are properly canonicalized before read into the VFS.
1916         FullPath = canonicalize(FullPath);
1917         ExternalContentsPath = FullPath.str();
1918       } else if (Key == "use-external-name") {
1919         bool Val;
1920         if (!parseScalarBool(I.getValue(), Val))
1921           return nullptr;
1922         UseExternalName = Val ? RedirectingFileSystem::NK_External
1923                               : RedirectingFileSystem::NK_Virtual;
1924       } else {
1925         llvm_unreachable("key missing from Keys");
1926       }
1927     }
1928 
1929     if (Stream.failed())
1930       return nullptr;
1931 
1932     // check for missing keys
1933     if (ContentsField == CF_NotSet) {
1934       error(N, "missing key 'contents' or 'external-contents'");
1935       return nullptr;
1936     }
1937     if (!checkMissingKeys(N, Keys))
1938       return nullptr;
1939 
1940     // check invalid configuration
1941     if (Kind == RedirectingFileSystem::EK_Directory &&
1942         UseExternalName != RedirectingFileSystem::NK_NotSet) {
1943       error(N, "'use-external-name' is not supported for 'directory' entries");
1944       return nullptr;
1945     }
1946 
1947     if (Kind == RedirectingFileSystem::EK_DirectoryRemap &&
1948         ContentsField == CF_List) {
1949       error(N, "'contents' is not supported for 'directory-remap' entries");
1950       return nullptr;
1951     }
1952 
1953     sys::path::Style path_style = sys::path::Style::native;
1954     if (IsRootEntry) {
1955       // VFS root entries may be in either Posix or Windows style.  Figure out
1956       // which style we have, and use it consistently.
1957       if (sys::path::is_absolute(Name, sys::path::Style::posix)) {
1958         path_style = sys::path::Style::posix;
1959       } else if (sys::path::is_absolute(Name,
1960                                         sys::path::Style::windows_backslash)) {
1961         path_style = sys::path::Style::windows_backslash;
1962       } else {
1963         // Relative VFS root entries are made absolute to either the overlay
1964         // directory, or the current working directory, then we can determine
1965         // the path style from that.
1966         std::error_code EC;
1967         if (FS->RootRelative ==
1968             RedirectingFileSystem::RootRelativeKind::OverlayDir) {
1969           StringRef FullPath = FS->getOverlayFileDir();
1970           assert(!FullPath.empty() && "Overlay file directory must exist");
1971           EC = FS->makeAbsolute(FullPath, Name);
1972           Name = canonicalize(Name);
1973         } else {
1974           EC = sys::fs::make_absolute(Name);
1975         }
1976         if (EC) {
1977           assert(NameValueNode && "Name presence should be checked earlier");
1978           error(
1979               NameValueNode,
1980               "entry with relative path at the root level is not discoverable");
1981           return nullptr;
1982         }
1983         path_style = sys::path::is_absolute(Name, sys::path::Style::posix)
1984                          ? sys::path::Style::posix
1985                          : sys::path::Style::windows_backslash;
1986       }
1987       // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will
1988       // return true even if `Name` is using forward slashes. Distinguish
1989       // between windows_backslash and windows_slash.
1990       if (path_style == sys::path::Style::windows_backslash &&
1991           getExistingStyle(Name) != sys::path::Style::windows_backslash)
1992         path_style = sys::path::Style::windows_slash;
1993     }
1994 
1995     // Remove trailing slash(es), being careful not to remove the root path
1996     StringRef Trimmed = Name;
1997     size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();
1998     while (Trimmed.size() > RootPathLen &&
1999            sys::path::is_separator(Trimmed.back(), path_style))
2000       Trimmed = Trimmed.slice(0, Trimmed.size() - 1);
2001 
2002     // Get the last component
2003     StringRef LastComponent = sys::path::filename(Trimmed, path_style);
2004 
2005     std::unique_ptr<RedirectingFileSystem::Entry> Result;
2006     switch (Kind) {
2007     case RedirectingFileSystem::EK_File:
2008       Result = std::make_unique<RedirectingFileSystem::FileEntry>(
2009           LastComponent, std::move(ExternalContentsPath), UseExternalName);
2010       break;
2011     case RedirectingFileSystem::EK_DirectoryRemap:
2012       Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
2013           LastComponent, std::move(ExternalContentsPath), UseExternalName);
2014       break;
2015     case RedirectingFileSystem::EK_Directory:
2016       Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
2017           LastComponent, std::move(EntryArrayContents),
2018           Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2019                  0, 0, 0, file_type::directory_file, sys::fs::all_all));
2020       break;
2021     }
2022 
2023     StringRef Parent = sys::path::parent_path(Trimmed, path_style);
2024     if (Parent.empty())
2025       return Result;
2026 
2027     // if 'name' contains multiple components, create implicit directory entries
2028     for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style),
2029                                      E = sys::path::rend(Parent);
2030          I != E; ++I) {
2031       std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;
2032       Entries.push_back(std::move(Result));
2033       Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
2034           *I, std::move(Entries),
2035           Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2036                  0, 0, 0, file_type::directory_file, sys::fs::all_all));
2037     }
2038     return Result;
2039   }
2040 
2041 public:
2042   RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
2043 
2044   // false on error
2045   bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
2046     auto *Top = dyn_cast<yaml::MappingNode>(Root);
2047     if (!Top) {
2048       error(Root, "expected mapping node");
2049       return false;
2050     }
2051 
2052     KeyStatusPair Fields[] = {
2053         KeyStatusPair("version", true),
2054         KeyStatusPair("case-sensitive", false),
2055         KeyStatusPair("use-external-names", false),
2056         KeyStatusPair("root-relative", false),
2057         KeyStatusPair("overlay-relative", false),
2058         KeyStatusPair("fallthrough", false),
2059         KeyStatusPair("redirecting-with", false),
2060         KeyStatusPair("roots", true),
2061     };
2062 
2063     DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
2064     std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries;
2065 
2066     // Parse configuration and 'roots'
2067     for (auto &I : *Top) {
2068       SmallString<10> KeyBuffer;
2069       StringRef Key;
2070       if (!parseScalarString(I.getKey(), Key, KeyBuffer))
2071         return false;
2072 
2073       if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
2074         return false;
2075 
2076       if (Key == "roots") {
2077         auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue());
2078         if (!Roots) {
2079           error(I.getValue(), "expected array");
2080           return false;
2081         }
2082 
2083         for (auto &I : *Roots) {
2084           if (std::unique_ptr<RedirectingFileSystem::Entry> E =
2085                   parseEntry(&I, FS, /*IsRootEntry*/ true))
2086             RootEntries.push_back(std::move(E));
2087           else
2088             return false;
2089         }
2090       } else if (Key == "version") {
2091         StringRef VersionString;
2092         SmallString<4> Storage;
2093         if (!parseScalarString(I.getValue(), VersionString, Storage))
2094           return false;
2095         int Version;
2096         if (VersionString.getAsInteger<int>(10, Version)) {
2097           error(I.getValue(), "expected integer");
2098           return false;
2099         }
2100         if (Version < 0) {
2101           error(I.getValue(), "invalid version number");
2102           return false;
2103         }
2104         if (Version != 0) {
2105           error(I.getValue(), "version mismatch, expected 0");
2106           return false;
2107         }
2108       } else if (Key == "case-sensitive") {
2109         if (!parseScalarBool(I.getValue(), FS->CaseSensitive))
2110           return false;
2111       } else if (Key == "overlay-relative") {
2112         if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay))
2113           return false;
2114       } else if (Key == "use-external-names") {
2115         if (!parseScalarBool(I.getValue(), FS->UseExternalNames))
2116           return false;
2117       } else if (Key == "fallthrough") {
2118         if (Keys["redirecting-with"].Seen) {
2119           error(I.getValue(),
2120                 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2121           return false;
2122         }
2123 
2124         bool ShouldFallthrough = false;
2125         if (!parseScalarBool(I.getValue(), ShouldFallthrough))
2126           return false;
2127 
2128         if (ShouldFallthrough) {
2129           FS->Redirection = RedirectingFileSystem::RedirectKind::Fallthrough;
2130         } else {
2131           FS->Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly;
2132         }
2133       } else if (Key == "redirecting-with") {
2134         if (Keys["fallthrough"].Seen) {
2135           error(I.getValue(),
2136                 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2137           return false;
2138         }
2139 
2140         if (auto Kind = parseRedirectKind(I.getValue())) {
2141           FS->Redirection = *Kind;
2142         } else {
2143           error(I.getValue(), "expected valid redirect kind");
2144           return false;
2145         }
2146       } else if (Key == "root-relative") {
2147         if (auto Kind = parseRootRelativeKind(I.getValue())) {
2148           FS->RootRelative = *Kind;
2149         } else {
2150           error(I.getValue(), "expected valid root-relative kind");
2151           return false;
2152         }
2153       } else {
2154         llvm_unreachable("key missing from Keys");
2155       }
2156     }
2157 
2158     if (Stream.failed())
2159       return false;
2160 
2161     if (!checkMissingKeys(Top, Keys))
2162       return false;
2163 
2164     // Now that we sucessefully parsed the YAML file, canonicalize the internal
2165     // representation to a proper directory tree so that we can search faster
2166     // inside the VFS.
2167     for (auto &E : RootEntries)
2168       uniqueOverlayTree(FS, E.get());
2169 
2170     return true;
2171   }
2172 };
2173 
2174 std::unique_ptr<RedirectingFileSystem>
2175 RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
2176                               SourceMgr::DiagHandlerTy DiagHandler,
2177                               StringRef YAMLFilePath, void *DiagContext,
2178                               IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2179   SourceMgr SM;
2180   yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
2181 
2182   SM.setDiagHandler(DiagHandler, DiagContext);
2183   yaml::document_iterator DI = Stream.begin();
2184   yaml::Node *Root = DI->getRoot();
2185   if (DI == Stream.end() || !Root) {
2186     SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
2187     return nullptr;
2188   }
2189 
2190   RedirectingFileSystemParser P(Stream);
2191 
2192   std::unique_ptr<RedirectingFileSystem> FS(
2193       new RedirectingFileSystem(ExternalFS));
2194 
2195   if (!YAMLFilePath.empty()) {
2196     // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
2197     // to each 'external-contents' path.
2198     //
2199     // Example:
2200     //    -ivfsoverlay dummy.cache/vfs/vfs.yaml
2201     // yields:
2202     //  FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs
2203     //
2204     SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
2205     std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir);
2206     assert(!EC && "Overlay dir final path must be absolute");
2207     (void)EC;
2208     FS->setOverlayFileDir(OverlayAbsDir);
2209   }
2210 
2211   if (!P.parse(Root, FS.get()))
2212     return nullptr;
2213 
2214   return FS;
2215 }
2216 
2217 std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
2218     ArrayRef<std::pair<std::string, std::string>> RemappedFiles,
2219     bool UseExternalNames, FileSystem &ExternalFS) {
2220   std::unique_ptr<RedirectingFileSystem> FS(
2221       new RedirectingFileSystem(&ExternalFS));
2222   FS->UseExternalNames = UseExternalNames;
2223 
2224   StringMap<RedirectingFileSystem::Entry *> Entries;
2225 
2226   for (auto &Mapping : llvm::reverse(RemappedFiles)) {
2227     SmallString<128> From = StringRef(Mapping.first);
2228     SmallString<128> To = StringRef(Mapping.second);
2229     {
2230       auto EC = ExternalFS.makeAbsolute(From);
2231       (void)EC;
2232       assert(!EC && "Could not make absolute path");
2233     }
2234 
2235     // Check if we've already mapped this file. The first one we see (in the
2236     // reverse iteration) wins.
2237     RedirectingFileSystem::Entry *&ToEntry = Entries[From];
2238     if (ToEntry)
2239       continue;
2240 
2241     // Add parent directories.
2242     RedirectingFileSystem::Entry *Parent = nullptr;
2243     StringRef FromDirectory = llvm::sys::path::parent_path(From);
2244     for (auto I = llvm::sys::path::begin(FromDirectory),
2245               E = llvm::sys::path::end(FromDirectory);
2246          I != E; ++I) {
2247       Parent = RedirectingFileSystemParser::lookupOrCreateEntry(FS.get(), *I,
2248                                                                 Parent);
2249     }
2250     assert(Parent && "File without a directory?");
2251     {
2252       auto EC = ExternalFS.makeAbsolute(To);
2253       (void)EC;
2254       assert(!EC && "Could not make absolute path");
2255     }
2256 
2257     // Add the file.
2258     auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
2259         llvm::sys::path::filename(From), To,
2260         UseExternalNames ? RedirectingFileSystem::NK_External
2261                          : RedirectingFileSystem::NK_Virtual);
2262     ToEntry = NewFile.get();
2263     cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(
2264         std::move(NewFile));
2265   }
2266 
2267   return FS;
2268 }
2269 
2270 RedirectingFileSystem::LookupResult::LookupResult(
2271     Entry *E, sys::path::const_iterator Start, sys::path::const_iterator End)
2272     : E(E) {
2273   assert(E != nullptr);
2274   // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
2275   // path of the directory it maps to in the external file system plus any
2276   // remaining path components in the provided iterator.
2277   if (auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(E)) {
2278     SmallString<256> Redirect(DRE->getExternalContentsPath());
2279     sys::path::append(Redirect, Start, End,
2280                       getExistingStyle(DRE->getExternalContentsPath()));
2281     ExternalRedirect = std::string(Redirect);
2282   }
2283 }
2284 
2285 void RedirectingFileSystem::LookupResult::getPath(
2286     llvm::SmallVectorImpl<char> &Result) const {
2287   Result.clear();
2288   for (Entry *Parent : Parents)
2289     llvm::sys::path::append(Result, Parent->getName());
2290   llvm::sys::path::append(Result, E->getName());
2291 }
2292 
2293 std::error_code RedirectingFileSystem::makeCanonicalForLookup(
2294     SmallVectorImpl<char> &Path) const {
2295   if (std::error_code EC = makeAbsolute(Path))
2296     return EC;
2297 
2298   llvm::SmallString<256> CanonicalPath =
2299       canonicalize(StringRef(Path.data(), Path.size()));
2300   if (CanonicalPath.empty())
2301     return make_error_code(llvm::errc::invalid_argument);
2302 
2303   Path.assign(CanonicalPath.begin(), CanonicalPath.end());
2304   return {};
2305 }
2306 
2307 ErrorOr<RedirectingFileSystem::LookupResult>
2308 RedirectingFileSystem::lookupPath(StringRef Path) const {
2309   llvm::SmallString<128> CanonicalPath(Path);
2310   if (std::error_code EC = makeCanonicalForLookup(CanonicalPath))
2311     return EC;
2312 
2313   // RedirectOnly means the VFS is always used.
2314   if (UsageTrackingActive && Redirection == RedirectKind::RedirectOnly)
2315     HasBeenUsed = true;
2316 
2317   sys::path::const_iterator Start = sys::path::begin(CanonicalPath);
2318   sys::path::const_iterator End = sys::path::end(CanonicalPath);
2319   llvm::SmallVector<Entry *, 32> Entries;
2320   for (const auto &Root : Roots) {
2321     ErrorOr<RedirectingFileSystem::LookupResult> Result =
2322         lookupPathImpl(Start, End, Root.get(), Entries);
2323     if (UsageTrackingActive && Result && isa<RemapEntry>(Result->E))
2324       HasBeenUsed = true;
2325     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) {
2326       Result->Parents = std::move(Entries);
2327       return Result;
2328     }
2329   }
2330   return make_error_code(llvm::errc::no_such_file_or_directory);
2331 }
2332 
2333 ErrorOr<RedirectingFileSystem::LookupResult>
2334 RedirectingFileSystem::lookupPathImpl(
2335     sys::path::const_iterator Start, sys::path::const_iterator End,
2336     RedirectingFileSystem::Entry *From,
2337     llvm::SmallVectorImpl<Entry *> &Entries) const {
2338   assert(!isTraversalComponent(*Start) &&
2339          !isTraversalComponent(From->getName()) &&
2340          "Paths should not contain traversal components");
2341 
2342   StringRef FromName = From->getName();
2343 
2344   // Forward the search to the next component in case this is an empty one.
2345   if (!FromName.empty()) {
2346     if (!pathComponentMatches(*Start, FromName))
2347       return make_error_code(llvm::errc::no_such_file_or_directory);
2348 
2349     ++Start;
2350 
2351     if (Start == End) {
2352       // Match!
2353       return LookupResult(From, Start, End);
2354     }
2355   }
2356 
2357   if (isa<RedirectingFileSystem::FileEntry>(From))
2358     return make_error_code(llvm::errc::not_a_directory);
2359 
2360   if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From))
2361     return LookupResult(From, Start, End);
2362 
2363   auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From);
2364   for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
2365        llvm::make_range(DE->contents_begin(), DE->contents_end())) {
2366     Entries.push_back(From);
2367     ErrorOr<RedirectingFileSystem::LookupResult> Result =
2368         lookupPathImpl(Start, End, DirEntry.get(), Entries);
2369     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
2370       return Result;
2371     Entries.pop_back();
2372   }
2373 
2374   return make_error_code(llvm::errc::no_such_file_or_directory);
2375 }
2376 
2377 static Status getRedirectedFileStatus(const Twine &OriginalPath,
2378                                       bool UseExternalNames,
2379                                       Status ExternalStatus) {
2380   // The path has been mapped by some nested VFS and exposes an external path,
2381   // don't override it with the original path.
2382   if (ExternalStatus.ExposesExternalVFSPath)
2383     return ExternalStatus;
2384 
2385   Status S = ExternalStatus;
2386   if (!UseExternalNames)
2387     S = Status::copyWithNewName(S, OriginalPath);
2388   else
2389     S.ExposesExternalVFSPath = true;
2390   return S;
2391 }
2392 
2393 ErrorOr<Status> RedirectingFileSystem::status(
2394     const Twine &LookupPath, const Twine &OriginalPath,
2395     const RedirectingFileSystem::LookupResult &Result) {
2396   if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
2397     SmallString<256> RemappedPath((*ExtRedirect).str());
2398     if (std::error_code EC = makeAbsolute(RemappedPath))
2399       return EC;
2400 
2401     ErrorOr<Status> S = ExternalFS->status(RemappedPath);
2402     if (!S)
2403       return S;
2404     S = Status::copyWithNewName(*S, *ExtRedirect);
2405     auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E);
2406     return getRedirectedFileStatus(OriginalPath,
2407                                    RE->useExternalName(UseExternalNames), *S);
2408   }
2409 
2410   auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
2411   return Status::copyWithNewName(DE->getStatus(), LookupPath);
2412 }
2413 
2414 ErrorOr<Status>
2415 RedirectingFileSystem::getExternalStatus(const Twine &LookupPath,
2416                                          const Twine &OriginalPath) const {
2417   auto Result = ExternalFS->status(LookupPath);
2418 
2419   // The path has been mapped by some nested VFS, don't override it with the
2420   // original path.
2421   if (!Result || Result->ExposesExternalVFSPath)
2422     return Result;
2423   return Status::copyWithNewName(Result.get(), OriginalPath);
2424 }
2425 
2426 ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {
2427   SmallString<256> Path;
2428   OriginalPath.toVector(Path);
2429 
2430   if (std::error_code EC = makeAbsolute(Path))
2431     return EC;
2432 
2433   if (Redirection == RedirectKind::Fallback) {
2434     // Attempt to find the original file first, only falling back to the
2435     // mapped file if that fails.
2436     ErrorOr<Status> S = getExternalStatus(Path, OriginalPath);
2437     if (S)
2438       return S;
2439   }
2440 
2441   ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
2442   if (!Result) {
2443     // Was not able to map file, fallthrough to using the original path if
2444     // that was the specified redirection type.
2445     if (Redirection == RedirectKind::Fallthrough &&
2446         isFileNotFound(Result.getError()))
2447       return getExternalStatus(Path, OriginalPath);
2448     return Result.getError();
2449   }
2450 
2451   ErrorOr<Status> S = status(Path, OriginalPath, *Result);
2452   if (!S && Redirection == RedirectKind::Fallthrough &&
2453       isFileNotFound(S.getError(), Result->E)) {
2454     // Mapped the file but it wasn't found in the underlying filesystem,
2455     // fallthrough to using the original path if that was the specified
2456     // redirection type.
2457     return getExternalStatus(Path, OriginalPath);
2458   }
2459 
2460   return S;
2461 }
2462 
2463 bool RedirectingFileSystem::exists(const Twine &OriginalPath) {
2464   SmallString<256> Path;
2465   OriginalPath.toVector(Path);
2466 
2467   if (makeAbsolute(Path))
2468     return false;
2469 
2470   if (Redirection == RedirectKind::Fallback) {
2471     // Attempt to find the original file first, only falling back to the
2472     // mapped file if that fails.
2473     if (ExternalFS->exists(Path))
2474       return true;
2475   }
2476 
2477   ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
2478   if (!Result) {
2479     // Was not able to map file, fallthrough to using the original path if
2480     // that was the specified redirection type.
2481     if (Redirection == RedirectKind::Fallthrough &&
2482         isFileNotFound(Result.getError()))
2483       return ExternalFS->exists(Path);
2484     return false;
2485   }
2486 
2487   std::optional<StringRef> ExtRedirect = Result->getExternalRedirect();
2488   if (!ExtRedirect) {
2489     assert(isa<RedirectingFileSystem::DirectoryEntry>(Result->E));
2490     return true;
2491   }
2492 
2493   SmallString<256> RemappedPath((*ExtRedirect).str());
2494   if (makeAbsolute(RemappedPath))
2495     return false;
2496 
2497   if (ExternalFS->exists(RemappedPath))
2498     return true;
2499 
2500   if (Redirection == RedirectKind::Fallthrough) {
2501     // Mapped the file but it wasn't found in the underlying filesystem,
2502     // fallthrough to using the original path if that was the specified
2503     // redirection type.
2504     return ExternalFS->exists(Path);
2505   }
2506 
2507   return false;
2508 }
2509 
2510 namespace {
2511 
2512 /// Provide a file wrapper with an overriden status.
2513 class FileWithFixedStatus : public File {
2514   std::unique_ptr<File> InnerFile;
2515   Status S;
2516 
2517 public:
2518   FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
2519       : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
2520 
2521   ErrorOr<Status> status() override { return S; }
2522   ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
2523 
2524   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
2525             bool IsVolatile) override {
2526     return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
2527                                 IsVolatile);
2528   }
2529 
2530   std::error_code close() override { return InnerFile->close(); }
2531 
2532   void setPath(const Twine &Path) override { S = S.copyWithNewName(S, Path); }
2533 };
2534 
2535 } // namespace
2536 
2537 ErrorOr<std::unique_ptr<File>>
2538 File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
2539   // See \c getRedirectedFileStatus - don't update path if it's exposing an
2540   // external path.
2541   if (!Result || (*Result)->status()->ExposesExternalVFSPath)
2542     return Result;
2543 
2544   ErrorOr<std::unique_ptr<File>> F = std::move(*Result);
2545   auto Name = F->get()->getName();
2546   if (Name && Name.get() != P.str())
2547     F->get()->setPath(P);
2548   return F;
2549 }
2550 
2551 ErrorOr<std::unique_ptr<File>>
2552 RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) {
2553   SmallString<256> Path;
2554   OriginalPath.toVector(Path);
2555 
2556   if (std::error_code EC = makeAbsolute(Path))
2557     return EC;
2558 
2559   if (Redirection == RedirectKind::Fallback) {
2560     // Attempt to find the original file first, only falling back to the
2561     // mapped file if that fails.
2562     auto F = File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2563     if (F)
2564       return F;
2565   }
2566 
2567   ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
2568   if (!Result) {
2569     // Was not able to map file, fallthrough to using the original path if
2570     // that was the specified redirection type.
2571     if (Redirection == RedirectKind::Fallthrough &&
2572         isFileNotFound(Result.getError()))
2573       return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2574     return Result.getError();
2575   }
2576 
2577   if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
2578     return make_error_code(llvm::errc::invalid_argument);
2579 
2580   StringRef ExtRedirect = *Result->getExternalRedirect();
2581   SmallString<256> RemappedPath(ExtRedirect.str());
2582   if (std::error_code EC = makeAbsolute(RemappedPath))
2583     return EC;
2584 
2585   auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
2586 
2587   auto ExternalFile =
2588       File::getWithPath(ExternalFS->openFileForRead(RemappedPath), ExtRedirect);
2589   if (!ExternalFile) {
2590     if (Redirection == RedirectKind::Fallthrough &&
2591         isFileNotFound(ExternalFile.getError(), Result->E)) {
2592       // Mapped the file but it wasn't found in the underlying filesystem,
2593       // fallthrough to using the original path if that was the specified
2594       // redirection type.
2595       return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2596     }
2597     return ExternalFile;
2598   }
2599 
2600   auto ExternalStatus = (*ExternalFile)->status();
2601   if (!ExternalStatus)
2602     return ExternalStatus.getError();
2603 
2604   // Otherwise, the file was successfully remapped. Mark it as such. Also
2605   // replace the underlying path if the external name is being used.
2606   Status S = getRedirectedFileStatus(
2607       OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus);
2608   return std::unique_ptr<File>(
2609       std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
2610 }
2611 
2612 std::error_code
2613 RedirectingFileSystem::getRealPath(const Twine &OriginalPath,
2614                                    SmallVectorImpl<char> &Output) {
2615   SmallString<256> Path;
2616   OriginalPath.toVector(Path);
2617 
2618   if (std::error_code EC = makeAbsolute(Path))
2619     return EC;
2620 
2621   if (Redirection == RedirectKind::Fallback) {
2622     // Attempt to find the original file first, only falling back to the
2623     // mapped file if that fails.
2624     std::error_code EC = ExternalFS->getRealPath(Path, Output);
2625     if (!EC)
2626       return EC;
2627   }
2628 
2629   ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
2630   if (!Result) {
2631     // Was not able to map file, fallthrough to using the original path if
2632     // that was the specified redirection type.
2633     if (Redirection == RedirectKind::Fallthrough &&
2634         isFileNotFound(Result.getError()))
2635       return ExternalFS->getRealPath(Path, Output);
2636     return Result.getError();
2637   }
2638 
2639   // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2640   // path in the external file system.
2641   if (auto ExtRedirect = Result->getExternalRedirect()) {
2642     auto P = ExternalFS->getRealPath(*ExtRedirect, Output);
2643     if (P && Redirection == RedirectKind::Fallthrough &&
2644         isFileNotFound(P, Result->E)) {
2645       // Mapped the file but it wasn't found in the underlying filesystem,
2646       // fallthrough to using the original path if that was the specified
2647       // redirection type.
2648       return ExternalFS->getRealPath(Path, Output);
2649     }
2650     return P;
2651   }
2652 
2653   // We found a DirectoryEntry, which does not have a single external contents
2654   // path. Use the canonical virtual path.
2655   if (Redirection == RedirectKind::Fallthrough) {
2656     Result->getPath(Output);
2657     return {};
2658   }
2659   return llvm::errc::invalid_argument;
2660 }
2661 
2662 std::unique_ptr<FileSystem>
2663 vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
2664                     SourceMgr::DiagHandlerTy DiagHandler,
2665                     StringRef YAMLFilePath, void *DiagContext,
2666                     IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2667   return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
2668                                        YAMLFilePath, DiagContext,
2669                                        std::move(ExternalFS));
2670 }
2671 
2672 static void getVFSEntries(RedirectingFileSystem::Entry *SrcE,
2673                           SmallVectorImpl<StringRef> &Path,
2674                           SmallVectorImpl<YAMLVFSEntry> &Entries) {
2675   auto Kind = SrcE->getKind();
2676   if (Kind == RedirectingFileSystem::EK_Directory) {
2677     auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
2678     assert(DE && "Must be a directory");
2679     for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
2680          llvm::make_range(DE->contents_begin(), DE->contents_end())) {
2681       Path.push_back(SubEntry->getName());
2682       getVFSEntries(SubEntry.get(), Path, Entries);
2683       Path.pop_back();
2684     }
2685     return;
2686   }
2687 
2688   if (Kind == RedirectingFileSystem::EK_DirectoryRemap) {
2689     auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
2690     assert(DR && "Must be a directory remap");
2691     SmallString<128> VPath;
2692     for (auto &Comp : Path)
2693       llvm::sys::path::append(VPath, Comp);
2694     Entries.push_back(
2695         YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
2696     return;
2697   }
2698 
2699   assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
2700   auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
2701   assert(FE && "Must be a file");
2702   SmallString<128> VPath;
2703   for (auto &Comp : Path)
2704     llvm::sys::path::append(VPath, Comp);
2705   Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
2706 }
2707 
2708 void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
2709                              SourceMgr::DiagHandlerTy DiagHandler,
2710                              StringRef YAMLFilePath,
2711                              SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
2712                              void *DiagContext,
2713                              IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2714   std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create(
2715       std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
2716       std::move(ExternalFS));
2717   if (!VFS)
2718     return;
2719   ErrorOr<RedirectingFileSystem::LookupResult> RootResult =
2720       VFS->lookupPath("/");
2721   if (!RootResult)
2722     return;
2723   SmallVector<StringRef, 8> Components;
2724   Components.push_back("/");
2725   getVFSEntries(RootResult->E, Components, CollectedEntries);
2726 }
2727 
2728 UniqueID vfs::getNextVirtualUniqueID() {
2729   static std::atomic<unsigned> UID;
2730   unsigned ID = ++UID;
2731   // The following assumes that uint64_t max will never collide with a real
2732   // dev_t value from the OS.
2733   return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
2734 }
2735 
2736 void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath,
2737                              bool IsDirectory) {
2738   assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
2739   assert(sys::path::is_absolute(RealPath) && "real path not absolute");
2740   assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
2741   Mappings.emplace_back(VirtualPath, RealPath, IsDirectory);
2742 }
2743 
2744 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
2745   addEntry(VirtualPath, RealPath, /*IsDirectory=*/false);
2746 }
2747 
2748 void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath,
2749                                         StringRef RealPath) {
2750   addEntry(VirtualPath, RealPath, /*IsDirectory=*/true);
2751 }
2752 
2753 namespace {
2754 
2755 class JSONWriter {
2756   llvm::raw_ostream &OS;
2757   SmallVector<StringRef, 16> DirStack;
2758 
2759   unsigned getDirIndent() { return 4 * DirStack.size(); }
2760   unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
2761   bool containedIn(StringRef Parent, StringRef Path);
2762   StringRef containedPart(StringRef Parent, StringRef Path);
2763   void startDirectory(StringRef Path);
2764   void endDirectory();
2765   void writeEntry(StringRef VPath, StringRef RPath);
2766 
2767 public:
2768   JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
2769 
2770   void write(ArrayRef<YAMLVFSEntry> Entries,
2771              std::optional<bool> UseExternalNames,
2772              std::optional<bool> IsCaseSensitive,
2773              std::optional<bool> IsOverlayRelative, StringRef OverlayDir);
2774 };
2775 
2776 } // namespace
2777 
2778 bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
2779   using namespace llvm::sys;
2780 
2781   // Compare each path component.
2782   auto IParent = path::begin(Parent), EParent = path::end(Parent);
2783   for (auto IChild = path::begin(Path), EChild = path::end(Path);
2784        IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
2785     if (*IParent != *IChild)
2786       return false;
2787   }
2788   // Have we exhausted the parent path?
2789   return IParent == EParent;
2790 }
2791 
2792 StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
2793   assert(!Parent.empty());
2794   assert(containedIn(Parent, Path));
2795   return Path.substr(Parent.size() + 1);
2796 }
2797 
2798 void JSONWriter::startDirectory(StringRef Path) {
2799   StringRef Name =
2800       DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
2801   DirStack.push_back(Path);
2802   unsigned Indent = getDirIndent();
2803   OS.indent(Indent) << "{\n";
2804   OS.indent(Indent + 2) << "'type': 'directory',\n";
2805   OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
2806   OS.indent(Indent + 2) << "'contents': [\n";
2807 }
2808 
2809 void JSONWriter::endDirectory() {
2810   unsigned Indent = getDirIndent();
2811   OS.indent(Indent + 2) << "]\n";
2812   OS.indent(Indent) << "}";
2813 
2814   DirStack.pop_back();
2815 }
2816 
2817 void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
2818   unsigned Indent = getFileIndent();
2819   OS.indent(Indent) << "{\n";
2820   OS.indent(Indent + 2) << "'type': 'file',\n";
2821   OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
2822   OS.indent(Indent + 2) << "'external-contents': \""
2823                         << llvm::yaml::escape(RPath) << "\"\n";
2824   OS.indent(Indent) << "}";
2825 }
2826 
2827 void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
2828                        std::optional<bool> UseExternalNames,
2829                        std::optional<bool> IsCaseSensitive,
2830                        std::optional<bool> IsOverlayRelative,
2831                        StringRef OverlayDir) {
2832   using namespace llvm::sys;
2833 
2834   OS << "{\n"
2835         "  'version': 0,\n";
2836   if (IsCaseSensitive)
2837     OS << "  'case-sensitive': '" << (*IsCaseSensitive ? "true" : "false")
2838        << "',\n";
2839   if (UseExternalNames)
2840     OS << "  'use-external-names': '" << (*UseExternalNames ? "true" : "false")
2841        << "',\n";
2842   bool UseOverlayRelative = false;
2843   if (IsOverlayRelative) {
2844     UseOverlayRelative = *IsOverlayRelative;
2845     OS << "  'overlay-relative': '" << (UseOverlayRelative ? "true" : "false")
2846        << "',\n";
2847   }
2848   OS << "  'roots': [\n";
2849 
2850   if (!Entries.empty()) {
2851     const YAMLVFSEntry &Entry = Entries.front();
2852 
2853     startDirectory(
2854       Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath)
2855     );
2856 
2857     StringRef RPath = Entry.RPath;
2858     if (UseOverlayRelative) {
2859       assert(RPath.starts_with(OverlayDir) &&
2860              "Overlay dir must be contained in RPath");
2861       RPath = RPath.substr(OverlayDir.size());
2862     }
2863 
2864     bool IsCurrentDirEmpty = true;
2865     if (!Entry.IsDirectory) {
2866       writeEntry(path::filename(Entry.VPath), RPath);
2867       IsCurrentDirEmpty = false;
2868     }
2869 
2870     for (const auto &Entry : Entries.slice(1)) {
2871       StringRef Dir =
2872           Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath);
2873       if (Dir == DirStack.back()) {
2874         if (!IsCurrentDirEmpty) {
2875           OS << ",\n";
2876         }
2877       } else {
2878         bool IsDirPoppedFromStack = false;
2879         while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
2880           OS << "\n";
2881           endDirectory();
2882           IsDirPoppedFromStack = true;
2883         }
2884         if (IsDirPoppedFromStack || !IsCurrentDirEmpty) {
2885           OS << ",\n";
2886         }
2887         startDirectory(Dir);
2888         IsCurrentDirEmpty = true;
2889       }
2890       StringRef RPath = Entry.RPath;
2891       if (UseOverlayRelative) {
2892         assert(RPath.starts_with(OverlayDir) &&
2893                "Overlay dir must be contained in RPath");
2894         RPath = RPath.substr(OverlayDir.size());
2895       }
2896       if (!Entry.IsDirectory) {
2897         writeEntry(path::filename(Entry.VPath), RPath);
2898         IsCurrentDirEmpty = false;
2899       }
2900     }
2901 
2902     while (!DirStack.empty()) {
2903       OS << "\n";
2904       endDirectory();
2905     }
2906     OS << "\n";
2907   }
2908 
2909   OS << "  ]\n"
2910      << "}\n";
2911 }
2912 
2913 void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
2914   llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
2915     return LHS.VPath < RHS.VPath;
2916   });
2917 
2918   JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
2919                        IsOverlayRelative, OverlayDir);
2920 }
2921 
2922 vfs::recursive_directory_iterator::recursive_directory_iterator(
2923     FileSystem &FS_, const Twine &Path, std::error_code &EC)
2924     : FS(&FS_) {
2925   directory_iterator I = FS->dir_begin(Path, EC);
2926   if (I != directory_iterator()) {
2927     State = std::make_shared<detail::RecDirIterState>();
2928     State->Stack.push_back(I);
2929   }
2930 }
2931 
2932 vfs::recursive_directory_iterator &
2933 recursive_directory_iterator::increment(std::error_code &EC) {
2934   assert(FS && State && !State->Stack.empty() && "incrementing past end");
2935   assert(!State->Stack.back()->path().empty() && "non-canonical end iterator");
2936   vfs::directory_iterator End;
2937 
2938   if (State->HasNoPushRequest)
2939     State->HasNoPushRequest = false;
2940   else {
2941     if (State->Stack.back()->type() == sys::fs::file_type::directory_file) {
2942       vfs::directory_iterator I =
2943           FS->dir_begin(State->Stack.back()->path(), EC);
2944       if (I != End) {
2945         State->Stack.push_back(I);
2946         return *this;
2947       }
2948     }
2949   }
2950 
2951   while (!State->Stack.empty() && State->Stack.back().increment(EC) == End)
2952     State->Stack.pop_back();
2953 
2954   if (State->Stack.empty())
2955     State.reset(); // end iterator
2956 
2957   return *this;
2958 }
2959 
2960 void TracingFileSystem::printImpl(raw_ostream &OS, PrintType Type,
2961                                   unsigned IndentLevel) const {
2962   printIndent(OS, IndentLevel);
2963   OS << "TracingFileSystem\n";
2964   if (Type == PrintType::Summary)
2965     return;
2966 
2967   printIndent(OS, IndentLevel);
2968   OS << "NumStatusCalls=" << NumStatusCalls << "\n";
2969   printIndent(OS, IndentLevel);
2970   OS << "NumOpenFileForReadCalls=" << NumOpenFileForReadCalls << "\n";
2971   printIndent(OS, IndentLevel);
2972   OS << "NumDirBeginCalls=" << NumDirBeginCalls << "\n";
2973   printIndent(OS, IndentLevel);
2974   OS << "NumGetRealPathCalls=" << NumGetRealPathCalls << "\n";
2975   printIndent(OS, IndentLevel);
2976   OS << "NumExistsCalls=" << NumExistsCalls << "\n";
2977   printIndent(OS, IndentLevel);
2978   OS << "NumIsLocalCalls=" << NumIsLocalCalls << "\n";
2979 
2980   if (Type == PrintType::Contents)
2981     Type = PrintType::Summary;
2982   getUnderlyingFS().print(OS, Type, IndentLevel + 1);
2983 }
2984 
2985 const char FileSystem::ID = 0;
2986 const char OverlayFileSystem::ID = 0;
2987 const char ProxyFileSystem::ID = 0;
2988 const char InMemoryFileSystem::ID = 0;
2989 const char RedirectingFileSystem::ID = 0;
2990 const char TracingFileSystem::ID = 0;
2991