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