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