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 while (true) {
871 StringRef Name = *I;
872 detail::InMemoryNode *Node = Dir->getChild(Name);
873 ++I;
874 if (!Node) {
875 if (I == E) {
876 // End of the path.
877 Dir->addChild(
878 Name, MakeNode({Dir->getUniqueID(), Path, Name, ModificationTime,
879 std::move(Buffer), ResolvedUser, ResolvedGroup,
880 ResolvedType, ResolvedPerms}));
881 return true;
882 }
883
884 // Create a new directory. Use the path up to here.
885 Status Stat(
886 StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
888 llvm::sys::toTimePoint(ModificationTime), ResolvedUser, ResolvedGroup,
889 0, sys::fs::file_type::directory_file, NewDirectoryPerms);
890 Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
891 Name, std::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
892 continue;
893 }
894
895 if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) {
896 Dir = NewDir;
897 } else {
898 assert((isa<detail::InMemoryFile>(Node) ||
899 isa<detail::InMemoryHardLink>(Node)) &&
900 "Must be either file, hardlink or directory!");
901
902 // Trying to insert a directory in place of a file.
903 if (I != E)
904 return false;
905
906 // Return false only if the new file is different from the existing one.
907 if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) {
908 return Link->getResolvedFile().getBuffer()->getBuffer() ==
909 Buffer->getBuffer();
910 }
911 return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
912 Buffer->getBuffer();
913 }
914 }
915}
916
917bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
918 std::unique_ptr<llvm::MemoryBuffer> Buffer,
919 std::optional<uint32_t> User,
920 std::optional<uint32_t> Group,
921 std::optional<llvm::sys::fs::file_type> Type,
922 std::optional<llvm::sys::fs::perms> Perms) {
923 return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type,
924 Perms,
926 -> std::unique_ptr<detail::InMemoryNode> {
927 Status Stat = NNI.makeStatus();
929 return std::make_unique<detail::InMemoryDirectory>(Stat);
930 return std::make_unique<detail::InMemoryFile>(
931 Stat, std::move(NNI.Buffer));
932 });
933}
934
936 const Twine &P, time_t ModificationTime,
937 const llvm::MemoryBufferRef &Buffer, std::optional<uint32_t> User,
938 std::optional<uint32_t> Group, std::optional<llvm::sys::fs::file_type> Type,
939 std::optional<llvm::sys::fs::perms> Perms) {
940 return addFile(P, ModificationTime, llvm::MemoryBuffer::getMemBuffer(Buffer),
941 std::move(User), std::move(Group), std::move(Type),
942 std::move(Perms),
944 -> std::unique_ptr<detail::InMemoryNode> {
945 Status Stat = NNI.makeStatus();
947 return std::make_unique<detail::InMemoryDirectory>(Stat);
948 return std::make_unique<detail::InMemoryFile>(
949 Stat, std::move(NNI.Buffer));
950 });
951}
952
954InMemoryFileSystem::lookupNode(const Twine &P, bool FollowFinalSymlink,
955 size_t SymlinkDepth) const {
956 SmallString<128> Path;
957 P.toVector(Path);
958
959 // Fix up relative paths. This just prepends the current working directory.
960 std::error_code EC = makeAbsolute(Path);
961 assert(!EC);
962 (void)EC;
963
964 if (useNormalizedPaths())
965 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
966
967 const detail::InMemoryDirectory *Dir = Root.get();
968 if (Path.empty())
969 return detail::NamedNodeOrError(Path, Dir);
970
971 auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
972 while (true) {
973 detail::InMemoryNode *Node = Dir->getChild(*I);
974 ++I;
975 if (!Node)
977
978 if (auto Symlink = dyn_cast<detail::InMemorySymbolicLink>(Node)) {
979 // If we're at the end of the path, and we're not following through
980 // terminal symlinks, then we're done.
981 if (I == E && !FollowFinalSymlink)
982 return detail::NamedNodeOrError(Path, Symlink);
983
984 if (SymlinkDepth > InMemoryFileSystem::MaxSymlinkDepth)
986
987 SmallString<128> TargetPath = Symlink->getTargetPath();
988 if (std::error_code EC = makeAbsolute(TargetPath))
989 return EC;
990
991 // Keep going with the target. We always want to follow symlinks here
992 // because we're either at the end of a path that we want to follow, or
993 // not at the end of a path, in which case we need to follow the symlink
994 // regardless.
995 auto Target =
996 lookupNode(TargetPath, /*FollowFinalSymlink=*/true, SymlinkDepth + 1);
997 if (!Target || I == E)
998 return Target;
999
1000 if (!isa<detail::InMemoryDirectory>(*Target))
1002
1003 // Otherwise, continue on the search in the symlinked directory.
1004 Dir = cast<detail::InMemoryDirectory>(*Target);
1005 continue;
1006 }
1007
1008 // Return the file if it's at the end of the path.
1009 if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
1010 if (I == E)
1011 return detail::NamedNodeOrError(Path, File);
1013 }
1014
1015 // If Node is HardLink then return the resolved file.
1016 if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) {
1017 if (I == E)
1018 return detail::NamedNodeOrError(Path, &File->getResolvedFile());
1020 }
1021 // Traverse directories.
1022 Dir = cast<detail::InMemoryDirectory>(Node);
1023 if (I == E)
1024 return detail::NamedNodeOrError(Path, Dir);
1025 }
1026}
1027
1029 const Twine &Target) {
1030 auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
1031 // Whether symlinks in the hardlink target are followed is
1032 // implementation-defined in POSIX.
1033 // We're following symlinks here to be consistent with macOS.
1034 auto TargetNode = lookupNode(Target, /*FollowFinalSymlink=*/true);
1035 // FromPath must not have been added before. ToPath must have been added
1036 // before. Resolved ToPath must be a File.
1037 if (!TargetNode || NewLinkNode || !isa<detail::InMemoryFile>(*TargetNode))
1038 return false;
1039 return addFile(NewLink, 0, nullptr, std::nullopt, std::nullopt, std::nullopt,
1040 std::nullopt, [&](detail::NewInMemoryNodeInfo NNI) {
1041 return std::make_unique<detail::InMemoryHardLink>(
1042 NNI.Path.str(),
1043 *cast<detail::InMemoryFile>(*TargetNode));
1044 });
1045}
1046
1048 const Twine &NewLink, const Twine &Target, time_t ModificationTime,
1049 std::optional<uint32_t> User, std::optional<uint32_t> Group,
1050 std::optional<llvm::sys::fs::perms> Perms) {
1051 auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
1052 if (NewLinkNode)
1053 return false;
1054
1055 SmallString<128> NewLinkStr, TargetStr;
1056 NewLink.toVector(NewLinkStr);
1057 Target.toVector(TargetStr);
1058
1059 return addFile(NewLinkStr, ModificationTime, nullptr, User, Group,
1062 return std::make_unique<detail::InMemorySymbolicLink>(
1063 NewLinkStr, TargetStr, NNI.makeStatus());
1064 });
1065}
1066
1068 auto Node = lookupNode(Path, /*FollowFinalSymlink=*/true);
1069 if (Node)
1070 return (*Node)->getStatus(Path);
1071 return Node.getError();
1072}
1073
1076 auto Node = lookupNode(Path,/*FollowFinalSymlink=*/true);
1077 if (!Node)
1078 return Node.getError();
1079
1080 // When we have a file provide a heap-allocated wrapper for the memory buffer
1081 // to match the ownership semantics for File.
1082 if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
1083 return std::unique_ptr<File>(
1084 new detail::InMemoryFileAdaptor(*F, Path.str()));
1085
1086 // FIXME: errc::not_a_file?
1088}
1089
1090/// Adaptor from InMemoryDir::iterator to directory_iterator.
1092 const InMemoryFileSystem *FS;
1095 std::string RequestedDirName;
1096
1097 void setCurrentEntry() {
1098 if (I != E) {
1099 SmallString<256> Path(RequestedDirName);
1100 llvm::sys::path::append(Path, I->second->getFileName());
1102 switch (I->second->getKind()) {
1103 case detail::IME_File:
1106 break;
1109 break;
1111 if (auto SymlinkTarget =
1112 FS->lookupNode(Path, /*FollowFinalSymlink=*/true)) {
1113 Path = SymlinkTarget.getName();
1114 Type = (*SymlinkTarget)->getStatus(Path).getType();
1115 }
1116 break;
1117 }
1118 CurrentEntry = directory_entry(std::string(Path), Type);
1119 } else {
1120 // When we're at the end, make CurrentEntry invalid and DirIterImpl will
1121 // do the rest.
1123 }
1124 }
1125
1126public:
1127 DirIterator() = default;
1128
1130 const detail::InMemoryDirectory &Dir,
1131 std::string RequestedDirName)
1132 : FS(FS), I(Dir.begin()), E(Dir.end()),
1133 RequestedDirName(std::move(RequestedDirName)) {
1134 setCurrentEntry();
1135 }
1136
1137 std::error_code increment() override {
1138 ++I;
1139 setCurrentEntry();
1140 return {};
1141 }
1142};
1143
1145 std::error_code &EC) {
1146 auto Node = lookupNode(Dir, /*FollowFinalSymlink=*/true);
1147 if (!Node) {
1148 EC = Node.getError();
1149 return directory_iterator(std::make_shared<DirIterator>());
1150 }
1151
1152 if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
1153 return directory_iterator(
1154 std::make_shared<DirIterator>(this, *DirNode, Dir.str()));
1155
1157 return directory_iterator(std::make_shared<DirIterator>());
1158}
1159
1161 SmallString<128> Path;
1162 P.toVector(Path);
1163
1164 // Fix up relative paths. This just prepends the current working directory.
1165 std::error_code EC = makeAbsolute(Path);
1166 assert(!EC);
1167 (void)EC;
1168
1169 if (useNormalizedPaths())
1170 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1171
1172 if (!Path.empty())
1173 WorkingDirectory = std::string(Path);
1174 return {};
1175}
1176
1177std::error_code InMemoryFileSystem::getRealPath(const Twine &Path,
1178 SmallVectorImpl<char> &Output) {
1179 auto CWD = getCurrentWorkingDirectory();
1180 if (!CWD || CWD->empty())
1182 Path.toVector(Output);
1183 if (auto EC = makeAbsolute(Output))
1184 return EC;
1185 llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true);
1186 return {};
1187}
1188
1189std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
1190 Result = false;
1191 return {};
1192}
1193
1194void InMemoryFileSystem::printImpl(raw_ostream &OS, PrintType PrintContents,
1195 unsigned IndentLevel) const {
1196 printIndent(OS, IndentLevel);
1197 OS << "InMemoryFileSystem\n";
1198}
1199
1200} // namespace vfs
1201} // namespace llvm
1202
1203//===-----------------------------------------------------------------------===/
1204// RedirectingFileSystem implementation
1205//===-----------------------------------------------------------------------===/
1206
1207namespace {
1208
1209static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) {
1210 // Detect the path style in use by checking the first separator.
1212 const size_t n = Path.find_first_of("/\\");
1213 // Can't distinguish between posix and windows_slash here.
1214 if (n != static_cast<size_t>(-1))
1215 style = (Path[n] == '/') ? llvm::sys::path::Style::posix
1217 return style;
1218}
1219
1220/// Removes leading "./" as well as path components like ".." and ".".
1221static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {
1222 // First detect the path style in use by checking the first separator.
1223 llvm::sys::path::Style style = getExistingStyle(Path);
1224
1225 // Now remove the dots. Explicitly specifying the path style prevents the
1226 // direction of the slashes from changing.
1227 llvm::SmallString<256> result =
1229 llvm::sys::path::remove_dots(result, /*remove_dot_dot=*/true, style);
1230 return result;
1231}
1232
1233/// Whether the error and entry specify a file/directory that was not found.
1234static bool isFileNotFound(std::error_code EC,
1235 RedirectingFileSystem::Entry *E = nullptr) {
1236 if (E && !isa<RedirectingFileSystem::DirectoryRemapEntry>(E))
1237 return false;
1239}
1240
1241} // anonymous namespace
1242
1243
1244RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
1245 : ExternalFS(std::move(FS)) {
1246 if (ExternalFS)
1247 if (auto ExternalWorkingDirectory =
1248 ExternalFS->getCurrentWorkingDirectory()) {
1249 WorkingDirectory = *ExternalWorkingDirectory;
1250 }
1251}
1252
1253/// Directory iterator implementation for \c RedirectingFileSystem's
1254/// directory entries.
1257 std::string Dir;
1259
1260 std::error_code incrementImpl(bool IsFirstTime) {
1261 assert((IsFirstTime || Current != End) && "cannot iterate past end");
1262 if (!IsFirstTime)
1263 ++Current;
1264 if (Current != End) {
1265 SmallString<128> PathStr(Dir);
1266 llvm::sys::path::append(PathStr, (*Current)->getName());
1268 switch ((*Current)->getKind()) {
1270 [[fallthrough]];
1273 break;
1276 break;
1277 }
1278 CurrentEntry = directory_entry(std::string(PathStr), Type);
1279 } else {
1281 }
1282 return {};
1283 };
1284
1285public:
1288 RedirectingFileSystem::DirectoryEntry::iterator End, std::error_code &EC)
1289 : Dir(Path.str()), Current(Begin), End(End) {
1290 EC = incrementImpl(/*IsFirstTime=*/true);
1291 }
1292
1293 std::error_code increment() override {
1294 return incrementImpl(/*IsFirstTime=*/false);
1295 }
1296};
1297
1298namespace {
1299/// Directory iterator implementation for \c RedirectingFileSystem's
1300/// directory remap entries that maps the paths reported by the external
1301/// file system's directory iterator back to the virtual directory's path.
1302class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl {
1303 std::string Dir;
1304 llvm::sys::path::Style DirStyle;
1305 llvm::vfs::directory_iterator ExternalIter;
1306
1307public:
1308 RedirectingFSDirRemapIterImpl(std::string DirPath,
1310 : Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)),
1311 ExternalIter(ExtIter) {
1312 if (ExternalIter != llvm::vfs::directory_iterator())
1313 setCurrentEntry();
1314 }
1315
1316 void setCurrentEntry() {
1317 StringRef ExternalPath = ExternalIter->path();
1318 llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath);
1319 StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle);
1320
1321 SmallString<128> NewPath(Dir);
1322 llvm::sys::path::append(NewPath, DirStyle, File);
1323
1324 CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type());
1325 }
1326
1327 std::error_code increment() override {
1328 std::error_code EC;
1329 ExternalIter.increment(EC);
1330 if (!EC && ExternalIter != llvm::vfs::directory_iterator())
1331 setCurrentEntry();
1332 else
1333 CurrentEntry = directory_entry();
1334 return EC;
1335 }
1336};
1337} // namespace
1338
1341 return WorkingDirectory;
1342}
1343
1344std::error_code
1346 // Don't change the working directory if the path doesn't exist.
1347 if (!exists(Path))
1349
1350 SmallString<128> AbsolutePath;
1351 Path.toVector(AbsolutePath);
1352 if (std::error_code EC = makeAbsolute(AbsolutePath))
1353 return EC;
1354 WorkingDirectory = std::string(AbsolutePath);
1355 return {};
1356}
1357
1358std::error_code RedirectingFileSystem::isLocal(const Twine &Path_,
1359 bool &Result) {
1360 SmallString<256> Path;
1361 Path_.toVector(Path);
1362
1363 if (makeAbsolute(Path))
1364 return {};
1365
1366 return ExternalFS->isLocal(Path, Result);
1367}
1368
1369std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1370 // is_absolute(..., Style::windows_*) accepts paths with both slash types.
1374 // This covers windows absolute path with forward slash as well, as the
1375 // forward slashes are treated as path seperation in llvm::path
1376 // regardless of what path::Style is used.
1377 return {};
1378
1379 auto WorkingDir = getCurrentWorkingDirectory();
1380 if (!WorkingDir)
1381 return WorkingDir.getError();
1382
1383 return makeAbsolute(WorkingDir.get(), Path);
1384}
1385
1386std::error_code
1387RedirectingFileSystem::makeAbsolute(StringRef WorkingDir,
1388 SmallVectorImpl<char> &Path) const {
1389 // We can't use sys::fs::make_absolute because that assumes the path style
1390 // is native and there is no way to override that. Since we know WorkingDir
1391 // is absolute, we can use it to determine which style we actually have and
1392 // append Path ourselves.
1393 if (!WorkingDir.empty() &&
1395 !sys::path::is_absolute(WorkingDir,
1397 return std::error_code();
1398 }
1402 } else {
1403 // Distinguish between windows_backslash and windows_slash; getExistingStyle
1404 // returns posix for a path with windows_slash.
1405 if (getExistingStyle(WorkingDir) != sys::path::Style::windows_backslash)
1407 }
1408
1409 std::string Result = std::string(WorkingDir);
1410 StringRef Dir(Result);
1411 if (!Dir.ends_with(sys::path::get_separator(style))) {
1413 }
1414 // backslashes '\' are legit path charactors under POSIX. Windows APIs
1415 // like CreateFile accepts forward slashes '/' as path
1416 // separator (even when mixed with backslashes). Therefore,
1417 // `Path` should be directly appended to `WorkingDir` without converting
1418 // path separator.
1419 Result.append(Path.data(), Path.size());
1420 Path.assign(Result.begin(), Result.end());
1421
1422 return {};
1423}
1424
1426 std::error_code &EC) {
1427 SmallString<256> Path;
1428 Dir.toVector(Path);
1429
1430 EC = makeAbsolute(Path);
1431 if (EC)
1432 return {};
1433
1435 if (!Result) {
1436 if (Redirection != RedirectKind::RedirectOnly &&
1437 isFileNotFound(Result.getError()))
1438 return ExternalFS->dir_begin(Path, EC);
1439
1440 EC = Result.getError();
1441 return {};
1442 }
1443
1444 // Use status to make sure the path exists and refers to a directory.
1445 ErrorOr<Status> S = status(Path, Dir, *Result);
1446 if (!S) {
1447 if (Redirection != RedirectKind::RedirectOnly &&
1448 isFileNotFound(S.getError(), Result->E))
1449 return ExternalFS->dir_begin(Dir, EC);
1450
1451 EC = S.getError();
1452 return {};
1453 }
1454
1455 if (!S->isDirectory()) {
1457 return {};
1458 }
1459
1460 // Create the appropriate directory iterator based on whether we found a
1461 // DirectoryRemapEntry or DirectoryEntry.
1462 directory_iterator RedirectIter;
1463 std::error_code RedirectEC;
1464 if (auto ExtRedirect = Result->getExternalRedirect()) {
1465 auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
1466 RedirectIter = ExternalFS->dir_begin(*ExtRedirect, RedirectEC);
1467
1468 if (!RE->useExternalName(UseExternalNames)) {
1469 // Update the paths in the results to use the virtual directory's path.
1470 RedirectIter =
1471 directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>(
1472 std::string(Path), RedirectIter));
1473 }
1474 } else {
1475 auto DE = cast<DirectoryEntry>(Result->E);
1476 RedirectIter =
1477 directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
1478 Path, DE->contents_begin(), DE->contents_end(), RedirectEC));
1479 }
1480
1481 if (RedirectEC) {
1482 if (RedirectEC != errc::no_such_file_or_directory) {
1483 EC = RedirectEC;
1484 return {};
1485 }
1486 RedirectIter = {};
1487 }
1488
1489 if (Redirection == RedirectKind::RedirectOnly) {
1490 EC = RedirectEC;
1491 return RedirectIter;
1492 }
1493
1494 std::error_code ExternalEC;
1495 directory_iterator ExternalIter = ExternalFS->dir_begin(Path, ExternalEC);
1496 if (ExternalEC) {
1497 if (ExternalEC != errc::no_such_file_or_directory) {
1498 EC = ExternalEC;
1499 return {};
1500 }
1501 ExternalIter = {};
1502 }
1503
1505 switch (Redirection) {
1507 Iters.push_back(ExternalIter);
1508 Iters.push_back(RedirectIter);
1509 break;
1511 Iters.push_back(RedirectIter);
1512 Iters.push_back(ExternalIter);
1513 break;
1514 default:
1515 llvm_unreachable("unhandled RedirectKind");
1516 }
1517
1518 directory_iterator Combined{
1519 std::make_shared<CombiningDirIterImpl>(Iters, EC)};
1520 if (EC)
1521 return {};
1522 return Combined;
1523}
1524
1526 OverlayFileDir = Dir.str();
1527}
1528
1530 return OverlayFileDir;
1531}
1532
1534 if (Fallthrough) {
1536 } else {
1538 }
1539}
1540
1543 Redirection = Kind;
1544}
1545
1546std::vector<StringRef> RedirectingFileSystem::getRoots() const {
1547 std::vector<StringRef> R;
1548 R.reserve(Roots.size());
1549 for (const auto &Root : Roots)
1550 R.push_back(Root->getName());
1551 return R;
1552}
1553
1555 unsigned IndentLevel) const {
1556 printIndent(OS, IndentLevel);
1557 OS << "RedirectingFileSystem (UseExternalNames: "
1558 << (UseExternalNames ? "true" : "false") << ")\n";
1559 if (Type == PrintType::Summary)
1560 return;
1561
1562 for (const auto &Root : Roots)
1563 printEntry(OS, Root.get(), IndentLevel);
1564
1565 printIndent(OS, IndentLevel);
1566 OS << "ExternalFS:\n";
1567 ExternalFS->print(OS, Type == PrintType::Contents ? PrintType::Summary : Type,
1568 IndentLevel + 1);
1569}
1570
1573 unsigned IndentLevel) const {
1574 printIndent(OS, IndentLevel);
1575 OS << "'" << E->getName() << "'";
1576
1577 switch (E->getKind()) {
1578 case EK_Directory: {
1579 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(E);
1580
1581 OS << "\n";
1582 for (std::unique_ptr<Entry> &SubEntry :
1583 llvm::make_range(DE->contents_begin(), DE->contents_end()))
1584 printEntry(OS, SubEntry.get(), IndentLevel + 1);
1585 break;
1586 }
1587 case EK_DirectoryRemap:
1588 case EK_File: {
1589 auto *RE = cast<RedirectingFileSystem::RemapEntry>(E);
1590 OS << " -> '" << RE->getExternalContentsPath() << "'";
1591 switch (RE->getUseName()) {
1592 case NK_NotSet:
1593 break;
1594 case NK_External:
1595 OS << " (UseExternalName: true)";
1596 break;
1597 case NK_Virtual:
1598 OS << " (UseExternalName: false)";
1599 break;
1600 }
1601 OS << "\n";
1602 break;
1603 }
1604 }
1605}
1606
1608 if (ExternalFS) {
1609 Callback(*ExternalFS);
1610 ExternalFS->visitChildFileSystems(Callback);
1611 }
1612}
1613
1614/// A helper class to hold the common YAML parsing state.
1616 yaml::Stream &Stream;
1617
1618 void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); }
1619
1620 // false on error
1621 bool parseScalarString(yaml::Node *N, StringRef &Result,
1622 SmallVectorImpl<char> &Storage) {
1623 const auto *S = dyn_cast<yaml::ScalarNode>(N);
1624
1625 if (!S) {
1626 error(N, "expected string");
1627 return false;
1628 }
1629 Result = S->getValue(Storage);
1630 return true;
1631 }
1632
1633 // false on error
1634 bool parseScalarBool(yaml::Node *N, bool &Result) {
1635 SmallString<5> Storage;
1637 if (!parseScalarString(N, Value, Storage))
1638 return false;
1639
1640 if (Value.equals_insensitive("true") || Value.equals_insensitive("on") ||
1641 Value.equals_insensitive("yes") || Value == "1") {
1642 Result = true;
1643 return true;
1644 } else if (Value.equals_insensitive("false") ||
1645 Value.equals_insensitive("off") ||
1646 Value.equals_insensitive("no") || Value == "0") {
1647 Result = false;
1648 return true;
1649 }
1650
1651 error(N, "expected boolean value");
1652 return false;
1653 }
1654
1655 std::optional<RedirectingFileSystem::RedirectKind>
1656 parseRedirectKind(yaml::Node *N) {
1657 SmallString<12> Storage;
1659 if (!parseScalarString(N, Value, Storage))
1660 return std::nullopt;
1661
1662 if (Value.equals_insensitive("fallthrough")) {
1664 } else if (Value.equals_insensitive("fallback")) {
1666 } else if (Value.equals_insensitive("redirect-only")) {
1668 }
1669 return std::nullopt;
1670 }
1671
1672 std::optional<RedirectingFileSystem::RootRelativeKind>
1673 parseRootRelativeKind(yaml::Node *N) {
1674 SmallString<12> Storage;
1676 if (!parseScalarString(N, Value, Storage))
1677 return std::nullopt;
1678 if (Value.equals_insensitive("cwd")) {
1680 } else if (Value.equals_insensitive("overlay-dir")) {
1682 }
1683 return std::nullopt;
1684 }
1685
1686 struct KeyStatus {
1687 bool Required;
1688 bool Seen = false;
1689
1690 KeyStatus(bool Required = false) : Required(Required) {}
1691 };
1692
1693 using KeyStatusPair = std::pair<StringRef, KeyStatus>;
1694
1695 // false on error
1696 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
1698 if (!Keys.count(Key)) {
1699 error(KeyNode, "unknown key");
1700 return false;
1701 }
1702 KeyStatus &S = Keys[Key];
1703 if (S.Seen) {
1704 error(KeyNode, Twine("duplicate key '") + Key + "'");
1705 return false;
1706 }
1707 S.Seen = true;
1708 return true;
1709 }
1710
1711 // false on error
1712 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
1713 for (const auto &I : Keys) {
1714 if (I.second.Required && !I.second.Seen) {
1715 error(Obj, Twine("missing key '") + I.first + "'");
1716 return false;
1717 }
1718 }
1719 return true;
1720 }
1721
1722public:
1725 RedirectingFileSystem::Entry *ParentEntry = nullptr) {
1726 if (!ParentEntry) { // Look for a existent root
1727 for (const auto &Root : FS->Roots) {
1728 if (Name == Root->getName()) {
1729 ParentEntry = Root.get();
1730 return ParentEntry;
1731 }
1732 }
1733 } else { // Advance to the next component
1734 auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
1735 for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :
1736 llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1737 auto *DirContent =
1738 dyn_cast<RedirectingFileSystem::DirectoryEntry>(Content.get());
1739 if (DirContent && Name == Content->getName())
1740 return DirContent;
1741 }
1742 }
1743
1744 // ... or create a new one
1745 std::unique_ptr<RedirectingFileSystem::Entry> E =
1746 std::make_unique<RedirectingFileSystem::DirectoryEntry>(
1748 std::chrono::system_clock::now(), 0, 0, 0,
1749 file_type::directory_file, sys::fs::all_all));
1750
1751 if (!ParentEntry) { // Add a new root to the overlay
1752 FS->Roots.push_back(std::move(E));
1753 ParentEntry = FS->Roots.back().get();
1754 return ParentEntry;
1755 }
1756
1757 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
1758 DE->addContent(std::move(E));
1759 return DE->getLastContent();
1760 }
1761
1762private:
1763 void uniqueOverlayTree(RedirectingFileSystem *FS,
1765 RedirectingFileSystem::Entry *NewParentE = nullptr) {
1766 StringRef Name = SrcE->getName();
1767 switch (SrcE->getKind()) {
1769 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
1770 // Empty directories could be present in the YAML as a way to
1771 // describe a file for a current directory after some of its subdir
1772 // is parsed. This only leads to redundant walks, ignore it.
1773 if (!Name.empty())
1774 NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
1775 for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
1776 llvm::make_range(DE->contents_begin(), DE->contents_end()))
1777 uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
1778 break;
1779 }
1781 assert(NewParentE && "Parent entry must exist");
1782 auto *DR = cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
1783 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1784 DE->addContent(
1785 std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
1786 Name, DR->getExternalContentsPath(), DR->getUseName()));
1787 break;
1788 }
1790 assert(NewParentE && "Parent entry must exist");
1791 auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);
1792 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1793 DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>(
1794 Name, FE->getExternalContentsPath(), FE->getUseName()));
1795 break;
1796 }
1797 }
1798 }
1799
1800 std::unique_ptr<RedirectingFileSystem::Entry>
1801 parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) {
1802 auto *M = dyn_cast<yaml::MappingNode>(N);
1803 if (!M) {
1804 error(N, "expected mapping node for file or directory entry");
1805 return nullptr;
1806 }
1807
1808 KeyStatusPair Fields[] = {
1809 KeyStatusPair("name", true),
1810 KeyStatusPair("type", true),
1811 KeyStatusPair("contents", false),
1812 KeyStatusPair("external-contents", false),
1813 KeyStatusPair("use-external-name", false),
1814 };
1815
1816 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1817
1818 enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;
1819 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
1820 EntryArrayContents;
1821 SmallString<256> ExternalContentsPath;
1823 yaml::Node *NameValueNode = nullptr;
1824 auto UseExternalName = RedirectingFileSystem::NK_NotSet;
1826
1827 for (auto &I : *M) {
1828 StringRef Key;
1829 // Reuse the buffer for key and value, since we don't look at key after
1830 // parsing value.
1831 SmallString<256> Buffer;
1832 if (!parseScalarString(I.getKey(), Key, Buffer))
1833 return nullptr;
1834
1835 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
1836 return nullptr;
1837
1839 if (Key == "name") {
1840 if (!parseScalarString(I.getValue(), Value, Buffer))
1841 return nullptr;
1842
1843 NameValueNode = I.getValue();
1844 // Guarantee that old YAML files containing paths with ".." and "."
1845 // are properly canonicalized before read into the VFS.
1846 Name = canonicalize(Value).str();
1847 } else if (Key == "type") {
1848 if (!parseScalarString(I.getValue(), Value, Buffer))
1849 return nullptr;
1850 if (Value == "file")
1852 else if (Value == "directory")
1854 else if (Value == "directory-remap")
1856 else {
1857 error(I.getValue(), "unknown value for 'type'");
1858 return nullptr;
1859 }
1860 } else if (Key == "contents") {
1861 if (ContentsField != CF_NotSet) {
1862 error(I.getKey(),
1863 "entry already has 'contents' or 'external-contents'");
1864 return nullptr;
1865 }
1866 ContentsField = CF_List;
1867 auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
1868 if (!Contents) {
1869 // FIXME: this is only for directories, what about files?
1870 error(I.getValue(), "expected array");
1871 return nullptr;
1872 }
1873
1874 for (auto &I : *Contents) {
1875 if (std::unique_ptr<RedirectingFileSystem::Entry> E =
1876 parseEntry(&I, FS, /*IsRootEntry*/ false))
1877 EntryArrayContents.push_back(std::move(E));
1878 else
1879 return nullptr;
1880 }
1881 } else if (Key == "external-contents") {
1882 if (ContentsField != CF_NotSet) {
1883 error(I.getKey(),
1884 "entry already has 'contents' or 'external-contents'");
1885 return nullptr;
1886 }
1887 ContentsField = CF_External;
1888 if (!parseScalarString(I.getValue(), Value, Buffer))
1889 return nullptr;
1890
1891 SmallString<256> FullPath;
1892 if (FS->IsRelativeOverlay) {
1893 FullPath = FS->getOverlayFileDir();
1894 assert(!FullPath.empty() &&
1895 "External contents prefix directory must exist");
1896 llvm::sys::path::append(FullPath, Value);
1897 } else {
1898 FullPath = Value;
1899 }
1900
1901 // Guarantee that old YAML files containing paths with ".." and "."
1902 // are properly canonicalized before read into the VFS.
1903 FullPath = canonicalize(FullPath);
1904 ExternalContentsPath = FullPath.str();
1905 } else if (Key == "use-external-name") {
1906 bool Val;
1907 if (!parseScalarBool(I.getValue(), Val))
1908 return nullptr;
1909 UseExternalName = Val ? RedirectingFileSystem::NK_External
1911 } else {
1912 llvm_unreachable("key missing from Keys");
1913 }
1914 }
1915
1916 if (Stream.failed())
1917 return nullptr;
1918
1919 // check for missing keys
1920 if (ContentsField == CF_NotSet) {
1921 error(N, "missing key 'contents' or 'external-contents'");
1922 return nullptr;
1923 }
1924 if (!checkMissingKeys(N, Keys))
1925 return nullptr;
1926
1927 // check invalid configuration
1929 UseExternalName != RedirectingFileSystem::NK_NotSet) {
1930 error(N, "'use-external-name' is not supported for 'directory' entries");
1931 return nullptr;
1932 }
1933
1935 ContentsField == CF_List) {
1936 error(N, "'contents' is not supported for 'directory-remap' entries");
1937 return nullptr;
1938 }
1939
1941 if (IsRootEntry) {
1942 // VFS root entries may be in either Posix or Windows style. Figure out
1943 // which style we have, and use it consistently.
1945 path_style = sys::path::Style::posix;
1946 } else if (sys::path::is_absolute(Name,
1949 } else {
1950 // Relative VFS root entries are made absolute to either the overlay
1951 // directory, or the current working directory, then we can determine
1952 // the path style from that.
1953 std::error_code EC;
1954 if (FS->RootRelative ==
1956 StringRef FullPath = FS->getOverlayFileDir();
1957 assert(!FullPath.empty() && "Overlay file directory must exist");
1958 EC = FS->makeAbsolute(FullPath, Name);
1959 Name = canonicalize(Name);
1960 } else {
1962 }
1963 if (EC) {
1964 assert(NameValueNode && "Name presence should be checked earlier");
1965 error(
1966 NameValueNode,
1967 "entry with relative path at the root level is not discoverable");
1968 return nullptr;
1969 }
1973 }
1974 // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will
1975 // return true even if `Name` is using forward slashes. Distinguish
1976 // between windows_backslash and windows_slash.
1977 if (path_style == sys::path::Style::windows_backslash &&
1978 getExistingStyle(Name) != sys::path::Style::windows_backslash)
1980 }
1981
1982 // Remove trailing slash(es), being careful not to remove the root path
1983 StringRef Trimmed = Name;
1984 size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();
1985 while (Trimmed.size() > RootPathLen &&
1986 sys::path::is_separator(Trimmed.back(), path_style))
1987 Trimmed = Trimmed.slice(0, Trimmed.size() - 1);
1988
1989 // Get the last component
1990 StringRef LastComponent = sys::path::filename(Trimmed, path_style);
1991
1992 std::unique_ptr<RedirectingFileSystem::Entry> Result;
1993 switch (Kind) {
1995 Result = std::make_unique<RedirectingFileSystem::FileEntry>(
1996 LastComponent, std::move(ExternalContentsPath), UseExternalName);
1997 break;
1999 Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
2000 LastComponent, std::move(ExternalContentsPath), UseExternalName);
2001 break;
2003 Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
2004 LastComponent, std::move(EntryArrayContents),
2005 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2006 0, 0, 0, file_type::directory_file, sys::fs::all_all));
2007 break;
2008 }
2009
2010 StringRef Parent = sys::path::parent_path(Trimmed, path_style);
2011 if (Parent.empty())
2012 return Result;
2013
2014 // if 'name' contains multiple components, create implicit directory entries
2015 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style),
2016 E = sys::path::rend(Parent);
2017 I != E; ++I) {
2018 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;
2019 Entries.push_back(std::move(Result));
2020 Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
2021 *I, std::move(Entries),
2022 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2023 0, 0, 0, file_type::directory_file, sys::fs::all_all));
2024 }
2025 return Result;
2026 }
2027
2028public:
2030
2031 // false on error
2033 auto *Top = dyn_cast<yaml::MappingNode>(Root);
2034 if (!Top) {
2035 error(Root, "expected mapping node");
2036 return false;
2037 }
2038
2039 KeyStatusPair Fields[] = {
2040 KeyStatusPair("version", true),
2041 KeyStatusPair("case-sensitive", false),
2042 KeyStatusPair("use-external-names", false),
2043 KeyStatusPair("root-relative", false),
2044 KeyStatusPair("overlay-relative", false),
2045 KeyStatusPair("fallthrough", false),
2046 KeyStatusPair("redirecting-with", false),
2047 KeyStatusPair("roots", true),
2048 };
2049
2050 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
2051 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries;
2052
2053 // Parse configuration and 'roots'
2054 for (auto &I : *Top) {
2055 SmallString<10> KeyBuffer;
2056 StringRef Key;
2057 if (!parseScalarString(I.getKey(), Key, KeyBuffer))
2058 return false;
2059
2060 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
2061 return false;
2062
2063 if (Key == "roots") {
2064 auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue());
2065 if (!Roots) {
2066 error(I.getValue(), "expected array");
2067 return false;
2068 }
2069
2070 for (auto &I : *Roots) {
2071 if (std::unique_ptr<RedirectingFileSystem::Entry> E =
2072 parseEntry(&I, FS, /*IsRootEntry*/ true))
2073 RootEntries.push_back(std::move(E));
2074 else
2075 return false;
2076 }
2077 } else if (Key == "version") {
2078 StringRef VersionString;
2079 SmallString<4> Storage;
2080 if (!parseScalarString(I.getValue(), VersionString, Storage))
2081 return false;
2082 int Version;
2083 if (VersionString.getAsInteger<int>(10, Version)) {
2084 error(I.getValue(), "expected integer");
2085 return false;
2086 }
2087 if (Version < 0) {
2088 error(I.getValue(), "invalid version number");
2089 return false;
2090 }
2091 if (Version != 0) {
2092 error(I.getValue(), "version mismatch, expected 0");
2093 return false;
2094 }
2095 } else if (Key == "case-sensitive") {
2096 if (!parseScalarBool(I.getValue(), FS->CaseSensitive))
2097 return false;
2098 } else if (Key == "overlay-relative") {
2099 if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay))
2100 return false;
2101 } else if (Key == "use-external-names") {
2102 if (!parseScalarBool(I.getValue(), FS->UseExternalNames))
2103 return false;
2104 } else if (Key == "fallthrough") {
2105 if (Keys["redirecting-with"].Seen) {
2106 error(I.getValue(),
2107 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2108 return false;
2109 }
2110
2111 bool ShouldFallthrough = false;
2112 if (!parseScalarBool(I.getValue(), ShouldFallthrough))
2113 return false;
2114
2115 if (ShouldFallthrough) {
2117 } else {
2119 }
2120 } else if (Key == "redirecting-with") {
2121 if (Keys["fallthrough"].Seen) {
2122 error(I.getValue(),
2123 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2124 return false;
2125 }
2126
2127 if (auto Kind = parseRedirectKind(I.getValue())) {
2128 FS->Redirection = *Kind;
2129 } else {
2130 error(I.getValue(), "expected valid redirect kind");
2131 return false;
2132 }
2133 } else if (Key == "root-relative") {
2134 if (auto Kind = parseRootRelativeKind(I.getValue())) {
2135 FS->RootRelative = *Kind;
2136 } else {
2137 error(I.getValue(), "expected valid root-relative kind");
2138 return false;
2139 }
2140 } else {
2141 llvm_unreachable("key missing from Keys");
2142 }
2143 }
2144
2145 if (Stream.failed())
2146 return false;
2147
2148 if (!checkMissingKeys(Top, Keys))
2149 return false;
2150
2151 // Now that we sucessefully parsed the YAML file, canonicalize the internal
2152 // representation to a proper directory tree so that we can search faster
2153 // inside the VFS.
2154 for (auto &E : RootEntries)
2155 uniqueOverlayTree(FS, E.get());
2156
2157 return true;
2158 }
2159};
2160
2161std::unique_ptr<RedirectingFileSystem>
2162RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
2164 StringRef YAMLFilePath, void *DiagContext,
2165 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2166 SourceMgr SM;
2167 yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
2168
2169 SM.setDiagHandler(DiagHandler, DiagContext);
2170 yaml::document_iterator DI = Stream.begin();
2171 yaml::Node *Root = DI->getRoot();
2172 if (DI == Stream.end() || !Root) {
2173 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
2174 return nullptr;
2175 }
2176
2178
2179 std::unique_ptr<RedirectingFileSystem> FS(
2180 new RedirectingFileSystem(ExternalFS));
2181
2182 if (!YAMLFilePath.empty()) {
2183 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
2184 // to each 'external-contents' path.
2185 //
2186 // Example:
2187 // -ivfsoverlay dummy.cache/vfs/vfs.yaml
2188 // yields:
2189 // FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs
2190 //
2191 SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
2192 std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir);
2193 assert(!EC && "Overlay dir final path must be absolute");
2194 (void)EC;
2195 FS->setOverlayFileDir(OverlayAbsDir);
2196 }
2197
2198 if (!P.parse(Root, FS.get()))
2199 return nullptr;
2200
2201 return FS;
2202}
2203
2204std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
2205 ArrayRef<std::pair<std::string, std::string>> RemappedFiles,
2206 bool UseExternalNames, FileSystem &ExternalFS) {
2207 std::unique_ptr<RedirectingFileSystem> FS(
2208 new RedirectingFileSystem(&ExternalFS));
2209 FS->UseExternalNames = UseExternalNames;
2210
2212
2213 for (auto &Mapping : llvm::reverse(RemappedFiles)) {
2214 SmallString<128> From = StringRef(Mapping.first);
2215 SmallString<128> To = StringRef(Mapping.second);
2216 {
2217 auto EC = ExternalFS.makeAbsolute(From);
2218 (void)EC;
2219 assert(!EC && "Could not make absolute path");
2220 }
2221
2222 // Check if we've already mapped this file. The first one we see (in the
2223 // reverse iteration) wins.
2224 RedirectingFileSystem::Entry *&ToEntry = Entries[From];
2225 if (ToEntry)
2226 continue;
2227
2228 // Add parent directories.
2229 RedirectingFileSystem::Entry *Parent = nullptr;
2231 for (auto I = llvm::sys::path::begin(FromDirectory),
2232 E = llvm::sys::path::end(FromDirectory);
2233 I != E; ++I) {
2235 Parent);
2236 }
2237 assert(Parent && "File without a directory?");
2238 {
2239 auto EC = ExternalFS.makeAbsolute(To);
2240 (void)EC;
2241 assert(!EC && "Could not make absolute path");
2242 }
2243
2244 // Add the file.
2245 auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
2247 UseExternalNames ? RedirectingFileSystem::NK_External
2249 ToEntry = NewFile.get();
2250 cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(
2251 std::move(NewFile));
2252 }
2253
2254 return FS;
2255}
2256
2259 : E(E) {
2260 assert(E != nullptr);
2261 // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
2262 // path of the directory it maps to in the external file system plus any
2263 // remaining path components in the provided iterator.
2264 if (auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(E)) {
2265 SmallString<256> Redirect(DRE->getExternalContentsPath());
2266 sys::path::append(Redirect, Start, End,
2267 getExistingStyle(DRE->getExternalContentsPath()));
2268 ExternalRedirect = std::string(Redirect);
2269 }
2270}
2271
2273 llvm::SmallVectorImpl<char> &Result) const {
2274 Result.clear();
2275 for (Entry *Parent : Parents)
2276 llvm::sys::path::append(Result, Parent->getName());
2277 llvm::sys::path::append(Result, E->getName());
2278}
2279
2280std::error_code RedirectingFileSystem::makeCanonicalForLookup(
2281 SmallVectorImpl<char> &Path) const {
2282 if (std::error_code EC = makeAbsolute(Path))
2283 return EC;
2284
2285 llvm::SmallString<256> CanonicalPath =
2286 canonicalize(StringRef(Path.data(), Path.size()));
2287 if (CanonicalPath.empty())
2289
2290 Path.assign(CanonicalPath.begin(), CanonicalPath.end());
2291 return {};
2292}
2293
2296 llvm::SmallString<128> CanonicalPath(Path);
2297 if (std::error_code EC = makeCanonicalForLookup(CanonicalPath))
2298 return EC;
2299
2300 // RedirectOnly means the VFS is always used.
2301 if (UsageTrackingActive && Redirection == RedirectKind::RedirectOnly)
2302 HasBeenUsed = true;
2303
2304 sys::path::const_iterator Start = sys::path::begin(CanonicalPath);
2307 for (const auto &Root : Roots) {
2309 lookupPathImpl(Start, End, Root.get(), Entries);
2310 if (UsageTrackingActive && Result && isa<RemapEntry>(Result->E))
2311 HasBeenUsed = true;
2312 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) {
2313 Result->Parents = std::move(Entries);
2314 return Result;
2315 }
2316 }
2318}
2319
2321RedirectingFileSystem::lookupPathImpl(
2324 llvm::SmallVectorImpl<Entry *> &Entries) const {
2325 assert(!isTraversalComponent(*Start) &&
2326 !isTraversalComponent(From->getName()) &&
2327 "Paths should not contain traversal components");
2328
2329 StringRef FromName = From->getName();
2330
2331 // Forward the search to the next component in case this is an empty one.
2332 if (!FromName.empty()) {
2333 if (!pathComponentMatches(*Start, FromName))
2335
2336 ++Start;
2337
2338 if (Start == End) {
2339 // Match!
2340 return LookupResult(From, Start, End);
2341 }
2342 }
2343
2344 if (isa<RedirectingFileSystem::FileEntry>(From))
2346
2347 if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From))
2348 return LookupResult(From, Start, End);
2349
2350 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From);
2351 for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
2352 llvm::make_range(DE->contents_begin(), DE->contents_end())) {
2353 Entries.push_back(From);
2355 lookupPathImpl(Start, End, DirEntry.get(), Entries);
2356 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
2357 return Result;
2358 Entries.pop_back();
2359 }
2360
2362}
2363
2364static Status getRedirectedFileStatus(const Twine &OriginalPath,
2365 bool UseExternalNames,
2366 Status ExternalStatus) {
2367 // The path has been mapped by some nested VFS and exposes an external path,
2368 // don't override it with the original path.
2369 if (ExternalStatus.ExposesExternalVFSPath)
2370 return ExternalStatus;
2371
2372 Status S = ExternalStatus;
2373 if (!UseExternalNames)
2374 S = Status::copyWithNewName(S, OriginalPath);
2375 else
2376 S.ExposesExternalVFSPath = true;
2377 return S;
2378}
2379
2380ErrorOr<Status> RedirectingFileSystem::status(
2381 const Twine &LookupPath, const Twine &OriginalPath,
2383 if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
2384 SmallString<256> RemappedPath((*ExtRedirect).str());
2385 if (std::error_code EC = makeAbsolute(RemappedPath))
2386 return EC;
2387
2388 ErrorOr<Status> S = ExternalFS->status(RemappedPath);
2389 if (!S)
2390 return S;
2391 S = Status::copyWithNewName(*S, *ExtRedirect);
2392 auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E);
2393 return getRedirectedFileStatus(OriginalPath,
2394 RE->useExternalName(UseExternalNames), *S);
2395 }
2396
2397 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
2398 return Status::copyWithNewName(DE->getStatus(), LookupPath);
2399}
2400
2402RedirectingFileSystem::getExternalStatus(const Twine &LookupPath,
2403 const Twine &OriginalPath) const {
2404 auto Result = ExternalFS->status(LookupPath);
2405
2406 // The path has been mapped by some nested VFS, don't override it with the
2407 // original path.
2408 if (!Result || Result->ExposesExternalVFSPath)
2409 return Result;
2410 return Status::copyWithNewName(Result.get(), OriginalPath);
2411}
2412
2413ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {
2414 SmallString<256> Path;
2415 OriginalPath.toVector(Path);
2416
2417 if (std::error_code EC = makeAbsolute(Path))
2418 return EC;
2419
2420 if (Redirection == RedirectKind::Fallback) {
2421 // Attempt to find the original file first, only falling back to the
2422 // mapped file if that fails.
2423 ErrorOr<Status> S = getExternalStatus(Path, OriginalPath);
2424 if (S)
2425 return S;
2426 }
2427
2429 if (!Result) {
2430 // Was not able to map file, fallthrough to using the original path if
2431 // that was the specified redirection type.
2432 if (Redirection == RedirectKind::Fallthrough &&
2433 isFileNotFound(Result.getError()))
2434 return getExternalStatus(Path, OriginalPath);
2435 return Result.getError();
2436 }
2437
2438 ErrorOr<Status> S = status(Path, OriginalPath, *Result);
2439 if (!S && Redirection == RedirectKind::Fallthrough &&
2440 isFileNotFound(S.getError(), Result->E)) {
2441 // Mapped the file but it wasn't found in the underlying filesystem,
2442 // fallthrough to using the original path if that was the specified
2443 // redirection type.
2444 return getExternalStatus(Path, OriginalPath);
2445 }
2446
2447 return S;
2448}
2449
2450bool RedirectingFileSystem::exists(const Twine &OriginalPath) {
2451 SmallString<256> Path;
2452 OriginalPath.toVector(Path);
2453
2454 if (makeAbsolute(Path))
2455 return false;
2456
2457 if (Redirection == RedirectKind::Fallback) {
2458 // Attempt to find the original file first, only falling back to the
2459 // mapped file if that fails.
2460 if (ExternalFS->exists(Path))
2461 return true;
2462 }
2463
2465 if (!Result) {
2466 // Was not able to map file, fallthrough to using the original path if
2467 // that was the specified redirection type.
2468 if (Redirection == RedirectKind::Fallthrough &&
2469 isFileNotFound(Result.getError()))
2470 return ExternalFS->exists(Path);
2471 return false;
2472 }
2473
2474 std::optional<StringRef> ExtRedirect = Result->getExternalRedirect();
2475 if (!ExtRedirect) {
2476 assert(isa<RedirectingFileSystem::DirectoryEntry>(Result->E));
2477 return true;
2478 }
2479
2480 SmallString<256> RemappedPath((*ExtRedirect).str());
2481 if (makeAbsolute(RemappedPath))
2482 return false;
2483
2484 if (ExternalFS->exists(RemappedPath))
2485 return true;
2486
2487 if (Redirection == RedirectKind::Fallthrough) {
2488 // Mapped the file but it wasn't found in the underlying filesystem,
2489 // fallthrough to using the original path if that was the specified
2490 // redirection type.
2491 return ExternalFS->exists(Path);
2492 }
2493
2494 return false;
2495}
2496
2497namespace {
2498
2499/// Provide a file wrapper with an overriden status.
2500class FileWithFixedStatus : public File {
2501 std::unique_ptr<File> InnerFile;
2502 Status S;
2503
2504public:
2505 FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
2506 : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
2507
2508 ErrorOr<Status> status() override { return S; }
2510
2511 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
2512 bool IsVolatile) override {
2513 return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
2514 IsVolatile);
2515 }
2516
2517 std::error_code close() override { return InnerFile->close(); }
2518
2519 void setPath(const Twine &Path) override { S = S.copyWithNewName(S, Path); }
2520};
2521
2522} // namespace
2523
2525File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
2526 // See \c getRedirectedFileStatus - don't update path if it's exposing an
2527 // external path.
2528 if (!Result || (*Result)->status()->ExposesExternalVFSPath)
2529 return Result;
2530
2531 ErrorOr<std::unique_ptr<File>> F = std::move(*Result);
2532 auto Name = F->get()->getName();
2533 if (Name && Name.get() != P.str())
2534 F->get()->setPath(P);
2535 return F;
2536}
2537
2540 SmallString<256> Path;
2541 OriginalPath.toVector(Path);
2542
2543 if (std::error_code EC = makeAbsolute(Path))
2544 return EC;
2545
2546 if (Redirection == RedirectKind::Fallback) {
2547 // Attempt to find the original file first, only falling back to the
2548 // mapped file if that fails.
2549 auto F = File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2550 if (F)
2551 return F;
2552 }
2553
2555 if (!Result) {
2556 // Was not able to map file, fallthrough to using the original path if
2557 // that was the specified redirection type.
2558 if (Redirection == RedirectKind::Fallthrough &&
2559 isFileNotFound(Result.getError()))
2560 return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2561 return Result.getError();
2562 }
2563
2564 if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
2566
2567 StringRef ExtRedirect = *Result->getExternalRedirect();
2568 SmallString<256> RemappedPath(ExtRedirect.str());
2569 if (std::error_code EC = makeAbsolute(RemappedPath))
2570 return EC;
2571
2572 auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
2573
2574 auto ExternalFile =
2575 File::getWithPath(ExternalFS->openFileForRead(RemappedPath), ExtRedirect);
2576 if (!ExternalFile) {
2577 if (Redirection == RedirectKind::Fallthrough &&
2578 isFileNotFound(ExternalFile.getError(), Result->E)) {
2579 // Mapped the file but it wasn't found in the underlying filesystem,
2580 // fallthrough to using the original path if that was the specified
2581 // redirection type.
2582 return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2583 }
2584 return ExternalFile;
2585 }
2586
2587 auto ExternalStatus = (*ExternalFile)->status();
2588 if (!ExternalStatus)
2589 return ExternalStatus.getError();
2590
2591 // Otherwise, the file was successfully remapped. Mark it as such. Also
2592 // replace the underlying path if the external name is being used.
2594 OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus);
2595 return std::unique_ptr<File>(
2596 std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
2597}
2598
2599std::error_code
2601 SmallVectorImpl<char> &Output) {
2602 SmallString<256> Path;
2603 OriginalPath.toVector(Path);
2604
2605 if (std::error_code EC = makeAbsolute(Path))
2606 return EC;
2607
2608 if (Redirection == RedirectKind::Fallback) {
2609 // Attempt to find the original file first, only falling back to the
2610 // mapped file if that fails.
2611 std::error_code EC = ExternalFS->getRealPath(Path, Output);
2612 if (!EC)
2613 return EC;
2614 }
2615
2617 if (!Result) {
2618 // Was not able to map file, fallthrough to using the original path if
2619 // that was the specified redirection type.
2620 if (Redirection == RedirectKind::Fallthrough &&
2621 isFileNotFound(Result.getError()))
2622 return ExternalFS->getRealPath(Path, Output);
2623 return Result.getError();
2624 }
2625
2626 // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2627 // path in the external file system.
2628 if (auto ExtRedirect = Result->getExternalRedirect()) {
2629 auto P = ExternalFS->getRealPath(*ExtRedirect, Output);
2630 if (P && Redirection == RedirectKind::Fallthrough &&
2631 isFileNotFound(P, Result->E)) {
2632 // Mapped the file but it wasn't found in the underlying filesystem,
2633 // fallthrough to using the original path if that was the specified
2634 // redirection type.
2635 return ExternalFS->getRealPath(Path, Output);
2636 }
2637 return P;
2638 }
2639
2640 // We found a DirectoryEntry, which does not have a single external contents
2641 // path. Use the canonical virtual path.
2642 if (Redirection == RedirectKind::Fallthrough) {
2643 Result->getPath(Output);
2644 return {};
2645 }
2647}
2648
2649std::unique_ptr<FileSystem>
2650vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
2652 StringRef YAMLFilePath, void *DiagContext,
2653 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2654 return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
2655 YAMLFilePath, DiagContext,
2656 std::move(ExternalFS));
2657}
2658
2662 auto Kind = SrcE->getKind();
2664 auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
2665 assert(DE && "Must be a directory");
2666 for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
2667 llvm::make_range(DE->contents_begin(), DE->contents_end())) {
2668 Path.push_back(SubEntry->getName());
2669 getVFSEntries(SubEntry.get(), Path, Entries);
2670 Path.pop_back();
2671 }
2672 return;
2673 }
2674
2676 auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
2677 assert(DR && "Must be a directory remap");
2678 SmallString<128> VPath;
2679 for (auto &Comp : Path)
2680 llvm::sys::path::append(VPath, Comp);
2681 Entries.push_back(
2682 YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
2683 return;
2684 }
2685
2686 assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
2687 auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
2688 assert(FE && "Must be a file");
2689 SmallString<128> VPath;
2690 for (auto &Comp : Path)
2691 llvm::sys::path::append(VPath, Comp);
2692 Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
2693}
2694
2695void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
2697 StringRef YAMLFilePath,
2698 SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
2699 void *DiagContext,
2700 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2701 std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create(
2702 std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
2703 std::move(ExternalFS));
2704 if (!VFS)
2705 return;
2707 VFS->lookupPath("/");
2708 if (!RootResult)
2709 return;
2710 SmallVector<StringRef, 8> Components;
2711 Components.push_back("/");
2712 getVFSEntries(RootResult->E, Components, CollectedEntries);
2713}
2714
2716 static std::atomic<unsigned> UID;
2717 unsigned ID = ++UID;
2718 // The following assumes that uint64_t max will never collide with a real
2719 // dev_t value from the OS.
2720 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
2721}
2722
2723void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath,
2724 bool IsDirectory) {
2725 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
2726 assert(sys::path::is_absolute(RealPath) && "real path not absolute");
2727 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
2728 Mappings.emplace_back(VirtualPath, RealPath, IsDirectory);
2729}
2730
2732 addEntry(VirtualPath, RealPath, /*IsDirectory=*/false);
2733}
2734
2736 StringRef RealPath) {
2737 addEntry(VirtualPath, RealPath, /*IsDirectory=*/true);
2738}
2739
2740namespace {
2741
2742class JSONWriter {
2745
2746 unsigned getDirIndent() { return 4 * DirStack.size(); }
2747 unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
2748 bool containedIn(StringRef Parent, StringRef Path);
2749 StringRef containedPart(StringRef Parent, StringRef Path);
2750 void startDirectory(StringRef Path);
2751 void endDirectory();
2752 void writeEntry(StringRef VPath, StringRef RPath);
2753
2754public:
2755 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
2756
2757 void write(ArrayRef<YAMLVFSEntry> Entries,
2758 std::optional<bool> UseExternalNames,
2759 std::optional<bool> IsCaseSensitive,
2760 std::optional<bool> IsOverlayRelative, StringRef OverlayDir);
2761};
2762
2763} // namespace
2764
2765bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
2766 using namespace llvm::sys;
2767
2768 // Compare each path component.
2769 auto IParent = path::begin(Parent), EParent = path::end(Parent);
2770 for (auto IChild = path::begin(Path), EChild = path::end(Path);
2771 IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
2772 if (*IParent != *IChild)
2773 return false;
2774 }
2775 // Have we exhausted the parent path?
2776 return IParent == EParent;
2777}
2778
2779StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
2780 assert(!Parent.empty());
2781 assert(containedIn(Parent, Path));
2782 return Path.slice(Parent.size() + 1, StringRef::npos);
2783}
2784
2785void JSONWriter::startDirectory(StringRef Path) {
2786 StringRef Name =
2787 DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
2788 DirStack.push_back(Path);
2789 unsigned Indent = getDirIndent();
2790 OS.indent(Indent) << "{\n";
2791 OS.indent(Indent + 2) << "'type': 'directory',\n";
2792 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
2793 OS.indent(Indent + 2) << "'contents': [\n";
2794}
2795
2796void JSONWriter::endDirectory() {
2797 unsigned Indent = getDirIndent();
2798 OS.indent(Indent + 2) << "]\n";
2799 OS.indent(Indent) << "}";
2800
2801 DirStack.pop_back();
2802}
2803
2804void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
2805 unsigned Indent = getFileIndent();
2806 OS.indent(Indent) << "{\n";
2807 OS.indent(Indent + 2) << "'type': 'file',\n";
2808 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
2809 OS.indent(Indent + 2) << "'external-contents': \""
2810 << llvm::yaml::escape(RPath) << "\"\n";
2811 OS.indent(Indent) << "}";
2812}
2813
2814void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
2815 std::optional<bool> UseExternalNames,
2816 std::optional<bool> IsCaseSensitive,
2817 std::optional<bool> IsOverlayRelative,
2819 using namespace llvm::sys;
2820
2821 OS << "{\n"
2822 " 'version': 0,\n";
2823 if (IsCaseSensitive)
2824 OS << " 'case-sensitive': '" << (*IsCaseSensitive ? "true" : "false")
2825 << "',\n";
2826 if (UseExternalNames)
2827 OS << " 'use-external-names': '" << (*UseExternalNames ? "true" : "false")
2828 << "',\n";
2829 bool UseOverlayRelative = false;
2830 if (IsOverlayRelative) {
2831 UseOverlayRelative = *IsOverlayRelative;
2832 OS << " 'overlay-relative': '" << (UseOverlayRelative ? "true" : "false")
2833 << "',\n";
2834 }
2835 OS << " 'roots': [\n";
2836
2837 if (!Entries.empty()) {
2838 const YAMLVFSEntry &Entry = Entries.front();
2839
2840 startDirectory(
2841 Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath)
2842 );
2843
2844 StringRef RPath = Entry.RPath;
2845 if (UseOverlayRelative) {
2846 assert(RPath.starts_with(OverlayDir) &&
2847 "Overlay dir must be contained in RPath");
2848 RPath = RPath.slice(OverlayDir.size(), RPath.size());
2849 }
2850
2851 bool IsCurrentDirEmpty = true;
2852 if (!Entry.IsDirectory) {
2853 writeEntry(path::filename(Entry.VPath), RPath);
2854 IsCurrentDirEmpty = false;
2855 }
2856
2857 for (const auto &Entry : Entries.slice(1)) {
2858 StringRef Dir =
2859 Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath);
2860 if (Dir == DirStack.back()) {
2861 if (!IsCurrentDirEmpty) {
2862 OS << ",\n";
2863 }
2864 } else {
2865 bool IsDirPoppedFromStack = false;
2866 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
2867 OS << "\n";
2868 endDirectory();
2869 IsDirPoppedFromStack = true;
2870 }
2871 if (IsDirPoppedFromStack || !IsCurrentDirEmpty) {
2872 OS << ",\n";
2873 }
2874 startDirectory(Dir);
2875 IsCurrentDirEmpty = true;
2876 }
2877 StringRef RPath = Entry.RPath;
2878 if (UseOverlayRelative) {
2879 assert(RPath.starts_with(OverlayDir) &&
2880 "Overlay dir must be contained in RPath");
2881 RPath = RPath.slice(OverlayDir.size(), RPath.size());
2882 }
2883 if (!Entry.IsDirectory) {
2884 writeEntry(path::filename(Entry.VPath), RPath);
2885 IsCurrentDirEmpty = false;
2886 }
2887 }
2888
2889 while (!DirStack.empty()) {
2890 OS << "\n";
2891 endDirectory();
2892 }
2893 OS << "\n";
2894 }
2895
2896 OS << " ]\n"
2897 << "}\n";
2898}
2899
2901 llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
2902 return LHS.VPath < RHS.VPath;
2903 });
2904
2905 JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
2906 IsOverlayRelative, OverlayDir);
2907}
2908
2910 FileSystem &FS_, const Twine &Path, std::error_code &EC)
2911 : FS(&FS_) {
2912 directory_iterator I = FS->dir_begin(Path, EC);
2913 if (I != directory_iterator()) {
2914 State = std::make_shared<detail::RecDirIterState>();
2915 State->Stack.push(I);
2916 }
2917}
2918
2921 assert(FS && State && !State->Stack.empty() && "incrementing past end");
2922 assert(!State->Stack.top()->path().empty() && "non-canonical end iterator");
2924
2925 if (State->HasNoPushRequest)
2926 State->HasNoPushRequest = false;
2927 else {
2928 if (State->Stack.top()->type() == sys::fs::file_type::directory_file) {
2929 vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC);
2930 if (I != End) {
2931 State->Stack.push(I);
2932 return *this;
2933 }
2934 }
2935 }
2936
2937 while (!State->Stack.empty() && State->Stack.top().increment(EC) == End)
2938 State->Stack.pop();
2939
2940 if (State->Stack.empty())
2941 State.reset(); // end iterator
2942
2943 return *this;
2944}
2945
2946const char FileSystem::ID = 0;
2947const char OverlayFileSystem::ID = 0;
2948const char ProxyFileSystem::ID = 0;
2949const char InMemoryFileSystem::ID = 0;
2950const 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:474
Error takeError()
Take ownership of the stored error.
Definition: Error.h:601
A smart pointer to a reference-counted object that inherits from RefCountedBase or ThreadSafeRefCount...
This interface provides simple read-only access to a block of memory, and provides simple methods for...
Definition: MemoryBuffer.h:51
static ErrorOr< std::unique_ptr< MemoryBuffer > > getOpenFile(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Given an already-open file descriptor, read the file and return a MemoryBuffer.
static std::unique_ptr< MemoryBuffer > getMemBuffer(StringRef InputData, StringRef BufferName="", bool RequiresNullTerminator=true)
Open the specified memory range as a MemoryBuffer.
virtual StringRef getBufferIdentifier() const
Return an identifier for this buffer, typically the filename it was read from.
Definition: MemoryBuffer.h:76
StringRef getBuffer() const
Definition: MemoryBuffer.h:70
Represents a location in source code.
Definition: SMLoc.h:23
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
const char * c_str()
Definition: SmallString.h:259
StringRef str() const
Explicit conversion to StringRef.
Definition: SmallString.h:254
bool empty() const
Definition: SmallVector.h:94
size_t size() const
Definition: SmallVector.h:91
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:586
void push_back(const T &Elt)
Definition: SmallVector.h:426
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1209
This owns the files read by a parser, handles include stacks, and handles diagnostic wrangling.
Definition: SourceMgr.h:31
void PrintMessage(raw_ostream &OS, SMLoc Loc, DiagKind Kind, const Twine &Msg, ArrayRef< SMRange > Ranges={}, ArrayRef< SMFixIt > FixIts={}, bool ShowColors=true) const
Emit a message about the specified location with the specified string.
Definition: SourceMgr.cpp:352
void(*)(const SMDiagnostic &, void *Context) DiagHandlerTy
Clients that want to handle their own diagnostics in a custom way can register a function pointer+con...
Definition: SourceMgr.h:43
void setDiagHandler(DiagHandlerTy DH, void *Ctx=nullptr)
Specify a diagnostic handler to be invoked every time PrintMessage is called.
Definition: SourceMgr.h:112
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:128
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition: StringRef.h:462
std::string str() const
str - Get the contents as an std::string.
Definition: StringRef.h:222
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition: StringRef.h:257
constexpr bool empty() const
empty - Check if the string is empty.
Definition: StringRef.h:134
char back() const
back - Get the last character in the string.
Definition: StringRef.h:146
StringRef slice(size_t Start, size_t End) const
Return a reference to the substring from [Start, End).
Definition: StringRef.h:676
constexpr size_t size() const
size - Get the string size.
Definition: StringRef.h:137
static constexpr size_t npos
Definition: StringRef.h:52
StringSet - A wrapper for StringMap that provides set-like functionality.
Definition: StringSet.h:23
std::pair< typename Base::iterator, bool > insert(StringRef key)
Definition: StringSet.h:38
Target - Wrapper for Target specific information.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:81
std::string str() const
Return the twine contents as a std::string.
Definition: Twine.cpp:17
void toVector(SmallVectorImpl< char > &Out) const
Append the concatenated string into the given SmallString or SmallVector.
Definition: Twine.cpp:32
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
LLVM Value Representation.
Definition: Value.h:74
An efficient, type-erasing, non-owning reference to a callable.
An opaque object representing a hash code.
Definition: Hashing.h:74
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
raw_ostream & indent(unsigned NumSpaces)
indent - Insert 'NumSpaces' spaces.
uint64_t getFile() const
Definition: UniqueID.h:48
file_type type() const
Definition: FileSystem.h:1383
const std::string & path() const
Definition: FileSystem.h:1375
directory_iterator - Iterates through the entries in path.
Definition: FileSystem.h:1421
directory_iterator & increment(std::error_code &ec)
Definition: FileSystem.h:1447
Represents the result of a call to sys::fs::status().
Definition: FileSystem.h:226
Reverse path iterator.
Definition: Path.h:101
The virtual file system interface.
virtual llvm::ErrorOr< std::string > getCurrentWorkingDirectory() const =0
Get the working directory of this file system.
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.
@ FS
Definition: X86.h:206
std::optional< const char * > toString(const std::optional< DWARFFormValue > &V)
Take an optional DWARFFormValue and try to extract a string value from it.
@ Resolved
Queried, materialization begun.
void make_absolute(const Twine &current_directory, SmallVectorImpl< char > &path)
Make path an absolute path.
Definition: Path.cpp:907
const file_t kInvalidFile
std::error_code real_path(const Twine &path, SmallVectorImpl< char > &output, bool expand_tilde=false)
Collapse all .
std::error_code closeFile(file_t &F)
Close the file object.
std::error_code openFileForRead(const Twine &Name, int &ResultFD, OpenFlags Flags=OF_None, SmallVectorImpl< char > *RealPath=nullptr)
Opens the file with the given name in a read-only mode, returning its open file descriptor.
std::error_code status(const Twine &path, file_status &result, bool follow=true)
Get file status as if by POSIX stat().
file_type
An enumeration for the file system's view of the type.
Definition: FileSystem.h:66
std::error_code set_current_path(const Twine &path)
Set the current path.
std::error_code is_local(const Twine &path, bool &result)
Is the file mounted on a local filesystem?
Expected< file_t > openNativeFileForRead(const Twine &Name, OpenFlags Flags=OF_None, SmallVectorImpl< char > *RealPath=nullptr)
Opens the file with the given name in a read-only mode, returning its open file descriptor.
std::error_code current_path(SmallVectorImpl< char > &result)
Get the current path.
bool is_directory(const basic_file_status &status)
Does status represent a directory?
Definition: Path.cpp:1093
StringRef get_separator(Style style=Style::native)
Return the preferred separator for this platform.
Definition: Path.cpp:610
const_iterator begin(StringRef path, Style style=Style::native)
Get begin iterator over path.
Definition: Path.cpp:227
reverse_iterator rend(StringRef path)
Get reverse end iterator over path.
Definition: Path.cpp:307
const_iterator end(StringRef path)
Get end iterator over path.
Definition: Path.cpp:236
bool remove_dots(SmallVectorImpl< char > &path, bool remove_dot_dot=false, Style style=Style::native)
In-place remove any '.
Definition: Path.cpp:716
StringRef filename(StringRef path, Style style=Style::native)
Get filename.
Definition: Path.cpp:578
reverse_iterator rbegin(StringRef path, Style style=Style::native)
Get reverse begin iterator over path.
Definition: Path.cpp:298
StringRef parent_path(StringRef path, Style style=Style::native)
Get parent path.
Definition: Path.cpp:468
bool is_absolute(const Twine &path, Style style=Style::native)
Is path absolute?
Definition: Path.cpp:672
void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition: Path.cpp:457
StringRef root_path(StringRef path, Style style=Style::native)
Get root path.
Definition: Path.cpp:349
bool is_separator(char value, Style style=Style::native)
Check whether the given char is a path separator on the host OS.
Definition: Path.cpp:602
StringRef remove_leading_dotslash(StringRef path, Style style=Style::native)
Remove redundant leading "./" pieces and consecutive separators.
Definition: Path.cpp:704
std::chrono::time_point< std::chrono::system_clock, D > TimePoint
A time point on the system clock.
Definition: Chrono.h:34
TimePoint< std::chrono::seconds > toTimePoint(std::time_t T)
Convert a std::time_t to a TimePoint.
Definition: Chrono.h:65
void collectVFSFromYAML(std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, SmallVectorImpl< YAMLVFSEntry > &CollectedEntries, void *DiagContext=nullptr, IntrusiveRefCntPtr< FileSystem > ExternalFS=getRealFileSystem())
Collect all pairs of <virtual path, real path> entries from the YAMLFilePath.
std::unique_ptr< FileSystem > getVFSFromYAML(std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, void *DiagContext=nullptr, IntrusiveRefCntPtr< FileSystem > ExternalFS=getRealFileSystem())
Gets a FileSystem for a virtual file system described in YAML format.
std::unique_ptr< FileSystem > createPhysicalFileSystem()
Create an vfs::FileSystem for the 'real' file system, as seen by the operating system.
static sys::fs::UniqueID getFileID(sys::fs::UniqueID Parent, llvm::StringRef Name, llvm::StringRef Contents)
llvm::sys::fs::UniqueID getNextVirtualUniqueID()
Get a globally unique ID for a virtual file or directory.
static sys::fs::UniqueID getUniqueID(hash_code Hash)
IntrusiveRefCntPtr< FileSystem > getRealFileSystem()
Gets an vfs::FileSystem for the 'real' file system, as seen by the operating system.
static sys::fs::UniqueID getDirectoryID(sys::fs::UniqueID Parent, llvm::StringRef Name)
std::string escape(StringRef Input, bool EscapePrintable=true)
Escape Input for a double quoted scalar; if EscapePrintable is true, all UTF8 sequences will be escap...
Definition: YAMLParser.cpp:705
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
std::error_code make_error_code(BitcodeError E)
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
@ no_such_file_or_directory
@ operation_not_permitted
auto reverse(ContainerTy &&C)
Definition: STLExtras.h:419
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
Error write(MCStreamer &Out, ArrayRef< std::string > Inputs, OnCuIndexOverflow OverflowOptValue)
Definition: DWP.cpp:601
void sort(IteratorTy Start, IteratorTy End)
Definition: STLExtras.h:1647
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
@ Other
Any other memory.
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1849
hash_code hash_combine(const Ts &...args)
Combine values into a single hash_code.
Definition: Hashing.h:613
std::error_code errorToErrorCode(Error Err)
Helper for converting an ECError to a std::error_code.
Definition: Error.cpp:109
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858
#define N
Status()=default
Represents the result of a path lookup into the RedirectingFileSystem.
Entry * E
The entry the looked-up path corresponds to.
LookupResult(Entry *E, sys::path::const_iterator Start, sys::path::const_iterator End)
void getPath(llvm::SmallVectorImpl< char > &Path) const
Get the (canonical) path of the found entry.
An interface for virtual file systems to provide an iterator over the (non-recursive) contents of a d...
std::unique_ptr< llvm::MemoryBuffer > Buffer