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