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