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