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