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