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