LLVM 22.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
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 = 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 if (ExternalFS)
1295 if (auto ExternalWorkingDirectory =
1296 ExternalFS->getCurrentWorkingDirectory()) {
1297 WorkingDirectory = *ExternalWorkingDirectory;
1298 }
1299}
1300
1301/// Directory iterator implementation for \c RedirectingFileSystem's
1302/// directory entries.
1305 std::string Dir;
1307
1308 std::error_code incrementImpl(bool IsFirstTime) {
1309 assert((IsFirstTime || Current != End) && "cannot iterate past end");
1310 if (!IsFirstTime)
1311 ++Current;
1312 if (Current != End) {
1313 SmallString<128> PathStr(Dir);
1314 llvm::sys::path::append(PathStr, (*Current)->getName());
1316 switch ((*Current)->getKind()) {
1318 [[fallthrough]];
1321 break;
1324 break;
1325 }
1326 CurrentEntry = directory_entry(std::string(PathStr), Type);
1327 } else {
1329 }
1330 return {};
1331 };
1332
1333public:
1336 RedirectingFileSystem::DirectoryEntry::iterator End, std::error_code &EC)
1337 : Dir(Path.str()), Current(Begin), End(End) {
1338 EC = incrementImpl(/*IsFirstTime=*/true);
1339 }
1340
1341 std::error_code increment() override {
1342 return incrementImpl(/*IsFirstTime=*/false);
1343 }
1344};
1345
1346namespace {
1347/// Directory iterator implementation for \c RedirectingFileSystem's
1348/// directory remap entries that maps the paths reported by the external
1349/// file system's directory iterator back to the virtual directory's path.
1350class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl {
1351 std::string Dir;
1352 llvm::sys::path::Style DirStyle;
1353 llvm::vfs::directory_iterator ExternalIter;
1354
1355public:
1356 RedirectingFSDirRemapIterImpl(std::string DirPath,
1358 : Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)),
1359 ExternalIter(ExtIter) {
1360 if (ExternalIter != llvm::vfs::directory_iterator())
1361 setCurrentEntry();
1362 }
1363
1364 void setCurrentEntry() {
1365 StringRef ExternalPath = ExternalIter->path();
1366 llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath);
1367 StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle);
1368
1369 SmallString<128> NewPath(Dir);
1370 llvm::sys::path::append(NewPath, DirStyle, File);
1371
1372 CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type());
1373 }
1374
1375 std::error_code increment() override {
1376 std::error_code EC;
1377 ExternalIter.increment(EC);
1378 if (!EC && ExternalIter != llvm::vfs::directory_iterator())
1379 setCurrentEntry();
1380 else
1381 CurrentEntry = directory_entry();
1382 return EC;
1383 }
1384};
1385} // namespace
1386
1387llvm::ErrorOr<std::string>
1389 return WorkingDirectory;
1390}
1391
1392std::error_code
1394 // Don't change the working directory if the path doesn't exist.
1395 if (!exists(Path))
1397
1398 SmallString<128> AbsolutePath;
1399 Path.toVector(AbsolutePath);
1400 if (std::error_code EC = makeAbsolute(AbsolutePath))
1401 return EC;
1402 WorkingDirectory = std::string(AbsolutePath);
1403 return {};
1404}
1405
1406std::error_code RedirectingFileSystem::isLocal(const Twine &Path_,
1407 bool &Result) {
1408 SmallString<256> Path;
1409 Path_.toVector(Path);
1410
1411 if (makeAbsolute(Path))
1412 return {};
1413
1414 return ExternalFS->isLocal(Path, Result);
1415}
1416
1417std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1418 // is_absolute(..., Style::windows_*) accepts paths with both slash types.
1422 // This covers windows absolute path with forward slash as well, as the
1423 // forward slashes are treated as path separation in llvm::path
1424 // regardless of what path::Style is used.
1425 return {};
1426
1427 auto WorkingDir = getCurrentWorkingDirectory();
1428 if (!WorkingDir)
1429 return WorkingDir.getError();
1430
1431 return makeAbsolute(WorkingDir.get(), Path);
1432}
1433
1434std::error_code
1435RedirectingFileSystem::makeAbsolute(StringRef WorkingDir,
1436 SmallVectorImpl<char> &Path) const {
1437 // We can't use sys::fs::make_absolute because that assumes the path style
1438 // is native and there is no way to override that. Since we know WorkingDir
1439 // is absolute, we can use it to determine which style we actually have and
1440 // append Path ourselves.
1441 if (!WorkingDir.empty() &&
1443 !sys::path::is_absolute(WorkingDir,
1445 return std::error_code();
1446 }
1450 } else {
1451 // Distinguish between windows_backslash and windows_slash; getExistingStyle
1452 // returns posix for a path with windows_slash.
1453 if (getExistingStyle(WorkingDir) != sys::path::Style::windows_backslash)
1455 }
1456
1457 std::string Result = std::string(WorkingDir);
1458 StringRef Dir(Result);
1459 if (!Dir.ends_with(sys::path::get_separator(style))) {
1461 }
1462 // backslashes '\' are legit path charactors under POSIX. Windows APIs
1463 // like CreateFile accepts forward slashes '/' as path
1464 // separator (even when mixed with backslashes). Therefore,
1465 // `Path` should be directly appended to `WorkingDir` without converting
1466 // path separator.
1467 Result.append(Path.data(), Path.size());
1468 Path.assign(Result.begin(), Result.end());
1469
1470 return {};
1471}
1472
1474 std::error_code &EC) {
1475 SmallString<256> Path;
1476 Dir.toVector(Path);
1477
1478 EC = makeAbsolute(Path);
1479 if (EC)
1480 return {};
1481
1483 if (!Result) {
1484 if (Redirection != RedirectKind::RedirectOnly &&
1485 isFileNotFound(Result.getError()))
1486 return ExternalFS->dir_begin(Path, EC);
1487
1488 EC = Result.getError();
1489 return {};
1490 }
1491
1492 // Use status to make sure the path exists and refers to a directory.
1493 ErrorOr<Status> S = status(Path, Dir, *Result);
1494 if (!S) {
1495 if (Redirection != RedirectKind::RedirectOnly &&
1496 isFileNotFound(S.getError(), Result->E))
1497 return ExternalFS->dir_begin(Dir, EC);
1498
1499 EC = S.getError();
1500 return {};
1501 }
1502
1503 if (!S->isDirectory()) {
1505 return {};
1506 }
1507
1508 // Create the appropriate directory iterator based on whether we found a
1509 // DirectoryRemapEntry or DirectoryEntry.
1510 directory_iterator RedirectIter;
1511 std::error_code RedirectEC;
1512 if (auto ExtRedirect = Result->getExternalRedirect()) {
1513 auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
1514 RedirectIter = ExternalFS->dir_begin(*ExtRedirect, RedirectEC);
1515
1516 if (!RE->useExternalName(UseExternalNames)) {
1517 // Update the paths in the results to use the virtual directory's path.
1518 RedirectIter =
1519 directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>(
1520 std::string(Path), RedirectIter));
1521 }
1522 } else {
1523 auto DE = cast<DirectoryEntry>(Result->E);
1524 RedirectIter =
1525 directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
1526 Path, DE->contents_begin(), DE->contents_end(), RedirectEC));
1527 }
1528
1529 if (RedirectEC) {
1530 if (RedirectEC != errc::no_such_file_or_directory) {
1531 EC = RedirectEC;
1532 return {};
1533 }
1534 RedirectIter = {};
1535 }
1536
1537 if (Redirection == RedirectKind::RedirectOnly) {
1538 EC = RedirectEC;
1539 return RedirectIter;
1540 }
1541
1542 std::error_code ExternalEC;
1543 directory_iterator ExternalIter = ExternalFS->dir_begin(Path, ExternalEC);
1544 if (ExternalEC) {
1545 if (ExternalEC != errc::no_such_file_or_directory) {
1546 EC = ExternalEC;
1547 return {};
1548 }
1549 ExternalIter = {};
1550 }
1551
1553 switch (Redirection) {
1555 Iters.push_back(ExternalIter);
1556 Iters.push_back(RedirectIter);
1557 break;
1559 Iters.push_back(RedirectIter);
1560 Iters.push_back(ExternalIter);
1561 break;
1562 default:
1563 llvm_unreachable("unhandled RedirectKind");
1564 }
1565
1566 directory_iterator Combined{
1567 std::make_shared<CombiningDirIterImpl>(Iters, EC)};
1568 if (EC)
1569 return {};
1570 return Combined;
1571}
1572
1574 OverlayFileDir = Dir.str();
1575}
1576
1578 return OverlayFileDir;
1579}
1580
1588
1591 Redirection = Kind;
1592}
1593
1594std::vector<StringRef> RedirectingFileSystem::getRoots() const {
1595 std::vector<StringRef> R;
1596 R.reserve(Roots.size());
1597 for (const auto &Root : Roots)
1598 R.push_back(Root->getName());
1599 return R;
1600}
1601
1603 unsigned IndentLevel) const {
1604 printIndent(OS, IndentLevel);
1605 OS << "RedirectingFileSystem (UseExternalNames: "
1606 << (UseExternalNames ? "true" : "false") << ")\n";
1607 if (Type == PrintType::Summary)
1608 return;
1609
1610 for (const auto &Root : Roots)
1611 printEntry(OS, Root.get(), IndentLevel);
1612
1613 printIndent(OS, IndentLevel);
1614 OS << "ExternalFS:\n";
1615 ExternalFS->print(OS, Type == PrintType::Contents ? PrintType::Summary : Type,
1616 IndentLevel + 1);
1617}
1618
1621 unsigned IndentLevel) const {
1622 printIndent(OS, IndentLevel);
1623 OS << "'" << E->getName() << "'";
1624
1625 switch (E->getKind()) {
1626 case EK_Directory: {
1628
1629 OS << "\n";
1630 for (std::unique_ptr<Entry> &SubEntry :
1631 llvm::make_range(DE->contents_begin(), DE->contents_end()))
1632 printEntry(OS, SubEntry.get(), IndentLevel + 1);
1633 break;
1634 }
1635 case EK_DirectoryRemap:
1636 case EK_File: {
1638 OS << " -> '" << RE->getExternalContentsPath() << "'";
1639 switch (RE->getUseName()) {
1640 case NK_NotSet:
1641 break;
1642 case NK_External:
1643 OS << " (UseExternalName: true)";
1644 break;
1645 case NK_Virtual:
1646 OS << " (UseExternalName: false)";
1647 break;
1648 }
1649 OS << "\n";
1650 break;
1651 }
1652 }
1653}
1654
1656 if (ExternalFS) {
1657 Callback(*ExternalFS);
1658 ExternalFS->visitChildFileSystems(Callback);
1659 }
1660}
1661
1662/// A helper class to hold the common YAML parsing state.
1664 yaml::Stream &Stream;
1665
1666 void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); }
1667
1668 // false on error
1669 bool parseScalarString(yaml::Node *N, StringRef &Result,
1670 SmallVectorImpl<char> &Storage) {
1671 const auto *S = dyn_cast<yaml::ScalarNode>(N);
1672
1673 if (!S) {
1674 error(N, "expected string");
1675 return false;
1676 }
1677 Result = S->getValue(Storage);
1678 return true;
1679 }
1680
1681 // false on error
1682 bool parseScalarBool(yaml::Node *N, bool &Result) {
1683 SmallString<5> Storage;
1685 if (!parseScalarString(N, Value, Storage))
1686 return false;
1687
1688 if (Value.equals_insensitive("true") || Value.equals_insensitive("on") ||
1689 Value.equals_insensitive("yes") || Value == "1") {
1690 Result = true;
1691 return true;
1692 } else if (Value.equals_insensitive("false") ||
1693 Value.equals_insensitive("off") ||
1694 Value.equals_insensitive("no") || Value == "0") {
1695 Result = false;
1696 return true;
1697 }
1698
1699 error(N, "expected boolean value");
1700 return false;
1701 }
1702
1703 std::optional<RedirectingFileSystem::RedirectKind>
1704 parseRedirectKind(yaml::Node *N) {
1705 SmallString<12> Storage;
1707 if (!parseScalarString(N, Value, Storage))
1708 return std::nullopt;
1709
1710 if (Value.equals_insensitive("fallthrough")) {
1712 } else if (Value.equals_insensitive("fallback")) {
1714 } else if (Value.equals_insensitive("redirect-only")) {
1716 }
1717 return std::nullopt;
1718 }
1719
1720 std::optional<RedirectingFileSystem::RootRelativeKind>
1721 parseRootRelativeKind(yaml::Node *N) {
1722 SmallString<12> Storage;
1724 if (!parseScalarString(N, Value, Storage))
1725 return std::nullopt;
1726 if (Value.equals_insensitive("cwd")) {
1728 } else if (Value.equals_insensitive("overlay-dir")) {
1730 }
1731 return std::nullopt;
1732 }
1733
1734 struct KeyStatus {
1735 bool Required;
1736 bool Seen = false;
1737
1738 KeyStatus(bool Required = false) : Required(Required) {}
1739 };
1740
1741 using KeyStatusPair = std::pair<StringRef, KeyStatus>;
1742
1743 // false on error
1744 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
1746 auto It = Keys.find(Key);
1747 if (It == Keys.end()) {
1748 error(KeyNode, "unknown key");
1749 return false;
1750 }
1751 KeyStatus &S = It->second;
1752 if (S.Seen) {
1753 error(KeyNode, Twine("duplicate key '") + Key + "'");
1754 return false;
1755 }
1756 S.Seen = true;
1757 return true;
1758 }
1759
1760 // false on error
1761 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
1762 for (const auto &I : Keys) {
1763 if (I.second.Required && !I.second.Seen) {
1764 error(Obj, Twine("missing key '") + I.first + "'");
1765 return false;
1766 }
1767 }
1768 return true;
1769 }
1770
1771public:
1774 RedirectingFileSystem::Entry *ParentEntry = nullptr) {
1775 if (!ParentEntry) { // Look for a existent root
1776 for (const auto &Root : FS->Roots) {
1777 if (Name == Root->getName()) {
1778 ParentEntry = Root.get();
1779 return ParentEntry;
1780 }
1781 }
1782 } else { // Advance to the next component
1784 for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :
1785 llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1786 auto *DirContent =
1788 if (DirContent && Name == Content->getName())
1789 return DirContent;
1790 }
1791 }
1792
1793 // ... or create a new one
1794 std::unique_ptr<RedirectingFileSystem::Entry> E =
1795 std::make_unique<RedirectingFileSystem::DirectoryEntry>(
1796 Name, Status("", getNextVirtualUniqueID(),
1797 std::chrono::system_clock::now(), 0, 0, 0,
1798 file_type::directory_file, sys::fs::all_all));
1799
1800 if (!ParentEntry) { // Add a new root to the overlay
1801 FS->Roots.push_back(std::move(E));
1802 ParentEntry = FS->Roots.back().get();
1803 return ParentEntry;
1804 }
1805
1806 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
1807 DE->addContent(std::move(E));
1808 return DE->getLastContent();
1809 }
1810
1811private:
1812 void uniqueOverlayTree(RedirectingFileSystem *FS,
1814 RedirectingFileSystem::Entry *NewParentE = nullptr) {
1815 StringRef Name = SrcE->getName();
1816 switch (SrcE->getKind()) {
1819 // Empty directories could be present in the YAML as a way to
1820 // describe a file for a current directory after some of its subdir
1821 // is parsed. This only leads to redundant walks, ignore it.
1822 if (!Name.empty())
1823 NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
1824 for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
1825 llvm::make_range(DE->contents_begin(), DE->contents_end()))
1826 uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
1827 break;
1828 }
1830 assert(NewParentE && "Parent entry must exist");
1832 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1833 DE->addContent(
1834 std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
1835 Name, DR->getExternalContentsPath(), DR->getUseName()));
1836 break;
1837 }
1839 assert(NewParentE && "Parent entry must exist");
1841 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1842 DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>(
1843 Name, FE->getExternalContentsPath(), FE->getUseName()));
1844 break;
1845 }
1846 }
1847 }
1848
1849 std::unique_ptr<RedirectingFileSystem::Entry>
1850 parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) {
1852 if (!M) {
1853 error(N, "expected mapping node for file or directory entry");
1854 return nullptr;
1855 }
1856
1857 KeyStatusPair Fields[] = {
1858 KeyStatusPair("name", true),
1859 KeyStatusPair("type", true),
1860 KeyStatusPair("contents", false),
1861 KeyStatusPair("external-contents", false),
1862 KeyStatusPair("use-external-name", false),
1863 };
1864
1865 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1866
1867 enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;
1868 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
1869 EntryArrayContents;
1870 SmallString<256> ExternalContentsPath;
1871 SmallString<256> Name;
1872 yaml::Node *NameValueNode = nullptr;
1873 auto UseExternalName = RedirectingFileSystem::NK_NotSet;
1875
1876 for (auto &I : *M) {
1877 StringRef Key;
1878 // Reuse the buffer for key and value, since we don't look at key after
1879 // parsing value.
1880 SmallString<256> Buffer;
1881 if (!parseScalarString(I.getKey(), Key, Buffer))
1882 return nullptr;
1883
1884 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
1885 return nullptr;
1886
1887 StringRef Value;
1888 if (Key == "name") {
1889 if (!parseScalarString(I.getValue(), Value, Buffer))
1890 return nullptr;
1891
1892 NameValueNode = I.getValue();
1893 // Guarantee that old YAML files containing paths with ".." and "."
1894 // are properly canonicalized before read into the VFS.
1895 Name = canonicalize(Value).str();
1896 } else if (Key == "type") {
1897 if (!parseScalarString(I.getValue(), Value, Buffer))
1898 return nullptr;
1899 if (Value == "file")
1901 else if (Value == "directory")
1903 else if (Value == "directory-remap")
1905 else {
1906 error(I.getValue(), "unknown value for 'type'");
1907 return nullptr;
1908 }
1909 } else if (Key == "contents") {
1910 if (ContentsField != CF_NotSet) {
1911 error(I.getKey(),
1912 "entry already has 'contents' or 'external-contents'");
1913 return nullptr;
1914 }
1915 ContentsField = CF_List;
1916 auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
1917 if (!Contents) {
1918 // FIXME: this is only for directories, what about files?
1919 error(I.getValue(), "expected array");
1920 return nullptr;
1921 }
1922
1923 for (auto &I : *Contents) {
1924 if (std::unique_ptr<RedirectingFileSystem::Entry> E =
1925 parseEntry(&I, FS, /*IsRootEntry*/ false))
1926 EntryArrayContents.push_back(std::move(E));
1927 else
1928 return nullptr;
1929 }
1930 } else if (Key == "external-contents") {
1931 if (ContentsField != CF_NotSet) {
1932 error(I.getKey(),
1933 "entry already has 'contents' or 'external-contents'");
1934 return nullptr;
1935 }
1936 ContentsField = CF_External;
1937 if (!parseScalarString(I.getValue(), Value, Buffer))
1938 return nullptr;
1939
1940 SmallString<256> FullPath;
1941 if (FS->IsRelativeOverlay) {
1942 FullPath = FS->getOverlayFileDir();
1943 assert(!FullPath.empty() &&
1944 "External contents prefix directory must exist");
1945 SmallString<256> AbsFullPath = Value;
1946 if (FS->makeAbsolute(FullPath, AbsFullPath)) {
1947 error(N, "failed to make 'external-contents' absolute");
1948 return nullptr;
1949 }
1950 FullPath = AbsFullPath;
1951 } else {
1952 FullPath = Value;
1953 }
1954
1955 // Guarantee that old YAML files containing paths with ".." and "."
1956 // are properly canonicalized before read into the VFS.
1957 FullPath = canonicalize(FullPath);
1958 ExternalContentsPath = FullPath.str();
1959 } else if (Key == "use-external-name") {
1960 bool Val;
1961 if (!parseScalarBool(I.getValue(), Val))
1962 return nullptr;
1963 UseExternalName = Val ? RedirectingFileSystem::NK_External
1965 } else {
1966 llvm_unreachable("key missing from Keys");
1967 }
1968 }
1969
1970 if (Stream.failed())
1971 return nullptr;
1972
1973 // check for missing keys
1974 if (ContentsField == CF_NotSet) {
1975 error(N, "missing key 'contents' or 'external-contents'");
1976 return nullptr;
1977 }
1978 if (!checkMissingKeys(N, Keys))
1979 return nullptr;
1980
1981 // check invalid configuration
1983 UseExternalName != RedirectingFileSystem::NK_NotSet) {
1984 error(N, "'use-external-name' is not supported for 'directory' entries");
1985 return nullptr;
1986 }
1987
1989 ContentsField == CF_List) {
1990 error(N, "'contents' is not supported for 'directory-remap' entries");
1991 return nullptr;
1992 }
1993
1994 sys::path::Style path_style = sys::path::Style::native;
1995 if (IsRootEntry) {
1996 // VFS root entries may be in either Posix or Windows style. Figure out
1997 // which style we have, and use it consistently.
1998 if (sys::path::is_absolute(Name, sys::path::Style::posix)) {
1999 path_style = sys::path::Style::posix;
2000 } else if (sys::path::is_absolute(Name,
2001 sys::path::Style::windows_backslash)) {
2002 path_style = sys::path::Style::windows_backslash;
2003 } else {
2004 // Relative VFS root entries are made absolute to either the overlay
2005 // directory, or the current working directory, then we can determine
2006 // the path style from that.
2007 std::error_code EC;
2008 if (FS->RootRelative ==
2009 RedirectingFileSystem::RootRelativeKind::OverlayDir) {
2010 StringRef FullPath = FS->getOverlayFileDir();
2011 assert(!FullPath.empty() && "Overlay file directory must exist");
2012 EC = FS->makeAbsolute(FullPath, Name);
2013 Name = canonicalize(Name);
2014 } else {
2015 EC = FS->makeAbsolute(Name);
2016 }
2017 if (EC) {
2018 assert(NameValueNode && "Name presence should be checked earlier");
2019 error(
2020 NameValueNode,
2021 "entry with relative path at the root level is not discoverable");
2022 return nullptr;
2023 }
2024 path_style = sys::path::is_absolute(Name, sys::path::Style::posix)
2025 ? sys::path::Style::posix
2026 : sys::path::Style::windows_backslash;
2027 }
2028 // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will
2029 // return true even if `Name` is using forward slashes. Distinguish
2030 // between windows_backslash and windows_slash.
2031 if (path_style == sys::path::Style::windows_backslash &&
2032 getExistingStyle(Name) != sys::path::Style::windows_backslash)
2033 path_style = sys::path::Style::windows_slash;
2034 }
2035
2036 // Remove trailing slash(es), being careful not to remove the root path
2037 StringRef Trimmed = Name;
2038 size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();
2039 while (Trimmed.size() > RootPathLen &&
2040 sys::path::is_separator(Trimmed.back(), path_style))
2041 Trimmed = Trimmed.slice(0, Trimmed.size() - 1);
2042
2043 // Get the last component
2044 StringRef LastComponent = sys::path::filename(Trimmed, path_style);
2045
2046 std::unique_ptr<RedirectingFileSystem::Entry> Result;
2047 switch (Kind) {
2049 Result = std::make_unique<RedirectingFileSystem::FileEntry>(
2050 LastComponent, std::move(ExternalContentsPath), UseExternalName);
2051 break;
2053 Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
2054 LastComponent, std::move(ExternalContentsPath), UseExternalName);
2055 break;
2057 Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
2058 LastComponent, std::move(EntryArrayContents),
2059 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2060 0, 0, 0, file_type::directory_file, sys::fs::all_all));
2061 break;
2062 }
2063
2064 StringRef Parent = sys::path::parent_path(Trimmed, path_style);
2065 if (Parent.empty())
2066 return Result;
2067
2068 // if 'name' contains multiple components, create implicit directory entries
2069 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style),
2070 E = sys::path::rend(Parent);
2071 I != E; ++I) {
2072 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;
2073 Entries.push_back(std::move(Result));
2074 Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
2075 *I, std::move(Entries),
2076 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2077 0, 0, 0, file_type::directory_file, sys::fs::all_all));
2078 }
2079 return Result;
2080 }
2081
2082public:
2084
2085 // false on error
2087 auto *Top = dyn_cast<yaml::MappingNode>(Root);
2088 if (!Top) {
2089 error(Root, "expected mapping node");
2090 return false;
2091 }
2092
2093 KeyStatusPair Fields[] = {
2094 KeyStatusPair("version", true),
2095 KeyStatusPair("case-sensitive", false),
2096 KeyStatusPair("use-external-names", false),
2097 KeyStatusPair("root-relative", false),
2098 KeyStatusPair("overlay-relative", false),
2099 KeyStatusPair("fallthrough", false),
2100 KeyStatusPair("redirecting-with", false),
2101 KeyStatusPair("roots", true),
2102 };
2103
2104 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
2105 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries;
2106
2107 // Parse configuration and 'roots'
2108 for (auto &I : *Top) {
2109 SmallString<10> KeyBuffer;
2110 StringRef Key;
2111 if (!parseScalarString(I.getKey(), Key, KeyBuffer))
2112 return false;
2113
2114 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
2115 return false;
2116
2117 if (Key == "roots") {
2118 auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue());
2119 if (!Roots) {
2120 error(I.getValue(), "expected array");
2121 return false;
2122 }
2123
2124 for (auto &I : *Roots) {
2125 if (std::unique_ptr<RedirectingFileSystem::Entry> E =
2126 parseEntry(&I, FS, /*IsRootEntry*/ true))
2127 RootEntries.push_back(std::move(E));
2128 else
2129 return false;
2130 }
2131 } else if (Key == "version") {
2132 StringRef VersionString;
2133 SmallString<4> Storage;
2134 if (!parseScalarString(I.getValue(), VersionString, Storage))
2135 return false;
2136 int Version;
2137 if (VersionString.getAsInteger<int>(10, Version)) {
2138 error(I.getValue(), "expected integer");
2139 return false;
2140 }
2141 if (Version < 0) {
2142 error(I.getValue(), "invalid version number");
2143 return false;
2144 }
2145 if (Version != 0) {
2146 error(I.getValue(), "version mismatch, expected 0");
2147 return false;
2148 }
2149 } else if (Key == "case-sensitive") {
2150 if (!parseScalarBool(I.getValue(), FS->CaseSensitive))
2151 return false;
2152 } else if (Key == "overlay-relative") {
2153 if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay))
2154 return false;
2155 } else if (Key == "use-external-names") {
2156 if (!parseScalarBool(I.getValue(), FS->UseExternalNames))
2157 return false;
2158 } else if (Key == "fallthrough") {
2159 if (Keys["redirecting-with"].Seen) {
2160 error(I.getValue(),
2161 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2162 return false;
2163 }
2164
2165 bool ShouldFallthrough = false;
2166 if (!parseScalarBool(I.getValue(), ShouldFallthrough))
2167 return false;
2168
2169 if (ShouldFallthrough) {
2171 } else {
2173 }
2174 } else if (Key == "redirecting-with") {
2175 if (Keys["fallthrough"].Seen) {
2176 error(I.getValue(),
2177 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2178 return false;
2179 }
2180
2181 if (auto Kind = parseRedirectKind(I.getValue())) {
2182 FS->Redirection = *Kind;
2183 } else {
2184 error(I.getValue(), "expected valid redirect kind");
2185 return false;
2186 }
2187 } else if (Key == "root-relative") {
2188 if (auto Kind = parseRootRelativeKind(I.getValue())) {
2189 FS->RootRelative = *Kind;
2190 } else {
2191 error(I.getValue(), "expected valid root-relative kind");
2192 return false;
2193 }
2194 } else {
2195 llvm_unreachable("key missing from Keys");
2196 }
2197 }
2198
2199 if (Stream.failed())
2200 return false;
2201
2202 if (!checkMissingKeys(Top, Keys))
2203 return false;
2204
2205 // Now that we sucessefully parsed the YAML file, canonicalize the internal
2206 // representation to a proper directory tree so that we can search faster
2207 // inside the VFS.
2208 for (auto &E : RootEntries)
2209 uniqueOverlayTree(FS, E.get());
2210
2211 return true;
2212 }
2213};
2214
2215std::unique_ptr<RedirectingFileSystem>
2216RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
2218 StringRef YAMLFilePath, void *DiagContext,
2219 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2220 SourceMgr SM;
2221 yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
2222
2223 SM.setDiagHandler(DiagHandler, DiagContext);
2224 yaml::document_iterator DI = Stream.begin();
2225 yaml::Node *Root = DI->getRoot();
2226 if (DI == Stream.end() || !Root) {
2227 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
2228 return nullptr;
2229 }
2230
2232
2233 std::unique_ptr<RedirectingFileSystem> FS(
2234 new RedirectingFileSystem(ExternalFS));
2235
2236 if (!YAMLFilePath.empty()) {
2237 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
2238 // to each 'external-contents' path.
2239 //
2240 // Example:
2241 // -ivfsoverlay dummy.cache/vfs/vfs.yaml
2242 // yields:
2243 // FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs
2244 //
2245 SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
2246 std::error_code EC = FS->makeAbsolute(OverlayAbsDir);
2247 assert(!EC && "Overlay dir final path must be absolute");
2248 (void)EC;
2249 FS->setOverlayFileDir(OverlayAbsDir);
2250 }
2251
2252 if (!P.parse(Root, FS.get()))
2253 return nullptr;
2254
2255 return FS;
2256}
2257
2258std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
2259 ArrayRef<std::pair<std::string, std::string>> RemappedFiles,
2260 bool UseExternalNames, llvm::IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2261 std::unique_ptr<RedirectingFileSystem> FS(
2262 new RedirectingFileSystem(ExternalFS));
2263 FS->UseExternalNames = UseExternalNames;
2264
2266
2267 for (auto &Mapping : llvm::reverse(RemappedFiles)) {
2268 SmallString<128> From = StringRef(Mapping.first);
2269 SmallString<128> To = StringRef(Mapping.second);
2270 {
2271 auto EC = ExternalFS->makeAbsolute(From);
2272 (void)EC;
2273 assert(!EC && "Could not make absolute path");
2274 }
2275
2276 // Check if we've already mapped this file. The first one we see (in the
2277 // reverse iteration) wins.
2278 RedirectingFileSystem::Entry *&ToEntry = Entries[From];
2279 if (ToEntry)
2280 continue;
2281
2282 // Add parent directories.
2283 RedirectingFileSystem::Entry *Parent = nullptr;
2284 StringRef FromDirectory = llvm::sys::path::parent_path(From);
2285 for (auto I = llvm::sys::path::begin(FromDirectory),
2286 E = llvm::sys::path::end(FromDirectory);
2287 I != E; ++I) {
2289 Parent);
2290 }
2291 assert(Parent && "File without a directory?");
2292 {
2293 auto EC = ExternalFS->makeAbsolute(To);
2294 (void)EC;
2295 assert(!EC && "Could not make absolute path");
2296 }
2297
2298 // Add the file.
2299 auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
2300 llvm::sys::path::filename(From), To,
2301 UseExternalNames ? RedirectingFileSystem::NK_External
2303 ToEntry = NewFile.get();
2305 std::move(NewFile));
2306 }
2307
2308 return FS;
2309}
2310
2313 : E(E) {
2314 assert(E != nullptr);
2315 // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
2316 // path of the directory it maps to in the external file system plus any
2317 // remaining path components in the provided iterator.
2319 SmallString<256> Redirect(DRE->getExternalContentsPath());
2320 sys::path::append(Redirect, Start, End,
2321 getExistingStyle(DRE->getExternalContentsPath()));
2322 ExternalRedirect = std::string(Redirect);
2323 }
2324}
2325
2327 llvm::SmallVectorImpl<char> &Result) const {
2328 Result.clear();
2329 for (Entry *Parent : Parents)
2330 llvm::sys::path::append(Result, Parent->getName());
2331 llvm::sys::path::append(Result, E->getName());
2332}
2333
2334std::error_code RedirectingFileSystem::makeCanonicalForLookup(
2335 SmallVectorImpl<char> &Path) const {
2336 if (std::error_code EC = makeAbsolute(Path))
2337 return EC;
2338
2339 llvm::SmallString<256> CanonicalPath =
2340 canonicalize(StringRef(Path.data(), Path.size()));
2341 if (CanonicalPath.empty())
2343
2344 Path.assign(CanonicalPath.begin(), CanonicalPath.end());
2345 return {};
2346}
2347
2350 llvm::SmallString<128> CanonicalPath(Path);
2351 if (std::error_code EC = makeCanonicalForLookup(CanonicalPath))
2352 return EC;
2353
2354 // RedirectOnly means the VFS is always used.
2355 if (UsageTrackingActive && Redirection == RedirectKind::RedirectOnly)
2356 HasBeenUsed = true;
2357
2358 sys::path::const_iterator Start = sys::path::begin(CanonicalPath);
2359 sys::path::const_iterator End = sys::path::end(CanonicalPath);
2361 for (const auto &Root : Roots) {
2363 lookupPathImpl(Start, End, Root.get(), Entries);
2364 if (UsageTrackingActive && Result && isa<RemapEntry>(Result->E))
2365 HasBeenUsed = true;
2366 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) {
2367 Result->Parents = std::move(Entries);
2368 return Result;
2369 }
2370 }
2372}
2373
2375RedirectingFileSystem::lookupPathImpl(
2378 llvm::SmallVectorImpl<Entry *> &Entries) const {
2379 assert(!isTraversalComponent(*Start) &&
2380 !isTraversalComponent(From->getName()) &&
2381 "Paths should not contain traversal components");
2382
2383 StringRef FromName = From->getName();
2384
2385 // Forward the search to the next component in case this is an empty one.
2386 if (!FromName.empty()) {
2387 if (!pathComponentMatches(*Start, FromName))
2389
2390 ++Start;
2391
2392 if (Start == End) {
2393 // Match!
2394 return LookupResult(From, Start, End);
2395 }
2396 }
2397
2400
2402 return LookupResult(From, Start, End);
2403
2405 for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
2406 llvm::make_range(DE->contents_begin(), DE->contents_end())) {
2407 Entries.push_back(From);
2409 lookupPathImpl(Start, End, DirEntry.get(), Entries);
2410 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
2411 return Result;
2412 Entries.pop_back();
2413 }
2414
2416}
2417
2418static Status getRedirectedFileStatus(const Twine &OriginalPath,
2419 bool UseExternalNames,
2420 Status ExternalStatus) {
2421 // The path has been mapped by some nested VFS and exposes an external path,
2422 // don't override it with the original path.
2423 if (ExternalStatus.ExposesExternalVFSPath)
2424 return ExternalStatus;
2425
2426 Status S = ExternalStatus;
2427 if (!UseExternalNames)
2428 S = Status::copyWithNewName(S, OriginalPath);
2429 else
2430 S.ExposesExternalVFSPath = true;
2431 return S;
2432}
2433
2434ErrorOr<Status> RedirectingFileSystem::status(
2435 const Twine &LookupPath, const Twine &OriginalPath,
2436 const RedirectingFileSystem::LookupResult &Result) {
2437 if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
2438 SmallString<256> RemappedPath((*ExtRedirect).str());
2439 if (std::error_code EC = makeAbsolute(RemappedPath))
2440 return EC;
2441
2442 ErrorOr<Status> S = ExternalFS->status(RemappedPath);
2443 if (!S)
2444 return S;
2445 S = Status::copyWithNewName(*S, *ExtRedirect);
2447 return getRedirectedFileStatus(OriginalPath,
2448 RE->useExternalName(UseExternalNames), *S);
2449 }
2450
2452 return Status::copyWithNewName(DE->getStatus(), LookupPath);
2453}
2454
2455ErrorOr<Status>
2456RedirectingFileSystem::getExternalStatus(const Twine &LookupPath,
2457 const Twine &OriginalPath) const {
2458 auto Result = ExternalFS->status(LookupPath);
2459
2460 // The path has been mapped by some nested VFS, don't override it with the
2461 // original path.
2462 if (!Result || Result->ExposesExternalVFSPath)
2463 return Result;
2464 return Status::copyWithNewName(Result.get(), OriginalPath);
2465}
2466
2467ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {
2468 SmallString<256> Path;
2469 OriginalPath.toVector(Path);
2470
2471 if (std::error_code EC = makeAbsolute(Path))
2472 return EC;
2473
2474 if (Redirection == RedirectKind::Fallback) {
2475 // Attempt to find the original file first, only falling back to the
2476 // mapped file if that fails.
2477 ErrorOr<Status> S = getExternalStatus(Path, OriginalPath);
2478 if (S)
2479 return S;
2480 }
2481
2483 if (!Result) {
2484 // Was not able to map file, fallthrough to using the original path if
2485 // that was the specified redirection type.
2486 if (Redirection == RedirectKind::Fallthrough &&
2487 isFileNotFound(Result.getError()))
2488 return getExternalStatus(Path, OriginalPath);
2489 return Result.getError();
2490 }
2491
2492 ErrorOr<Status> S = status(Path, OriginalPath, *Result);
2493 if (!S && Redirection == RedirectKind::Fallthrough &&
2494 isFileNotFound(S.getError(), Result->E)) {
2495 // Mapped the file but it wasn't found in the underlying filesystem,
2496 // fallthrough to using the original path if that was the specified
2497 // redirection type.
2498 return getExternalStatus(Path, OriginalPath);
2499 }
2500
2501 return S;
2502}
2503
2504bool RedirectingFileSystem::exists(const Twine &OriginalPath) {
2505 SmallString<256> Path;
2506 OriginalPath.toVector(Path);
2507
2508 if (makeAbsolute(Path))
2509 return false;
2510
2511 if (Redirection == RedirectKind::Fallback) {
2512 // Attempt to find the original file first, only falling back to the
2513 // mapped file if that fails.
2514 if (ExternalFS->exists(Path))
2515 return true;
2516 }
2517
2519 if (!Result) {
2520 // Was not able to map file, fallthrough to using the original path if
2521 // that was the specified redirection type.
2522 if (Redirection == RedirectKind::Fallthrough &&
2523 isFileNotFound(Result.getError()))
2524 return ExternalFS->exists(Path);
2525 return false;
2526 }
2527
2528 std::optional<StringRef> ExtRedirect = Result->getExternalRedirect();
2529 if (!ExtRedirect) {
2531 return true;
2532 }
2533
2534 SmallString<256> RemappedPath((*ExtRedirect).str());
2535 if (makeAbsolute(RemappedPath))
2536 return false;
2537
2538 if (ExternalFS->exists(RemappedPath))
2539 return true;
2540
2541 if (Redirection == RedirectKind::Fallthrough) {
2542 // Mapped the file but it wasn't found in the underlying filesystem,
2543 // fallthrough to using the original path if that was the specified
2544 // redirection type.
2545 return ExternalFS->exists(Path);
2546 }
2547
2548 return false;
2549}
2550
2551namespace {
2552
2553/// Provide a file wrapper with an overriden status.
2554class FileWithFixedStatus : public File {
2555 std::unique_ptr<File> InnerFile;
2556 Status S;
2557
2558public:
2559 FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
2560 : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
2561
2562 ErrorOr<Status> status() override { return S; }
2564
2565 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
2566 bool IsVolatile) override {
2567 return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
2568 IsVolatile);
2569 }
2570
2571 std::error_code close() override { return InnerFile->close(); }
2572
2573 void setPath(const Twine &Path) override { S = S.copyWithNewName(S, Path); }
2574};
2575
2576} // namespace
2577
2578ErrorOr<std::unique_ptr<File>>
2579File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
2580 // See \c getRedirectedFileStatus - don't update path if it's exposing an
2581 // external path.
2582 if (!Result || (*Result)->status()->ExposesExternalVFSPath)
2583 return Result;
2584
2585 ErrorOr<std::unique_ptr<File>> F = std::move(*Result);
2586 auto Name = F->get()->getName();
2587 if (Name && Name.get() != P.str())
2588 F->get()->setPath(P);
2589 return F;
2590}
2591
2594 SmallString<256> Path;
2595 OriginalPath.toVector(Path);
2596
2597 if (std::error_code EC = makeAbsolute(Path))
2598 return EC;
2599
2600 if (Redirection == RedirectKind::Fallback) {
2601 // Attempt to find the original file first, only falling back to the
2602 // mapped file if that fails.
2603 auto F = File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2604 if (F)
2605 return F;
2606 }
2607
2609 if (!Result) {
2610 // Was not able to map file, fallthrough to using the original path if
2611 // that was the specified redirection type.
2612 if (Redirection == RedirectKind::Fallthrough &&
2613 isFileNotFound(Result.getError()))
2614 return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2615 return Result.getError();
2616 }
2617
2618 if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
2620
2621 StringRef ExtRedirect = *Result->getExternalRedirect();
2622 SmallString<256> RemappedPath(ExtRedirect.str());
2623 if (std::error_code EC = makeAbsolute(RemappedPath))
2624 return EC;
2625
2626 auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
2627
2628 auto ExternalFile =
2629 File::getWithPath(ExternalFS->openFileForRead(RemappedPath), ExtRedirect);
2630 if (!ExternalFile) {
2631 if (Redirection == RedirectKind::Fallthrough &&
2632 isFileNotFound(ExternalFile.getError(), Result->E)) {
2633 // Mapped the file but it wasn't found in the underlying filesystem,
2634 // fallthrough to using the original path if that was the specified
2635 // redirection type.
2636 return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2637 }
2638 return ExternalFile;
2639 }
2640
2641 auto ExternalStatus = (*ExternalFile)->status();
2642 if (!ExternalStatus)
2643 return ExternalStatus.getError();
2644
2645 // Otherwise, the file was successfully remapped. Mark it as such. Also
2646 // replace the underlying path if the external name is being used.
2648 OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus);
2649 return std::unique_ptr<File>(
2650 std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
2651}
2652
2653std::error_code
2655 SmallVectorImpl<char> &Output) {
2656 SmallString<256> Path;
2657 OriginalPath.toVector(Path);
2658
2659 if (std::error_code EC = makeAbsolute(Path))
2660 return EC;
2661
2662 if (Redirection == RedirectKind::Fallback) {
2663 // Attempt to find the original file first, only falling back to the
2664 // mapped file if that fails.
2665 std::error_code EC = ExternalFS->getRealPath(Path, Output);
2666 if (!EC)
2667 return EC;
2668 }
2669
2671 if (!Result) {
2672 // Was not able to map file, fallthrough to using the original path if
2673 // that was the specified redirection type.
2674 if (Redirection == RedirectKind::Fallthrough &&
2675 isFileNotFound(Result.getError()))
2676 return ExternalFS->getRealPath(Path, Output);
2677 return Result.getError();
2678 }
2679
2680 // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2681 // path in the external file system.
2682 if (auto ExtRedirect = Result->getExternalRedirect()) {
2683 auto P = ExternalFS->getRealPath(*ExtRedirect, Output);
2684 if (P && Redirection == RedirectKind::Fallthrough &&
2685 isFileNotFound(P, Result->E)) {
2686 // Mapped the file but it wasn't found in the underlying filesystem,
2687 // fallthrough to using the original path if that was the specified
2688 // redirection type.
2689 return ExternalFS->getRealPath(Path, Output);
2690 }
2691 return P;
2692 }
2693
2694 // We found a DirectoryEntry, which does not have a single external contents
2695 // path. Use the canonical virtual path.
2696 if (Redirection == RedirectKind::Fallthrough) {
2697 Result->getPath(Output);
2698 return {};
2699 }
2701}
2702
2703std::unique_ptr<FileSystem>
2704vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
2706 StringRef YAMLFilePath, void *DiagContext,
2707 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2708 return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
2709 YAMLFilePath, DiagContext,
2710 std::move(ExternalFS));
2711}
2712
2716 auto Kind = SrcE->getKind();
2719 assert(DE && "Must be a directory");
2720 for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
2721 llvm::make_range(DE->contents_begin(), DE->contents_end())) {
2722 Path.push_back(SubEntry->getName());
2723 getVFSEntries(SubEntry.get(), Path, Entries);
2724 Path.pop_back();
2725 }
2726 return;
2727 }
2728
2731 assert(DR && "Must be a directory remap");
2732 SmallString<128> VPath;
2733 for (auto &Comp : Path)
2734 llvm::sys::path::append(VPath, Comp);
2735 Entries.push_back(
2736 YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
2737 return;
2738 }
2739
2740 assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
2742 assert(FE && "Must be a file");
2743 SmallString<128> VPath;
2744 for (auto &Comp : Path)
2745 llvm::sys::path::append(VPath, Comp);
2746 Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
2747}
2748
2750 SmallVectorImpl<YAMLVFSEntry> &CollectedEntries) {
2752 if (!RootResult)
2753 return;
2754 SmallVector<StringRef, 8> Components;
2755 Components.push_back("/");
2756 getVFSEntries(RootResult->E, Components, CollectedEntries);
2757}
2758
2760 static std::atomic<unsigned> UID;
2761 unsigned ID = ++UID;
2762 // The following assumes that uint64_t max will never collide with a real
2763 // dev_t value from the OS.
2764 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
2765}
2766
2767void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath,
2768 bool IsDirectory) {
2769 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
2770 assert(sys::path::is_absolute(RealPath) && "real path not absolute");
2771 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
2772 Mappings.emplace_back(VirtualPath, RealPath, IsDirectory);
2773}
2774
2776 addEntry(VirtualPath, RealPath, /*IsDirectory=*/false);
2777}
2778
2780 StringRef RealPath) {
2781 addEntry(VirtualPath, RealPath, /*IsDirectory=*/true);
2782}
2783
2784namespace {
2785
2786class JSONWriter {
2789
2790 unsigned getDirIndent() { return 4 * DirStack.size(); }
2791 unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
2792 bool containedIn(StringRef Parent, StringRef Path);
2793 StringRef containedPart(StringRef Parent, StringRef Path);
2794 void startDirectory(StringRef Path);
2795 void endDirectory();
2796 void writeEntry(StringRef VPath, StringRef RPath);
2797
2798public:
2799 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
2800
2801 void write(ArrayRef<YAMLVFSEntry> Entries,
2802 std::optional<bool> UseExternalNames,
2803 std::optional<bool> IsCaseSensitive,
2804 std::optional<bool> IsOverlayRelative, StringRef OverlayDir);
2805};
2806
2807} // namespace
2808
2809bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
2810 using namespace llvm::sys;
2811
2812 // Compare each path component.
2813 auto IParent = path::begin(Parent), EParent = path::end(Parent);
2814 for (auto IChild = path::begin(Path), EChild = path::end(Path);
2815 IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
2816 if (*IParent != *IChild)
2817 return false;
2818 }
2819 // Have we exhausted the parent path?
2820 return IParent == EParent;
2821}
2822
2823StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
2824 assert(!Parent.empty());
2825 assert(containedIn(Parent, Path));
2826 return Path.substr(Parent.size() + 1);
2827}
2828
2829void JSONWriter::startDirectory(StringRef Path) {
2830 StringRef Name =
2831 DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
2832 DirStack.push_back(Path);
2833 unsigned Indent = getDirIndent();
2834 OS.indent(Indent) << "{\n";
2835 OS.indent(Indent + 2) << "'type': 'directory',\n";
2836 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
2837 OS.indent(Indent + 2) << "'contents': [\n";
2838}
2839
2840void JSONWriter::endDirectory() {
2841 unsigned Indent = getDirIndent();
2842 OS.indent(Indent + 2) << "]\n";
2843 OS.indent(Indent) << "}";
2844
2845 DirStack.pop_back();
2846}
2847
2848void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
2849 unsigned Indent = getFileIndent();
2850 OS.indent(Indent) << "{\n";
2851 OS.indent(Indent + 2) << "'type': 'file',\n";
2852 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
2853 OS.indent(Indent + 2) << "'external-contents': \""
2854 << llvm::yaml::escape(RPath) << "\"\n";
2855 OS.indent(Indent) << "}";
2856}
2857
2858void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
2859 std::optional<bool> UseExternalNames,
2860 std::optional<bool> IsCaseSensitive,
2861 std::optional<bool> IsOverlayRelative,
2862 StringRef OverlayDir) {
2863 using namespace llvm::sys;
2864
2865 OS << "{\n"
2866 " 'version': 0,\n";
2867 if (IsCaseSensitive)
2868 OS << " 'case-sensitive': '" << (*IsCaseSensitive ? "true" : "false")
2869 << "',\n";
2870 if (UseExternalNames)
2871 OS << " 'use-external-names': '" << (*UseExternalNames ? "true" : "false")
2872 << "',\n";
2873 bool UseOverlayRelative = false;
2874 if (IsOverlayRelative) {
2875 UseOverlayRelative = *IsOverlayRelative;
2876 OS << " 'overlay-relative': '" << (UseOverlayRelative ? "true" : "false")
2877 << "',\n";
2878 }
2879 OS << " 'roots': [\n";
2880
2881 if (!Entries.empty()) {
2882 const YAMLVFSEntry &Entry = Entries.front();
2883
2884 startDirectory(
2885 Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath)
2886 );
2887
2888 StringRef RPath = Entry.RPath;
2889 if (UseOverlayRelative) {
2890 assert(RPath.starts_with(OverlayDir) &&
2891 "Overlay dir must be contained in RPath");
2892 RPath = RPath.substr(OverlayDir.size());
2893 }
2894
2895 bool IsCurrentDirEmpty = true;
2896 if (!Entry.IsDirectory) {
2897 writeEntry(path::filename(Entry.VPath), RPath);
2898 IsCurrentDirEmpty = false;
2899 }
2900
2901 for (const auto &Entry : Entries.slice(1)) {
2902 StringRef Dir =
2903 Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath);
2904 if (Dir == DirStack.back()) {
2905 if (!IsCurrentDirEmpty) {
2906 OS << ",\n";
2907 }
2908 } else {
2909 bool IsDirPoppedFromStack = false;
2910 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
2911 OS << "\n";
2912 endDirectory();
2913 IsDirPoppedFromStack = true;
2914 }
2915 if (IsDirPoppedFromStack || !IsCurrentDirEmpty) {
2916 OS << ",\n";
2917 }
2918 startDirectory(Dir);
2919 IsCurrentDirEmpty = true;
2920 }
2921 StringRef RPath = Entry.RPath;
2922 if (UseOverlayRelative) {
2923 assert(RPath.starts_with(OverlayDir) &&
2924 "Overlay dir must be contained in RPath");
2925 RPath = RPath.substr(OverlayDir.size());
2926 }
2927 if (!Entry.IsDirectory) {
2928 writeEntry(path::filename(Entry.VPath), RPath);
2929 IsCurrentDirEmpty = false;
2930 }
2931 }
2932
2933 while (!DirStack.empty()) {
2934 OS << "\n";
2935 endDirectory();
2936 }
2937 OS << "\n";
2938 }
2939
2940 OS << " ]\n"
2941 << "}\n";
2942}
2943
2945 llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
2946 return LHS.VPath < RHS.VPath;
2947 });
2948
2949 JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
2950 IsOverlayRelative, OverlayDir);
2951}
2952
2954 FileSystem &FS_, const Twine &Path, std::error_code &EC)
2955 : FS(&FS_) {
2956 directory_iterator I = FS->dir_begin(Path, EC);
2957 if (I != directory_iterator()) {
2958 State = std::make_shared<detail::RecDirIterState>();
2959 State->Stack.push_back(I);
2960 }
2961}
2962
2965 assert(FS && State && !State->Stack.empty() && "incrementing past end");
2966 assert(!State->Stack.back()->path().empty() && "non-canonical end iterator");
2968
2969 if (State->HasNoPushRequest)
2970 State->HasNoPushRequest = false;
2971 else {
2972 if (State->Stack.back()->type() == sys::fs::file_type::directory_file) {
2974 FS->dir_begin(State->Stack.back()->path(), EC);
2975 if (I != End) {
2976 State->Stack.push_back(I);
2977 return *this;
2978 }
2979 }
2980 }
2981
2982 while (!State->Stack.empty() && State->Stack.back().increment(EC) == End)
2983 State->Stack.pop_back();
2984
2985 if (State->Stack.empty())
2986 State.reset(); // end iterator
2987
2988 return *this;
2989}
2990
2992 unsigned IndentLevel) const {
2993 printIndent(OS, IndentLevel);
2994 OS << "TracingFileSystem\n";
2995 if (Type == PrintType::Summary)
2996 return;
2997
2998 printIndent(OS, IndentLevel);
2999 OS << "NumStatusCalls=" << NumStatusCalls << "\n";
3000 printIndent(OS, IndentLevel);
3001 OS << "NumOpenFileForReadCalls=" << NumOpenFileForReadCalls << "\n";
3002 printIndent(OS, IndentLevel);
3003 OS << "NumDirBeginCalls=" << NumDirBeginCalls << "\n";
3004 printIndent(OS, IndentLevel);
3005 OS << "NumGetRealPathCalls=" << NumGetRealPathCalls << "\n";
3006 printIndent(OS, IndentLevel);
3007 OS << "NumExistsCalls=" << NumExistsCalls << "\n";
3008 printIndent(OS, IndentLevel);
3009 OS << "NumIsLocalCalls=" << NumIsLocalCalls << "\n";
3010
3011 if (Type == PrintType::Contents)
3012 Type = PrintType::Summary;
3013 getUnderlyingFS().print(OS, Type, IndentLevel + 1);
3014}
3015
3016const char FileSystem::ID = 0;
3017const char OverlayFileSystem::ID = 0;
3018const char ProxyFileSystem::ID = 0;
3019const char InMemoryFileSystem::ID = 0;
3020const char RedirectingFileSystem::ID = 0;
3021const char TracingFileSystem::ID = 0;
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
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)
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.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
const T & front() const
front - Get the first element.
Definition ArrayRef.h:145
bool empty() const
empty - Check if the array is empty.
Definition ArrayRef.h:137
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:186
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:178
iterator end()
Definition DenseMap.h:81
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:128
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:133
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition StringRef.h:472
std::string str() const
str - Get the contents as an std::string.
Definition StringRef.h:225
constexpr bool empty() const
empty - Check if the string is empty.
Definition StringRef.h:143
char back() const
back - Get the last character in the string.
Definition StringRef.h:155
StringRef slice(size_t Start, size_t End) const
Return a reference to the substring from [Start, End).
Definition StringRef.h:686
constexpr size_t size() const
size - Get the string size.
Definition StringRef.h:146
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:45
LLVM_ABI void print(raw_ostream &O, bool IsForDebug=false, bool NoDetails=false) const
Print the current type.
LLVM Value Representation.
Definition Value.h:75
An opaque object representing a hash code.
Definition Hashing.h:76
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
void printImpl(raw_ostream &OS, PrintType Type, unsigned IndentLevel) const override
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.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ Resolved
Queried, materialization begun.
Definition Core.h:780
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:755
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:1101
LLVM_ABI StringRef get_separator(Style style=Style::native)
Return the preferred separator for this platform.
Definition Path.cpp:610
LLVM_ABI StringRef root_path(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get root path.
Definition Path.cpp:349
LLVM_ABI const_iterator begin(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get begin iterator over path.
Definition Path.cpp:227
LLVM_ABI bool remove_dots(SmallVectorImpl< char > &path, bool remove_dot_dot=false, Style style=Style::native)
In-place remove any '.
Definition Path.cpp:765
LLVM_ABI StringRef parent_path(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get parent path.
Definition Path.cpp:468
LLVM_ABI void make_absolute(const Twine &current_directory, SmallVectorImpl< char > &path)
Make path an absolute path.
Definition Path.cpp:704
LLVM_ABI StringRef filename(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get filename.
Definition Path.cpp:578
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:672
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:457
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:236
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:602
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.
FunctionAddr VTableAddr Value
Definition InstrProf.h:137
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
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.
@ 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
FunctionAddr VTableAddr uintptr_t uintptr_t Version
Definition InstrProf.h:302
auto reverse(ContainerTy &&C)
Definition STLExtras.h:406
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1634
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:207
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:1879
LLVM_ABI Error write(MCStreamer &Out, ArrayRef< std::string > Inputs, OnCuIndexOverflow OverflowOptValue, Dwarf64StrOffsetsPromotion StrOffsetsOptValue)
Definition DWP.cpp:677
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:592
LLVM_ABI std::error_code errorToErrorCode(Error Err)
Helper for converting an ECError to a std::error_code.
Definition Error.cpp:117
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:870
#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