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