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 void
298 getDirectoryContentRealSources(const Twine &Dir,
299 SmallVectorImpl<std::string> &Out) override {
300 Out.push_back(Dir.str());
301 }
302 std::error_code getRealPath(const Twine &Path,
303 SmallVectorImpl<char> &Output) override;
304
305protected:
306 void printImpl(raw_ostream &OS, PrintType Type,
307 unsigned IndentLevel) const override;
308
309private:
310 // If this FS has its own working dir, use it to make Path absolute.
311 // The returned twine is safe to use as long as both Storage and Path live.
312 Twine adjustPath(const Twine &Path, SmallVectorImpl<char> &Storage) const {
313 if (!WD || !*WD)
314 return Path;
315 Path.toVector(Storage);
316 sys::path::make_absolute(WD->get().Resolved, Storage);
317 return Storage;
318 }
319
320 ErrorOr<std::unique_ptr<File>>
321 openFileForReadWithFlags(const Twine &Name, sys::fs::OpenFlags Flags) {
322 SmallString<256> RealName, Storage;
323 Expected<file_t> FDOrErr = sys::fs::openNativeFileForRead(
324 adjustPath(Name, Storage), Flags, &RealName);
325 if (!FDOrErr)
326 return errorToErrorCode(FDOrErr.takeError());
327 return std::unique_ptr<File>(
328 new RealFile(*FDOrErr, Name.str(), RealName.str()));
329 }
330
331 struct WorkingDirectory {
332 // The current working directory, without symlinks resolved. (echo $PWD).
333 SmallString<128> Specified;
334 // The current working directory, with links resolved. (readlink .).
335 SmallString<128> Resolved;
336 };
337 std::optional<llvm::ErrorOr<WorkingDirectory>> WD;
338};
339
340} // namespace
341
342ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
343 auto BypassSandbox = sys::sandbox::scopedDisable();
344
345 SmallString<256> Storage;
346 sys::fs::file_status RealStatus;
347 if (std::error_code EC =
348 sys::fs::status(adjustPath(Path, Storage), RealStatus))
349 return EC;
350 return Status::copyWithNewName(RealStatus, Path);
351}
352
353ErrorOr<std::unique_ptr<File>>
354RealFileSystem::openFileForRead(const Twine &Name) {
355 auto BypassSandbox = sys::sandbox::scopedDisable();
356
357 return openFileForReadWithFlags(Name, sys::fs::OF_Text);
358}
359
360ErrorOr<std::unique_ptr<File>>
361RealFileSystem::openFileForReadBinary(const Twine &Name) {
362 auto BypassSandbox = sys::sandbox::scopedDisable();
363
364 return openFileForReadWithFlags(Name, sys::fs::OF_None);
365}
366
367llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
368 auto BypassSandbox = sys::sandbox::scopedDisable();
369
370 if (WD && *WD)
371 return std::string(WD->get().Specified);
372 if (WD)
373 return WD->getError();
374
375 SmallString<128> Dir;
376 if (std::error_code EC = llvm::sys::fs::current_path(Dir))
377 return EC;
378 return std::string(Dir);
379}
380
381std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
382 auto BypassSandbox = sys::sandbox::scopedDisable();
383
384 if (!WD)
386
387 SmallString<128> Absolute, Resolved, Storage;
388 adjustPath(Path, Storage).toVector(Absolute);
389 bool IsDir;
390 if (auto Err = llvm::sys::fs::is_directory(Absolute, IsDir))
391 return Err;
392 if (!IsDir)
393 return std::make_error_code(std::errc::not_a_directory);
394 if (auto Err = llvm::sys::fs::real_path(Absolute, Resolved))
395 return Err;
396 WD = WorkingDirectory{Absolute, Resolved};
397 return std::error_code();
398}
399
400std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) {
401 auto BypassSandbox = sys::sandbox::scopedDisable();
402
403 SmallString<256> Storage;
404 return llvm::sys::fs::is_local(adjustPath(Path, Storage), Result);
405}
406
407std::error_code RealFileSystem::getRealPath(const Twine &Path,
408 SmallVectorImpl<char> &Output) {
409 auto BypassSandbox = sys::sandbox::scopedDisable();
410
411 SmallString<256> Storage;
412 return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output);
413}
414
415void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type,
416 unsigned IndentLevel) const {
417 printIndent(OS, IndentLevel);
418 OS << "RealFileSystem using ";
419 if (WD)
420 OS << "own";
421 else
422 OS << "process";
423 OS << " CWD\n";
424}
425
433
434std::unique_ptr<FileSystem> vfs::createPhysicalFileSystem() {
436
437 return std::make_unique<RealFileSystem>(false);
438}
439
440namespace {
441
442class RealFSDirIter : public llvm::vfs::detail::DirIterImpl {
444
445public:
446 RealFSDirIter(const Twine &Path, std::error_code &EC) {
447 auto BypassSandbox = sys::sandbox::scopedDisable();
448
449 Iter = sys::fs::directory_iterator(Path, EC);
450 if (Iter != sys::fs::directory_iterator())
451 CurrentEntry = directory_entry(Iter->path(), Iter->type());
452 }
453
454 std::error_code increment() override {
455 auto BypassSandbox = sys::sandbox::scopedDisable();
456
457 std::error_code EC;
458 Iter.increment(EC);
459 CurrentEntry = (Iter == llvm::sys::fs::directory_iterator())
461 : directory_entry(Iter->path(), Iter->type());
462 return EC;
463 }
464};
465
466} // namespace
467
468directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
469 std::error_code &EC) {
470 auto BypassSandbox = sys::sandbox::scopedDisable();
471
472 SmallString<128> Storage;
473 return directory_iterator(
474 std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC));
475}
476
477//===-----------------------------------------------------------------------===/
478// OverlayFileSystem implementation
479//===-----------------------------------------------------------------------===/
480
482 FSList.push_back(std::move(BaseFS));
483}
484
486 FSList.push_back(FS);
487 // Synchronize added file systems by duplicating the working directory from
488 // the first one in the list.
489 FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
490}
491
493 // FIXME: handle symlinks that cross file systems
494 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
495 ErrorOr<Status> Status = (*I)->status(Path);
497 return Status;
498 }
500}
501
503 // FIXME: handle symlinks that cross file systems
504 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
505 if ((*I)->exists(Path))
506 return true;
507 }
508 return false;
509}
510
513 // FIXME: handle symlinks that cross file systems
514 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
515 auto Result = (*I)->openFileForRead(Path);
516 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
517 return Result;
518 }
520}
521
524 // All file systems are synchronized, just take the first working directory.
525 return FSList.front()->getCurrentWorkingDirectory();
526}
527
528std::error_code
530 for (auto &FS : FSList)
531 if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
532 return EC;
533 return {};
534}
535
536std::error_code OverlayFileSystem::isLocal(const Twine &Path, bool &Result) {
537 for (auto &FS : FSList)
538 if (FS->exists(Path))
539 return FS->isLocal(Path, Result);
541}
542
543std::error_code OverlayFileSystem::getRealPath(const Twine &Path,
544 SmallVectorImpl<char> &Output) {
545 for (const auto &FS : FSList)
546 if (FS->exists(Path))
547 return FS->getRealPath(Path, Output);
549}
550
552 const Twine &Dir, SmallVectorImpl<std::string> &Out) {
553 // All layers contribute.
554 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I)
555 (*I)->getDirectoryContentRealSources(Dir, Out);
556}
557
558void OverlayFileSystem::visitChildFileSystems(VisitCallbackTy Callback) {
560 Callback(*FS);
561 FS->visitChildFileSystems(Callback);
562 }
563}
564
566 unsigned IndentLevel) const {
567 printIndent(OS, IndentLevel);
568 OS << "OverlayFileSystem\n";
569 if (Type == PrintType::Summary)
570 return;
571
572 if (Type == PrintType::Contents)
573 Type = PrintType::Summary;
574 for (const auto &FS : overlays_range())
575 FS->print(OS, Type, IndentLevel + 1);
576}
577
579
580namespace {
581
582/// Combines and deduplicates directory entries across multiple file systems.
583class CombiningDirIterImpl : public llvm::vfs::detail::DirIterImpl {
585
586 /// Iterators to combine, processed in reverse order.
588 /// The iterator currently being traversed.
589 directory_iterator CurrentDirIter;
590 /// The set of names already returned as entries.
591 llvm::StringSet<> SeenNames;
592
593 /// Sets \c CurrentDirIter to the next iterator in the list, or leaves it as
594 /// is (at its end position) if we've already gone through them all.
595 std::error_code incrementIter(bool IsFirstTime) {
596 while (!IterList.empty()) {
597 CurrentDirIter = IterList.back();
598 IterList.pop_back();
599 if (CurrentDirIter != directory_iterator())
600 break; // found
601 }
602
603 if (IsFirstTime && CurrentDirIter == directory_iterator())
605 return {};
606 }
607
608 std::error_code incrementDirIter(bool IsFirstTime) {
609 assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
610 "incrementing past end");
611 std::error_code EC;
612 if (!IsFirstTime)
613 CurrentDirIter.increment(EC);
614 if (!EC && CurrentDirIter == directory_iterator())
615 EC = incrementIter(IsFirstTime);
616 return EC;
617 }
618
619 std::error_code incrementImpl(bool IsFirstTime) {
620 while (true) {
621 std::error_code EC = incrementDirIter(IsFirstTime);
622 if (EC || CurrentDirIter == directory_iterator()) {
623 CurrentEntry = directory_entry();
624 return EC;
625 }
626 CurrentEntry = *CurrentDirIter;
627 StringRef Name = llvm::sys::path::filename(CurrentEntry.path());
628 if (SeenNames.insert(Name).second)
629 return EC; // name not seen before
630 }
631 llvm_unreachable("returned above");
632 }
633
634public:
635 CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems, std::string Dir,
636 std::error_code &EC) {
637 for (const auto &FS : FileSystems) {
638 std::error_code FEC;
639 directory_iterator Iter = FS->dir_begin(Dir, FEC);
640 if (FEC && FEC != errc::no_such_file_or_directory) {
641 EC = FEC;
642 return;
643 }
644 if (!FEC)
645 IterList.push_back(Iter);
646 }
647 EC = incrementImpl(true);
648 }
649
650 CombiningDirIterImpl(ArrayRef<directory_iterator> DirIters,
651 std::error_code &EC)
652 : IterList(DirIters) {
653 EC = incrementImpl(true);
654 }
655
656 std::error_code increment() override { return incrementImpl(false); }
657};
658
659} // namespace
660
662 std::error_code &EC) {
664 std::make_shared<CombiningDirIterImpl>(FSList, Dir.str(), EC));
665 if (EC)
666 return {};
667 return Combined;
668}
669
670void ProxyFileSystem::anchor() {}
671
672namespace llvm {
673namespace vfs {
674
675namespace detail {
676
683
684/// The in memory file system is a tree of Nodes. Every node can either be a
685/// file, symlink, hardlink or a directory.
687 InMemoryNodeKind Kind;
688 std::string FileName;
689
690public:
692 : Kind(Kind), FileName(std::string(llvm::sys::path::filename(FileName))) {
693 }
694 virtual ~InMemoryNode() = default;
695
696 /// Return the \p Status for this node. \p RequestedName should be the name
697 /// through which the caller referred to this node. It will override
698 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
699 virtual Status getStatus(const Twine &RequestedName) const = 0;
700
701 /// Get the filename of this node (the name without the directory part).
702 StringRef getFileName() const { return FileName; }
703 InMemoryNodeKind getKind() const { return Kind; }
704 virtual std::string toString(unsigned Indent) const = 0;
705};
706
708 Status Stat;
709 std::unique_ptr<llvm::MemoryBuffer> Buffer;
710
711public:
712 InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
713 : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)),
714 Buffer(std::move(Buffer)) {}
715
716 Status getStatus(const Twine &RequestedName) const override {
717 return Status::copyWithNewName(Stat, RequestedName);
718 }
719 llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); }
720
721 std::string toString(unsigned Indent) const override {
722 return (std::string(Indent, ' ') + Stat.getName() + "\n").str();
723 }
724
725 static bool classof(const InMemoryNode *N) {
726 return N->getKind() == IME_File;
727 }
728};
729
730namespace {
731
732class InMemoryHardLink : public InMemoryNode {
733 const InMemoryFile &ResolvedFile;
734
735public:
736 InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile)
737 : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {}
738 const InMemoryFile &getResolvedFile() const { return ResolvedFile; }
739
740 Status getStatus(const Twine &RequestedName) const override {
741 return ResolvedFile.getStatus(RequestedName);
742 }
743
744 std::string toString(unsigned Indent) const override {
745 return std::string(Indent, ' ') + "HardLink to -> " +
746 ResolvedFile.toString(0);
747 }
748
749 static bool classof(const InMemoryNode *N) {
750 return N->getKind() == IME_HardLink;
751 }
752};
753
754class InMemorySymbolicLink : public InMemoryNode {
755 std::string TargetPath;
756 Status Stat;
757
758public:
759 InMemorySymbolicLink(StringRef Path, StringRef TargetPath, Status Stat)
760 : InMemoryNode(Path, IME_SymbolicLink), TargetPath(std::move(TargetPath)),
761 Stat(Stat) {}
762
763 std::string toString(unsigned Indent) const override {
764 return std::string(Indent, ' ') + "SymbolicLink to -> " + TargetPath;
765 }
766
767 Status getStatus(const Twine &RequestedName) const override {
768 return Status::copyWithNewName(Stat, RequestedName);
769 }
770
771 StringRef getTargetPath() const { return TargetPath; }
772
773 static bool classof(const InMemoryNode *N) {
774 return N->getKind() == IME_SymbolicLink;
775 }
776};
777
778/// Adapt a InMemoryFile for VFS' File interface. The goal is to make
779/// \p InMemoryFileAdaptor mimic as much as possible the behavior of
780/// \p RealFile.
781class InMemoryFileAdaptor : public File {
782 const InMemoryFile &Node;
783 /// The name to use when returning a Status for this file.
784 std::string RequestedName;
785
786public:
787 explicit InMemoryFileAdaptor(const InMemoryFile &Node,
788 std::string RequestedName)
789 : Node(Node), RequestedName(std::move(RequestedName)) {}
790
791 llvm::ErrorOr<Status> status() override {
792 return Node.getStatus(RequestedName);
793 }
794
795 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
796 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
797 bool IsVolatile) override {
798 llvm::MemoryBuffer *Buf = Node.getBuffer();
800 Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
801 }
802
803 std::error_code close() override { return {}; }
804
805 void setPath(const Twine &Path) override { RequestedName = Path.str(); }
806};
807} // namespace
808
810 Status Stat;
811 std::map<std::string, std::unique_ptr<InMemoryNode>, std::less<>> Entries;
812
813public:
815 : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {}
816
817 /// Return the \p Status for this node. \p RequestedName should be the name
818 /// through which the caller referred to this node. It will override
819 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
820 Status getStatus(const Twine &RequestedName) const override {
821 return Status::copyWithNewName(Stat, RequestedName);
822 }
823
824 UniqueID getUniqueID() const { return Stat.getUniqueID(); }
825
827 auto I = Entries.find(Name);
828 if (I != Entries.end())
829 return I->second.get();
830 return nullptr;
831 }
832
833 InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
834 return Entries.emplace(Name, std::move(Child)).first->second.get();
835 }
836
837 using const_iterator = decltype(Entries)::const_iterator;
838
839 const_iterator begin() const { return Entries.begin(); }
840 const_iterator end() const { return Entries.end(); }
841
842 std::string toString(unsigned Indent) const override {
843 std::string Result =
844 (std::string(Indent, ' ') + Stat.getName() + "\n").str();
845 for (const auto &Entry : Entries)
846 Result += Entry.second->toString(Indent + 2);
847 return Result;
848 }
849
850 static bool classof(const InMemoryNode *N) {
851 return N->getKind() == IME_Directory;
852 }
853};
854
855} // namespace detail
856
857// The UniqueID of in-memory files is derived from path and content.
858// This avoids difficulties in creating exactly equivalent in-memory FSes,
859// as often needed in multithreaded programs.
861 return sys::fs::UniqueID(std::numeric_limits<uint64_t>::max(),
862 uint64_t(size_t(Hash)));
863}
865 llvm::StringRef Name,
866 llvm::StringRef Contents) {
867 return getUniqueID(llvm::hash_combine(Parent.getFile(), Name, Contents));
868}
873
883
885 : Root(new detail::InMemoryDirectory(
886 Status("", getDirectoryID(llvm::sys::fs::UniqueID(), ""),
887 llvm::sys::TimePoint<>(), 0, 0, 0,
888 llvm::sys::fs::file_type::directory_file,
889 llvm::sys::fs::perms::all_all))),
890 UseNormalizedPaths(UseNormalizedPaths) {}
891
893
894std::string InMemoryFileSystem::toString() const {
895 return Root->toString(/*Indent=*/0);
896}
897
898bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
899 std::unique_ptr<llvm::MemoryBuffer> Buffer,
900 std::optional<uint32_t> User,
901 std::optional<uint32_t> Group,
902 std::optional<llvm::sys::fs::file_type> Type,
903 std::optional<llvm::sys::fs::perms> Perms,
904 MakeNodeFn MakeNode) {
905 SmallString<128> Path;
906 P.toVector(Path);
907
908 // Fix up relative paths. This just prepends the current working directory.
909 std::error_code EC = makeAbsolute(Path);
910 assert(!EC);
911 (void)EC;
912
913 if (useNormalizedPaths())
914 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
915
916 if (Path.empty())
917 return false;
918
919 detail::InMemoryDirectory *Dir = Root.get();
920 auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path);
921 const auto ResolvedUser = User.value_or(0);
922 const auto ResolvedGroup = Group.value_or(0);
923 const auto ResolvedType = Type.value_or(sys::fs::file_type::regular_file);
924 const auto ResolvedPerms = Perms.value_or(sys::fs::all_all);
925 // Any intermediate directories we create should be accessible by
926 // the owner, even if Perms says otherwise for the final path.
927 const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all;
928
929 StringRef Name = *I;
930 while (true) {
931 Name = *I;
932 ++I;
933 if (I == E)
934 break;
935 detail::InMemoryNode *Node = Dir->getChild(Name);
936 if (!Node) {
937 // This isn't the last element, so we create a new directory.
938 Status Stat(
939 StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
940 getDirectoryID(Dir->getUniqueID(), Name),
941 llvm::sys::toTimePoint(ModificationTime), ResolvedUser, ResolvedGroup,
942 0, sys::fs::file_type::directory_file, NewDirectoryPerms);
944 Name, std::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
945 continue;
946 }
947 // Creating file under another file.
949 return false;
951 }
952 detail::InMemoryNode *Node = Dir->getChild(Name);
953 if (!Node) {
954 Dir->addChild(Name,
955 MakeNode({Dir->getUniqueID(), Path, Name, ModificationTime,
956 std::move(Buffer), ResolvedUser, ResolvedGroup,
957 ResolvedType, ResolvedPerms}));
958 return true;
959 }
961 return ResolvedType == sys::fs::file_type::directory_file;
962
965 "Must be either file, hardlink or directory!");
966
967 // Return false only if the new file is different from the existing one.
968 if (auto *Link = dyn_cast<detail::InMemoryHardLink>(Node)) {
969 return Link->getResolvedFile().getBuffer()->getBuffer() ==
970 Buffer->getBuffer();
971 }
972 return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
973 Buffer->getBuffer();
974}
975
976bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
977 std::unique_ptr<llvm::MemoryBuffer> Buffer,
978 std::optional<uint32_t> User,
979 std::optional<uint32_t> Group,
980 std::optional<llvm::sys::fs::file_type> Type,
981 std::optional<llvm::sys::fs::perms> Perms) {
982 return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type,
983 Perms,
985 -> std::unique_ptr<detail::InMemoryNode> {
986 Status Stat = NNI.makeStatus();
988 return std::make_unique<detail::InMemoryDirectory>(Stat);
989 return std::make_unique<detail::InMemoryFile>(
990 Stat, std::move(NNI.Buffer));
991 });
992}
993
995 const Twine &P, time_t ModificationTime,
996 const llvm::MemoryBufferRef &Buffer, std::optional<uint32_t> User,
997 std::optional<uint32_t> Group, std::optional<llvm::sys::fs::file_type> Type,
998 std::optional<llvm::sys::fs::perms> Perms) {
999 return addFile(P, ModificationTime, llvm::MemoryBuffer::getMemBuffer(Buffer),
1000 std::move(User), std::move(Group), std::move(Type),
1001 std::move(Perms),
1003 -> std::unique_ptr<detail::InMemoryNode> {
1004 Status Stat = NNI.makeStatus();
1006 return std::make_unique<detail::InMemoryDirectory>(Stat);
1007 return std::make_unique<detail::InMemoryFile>(
1008 Stat, std::move(NNI.Buffer));
1009 });
1010}
1011
1013InMemoryFileSystem::lookupNode(const Twine &P, bool FollowFinalSymlink,
1014 size_t SymlinkDepth) const {
1015 SmallString<128> Path;
1016 P.toVector(Path);
1017
1018 // Fix up relative paths. This just prepends the current working directory.
1019 std::error_code EC = makeAbsolute(Path);
1020 assert(!EC);
1021 (void)EC;
1022
1023 if (useNormalizedPaths())
1024 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1025
1026 const detail::InMemoryDirectory *Dir = Root.get();
1027 if (Path.empty())
1028 return detail::NamedNodeOrError(Path, Dir);
1029
1030 auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
1031 while (true) {
1033 ++I;
1034 if (!Node)
1036
1037 if (auto Symlink = dyn_cast<detail::InMemorySymbolicLink>(Node)) {
1038 // If we're at the end of the path, and we're not following through
1039 // terminal symlinks, then we're done.
1040 if (I == E && !FollowFinalSymlink)
1041 return detail::NamedNodeOrError(Path, Symlink);
1042
1043 if (SymlinkDepth > InMemoryFileSystem::MaxSymlinkDepth)
1045
1046 SmallString<128> TargetPath = Symlink->getTargetPath();
1047 if (std::error_code EC = makeAbsolute(TargetPath))
1048 return EC;
1049
1050 // Keep going with the target. We always want to follow symlinks here
1051 // because we're either at the end of a path that we want to follow, or
1052 // not at the end of a path, in which case we need to follow the symlink
1053 // regardless.
1054 auto Target =
1055 lookupNode(TargetPath, /*FollowFinalSymlink=*/true, SymlinkDepth + 1);
1056 if (!Target || I == E)
1057 return Target;
1058
1061
1062 // Otherwise, continue on the search in the symlinked directory.
1064 continue;
1065 }
1066
1067 // Return the file if it's at the end of the path.
1069 if (I == E)
1070 return detail::NamedNodeOrError(Path, File);
1072 }
1073
1074 // If Node is HardLink then return the resolved file.
1076 if (I == E)
1077 return detail::NamedNodeOrError(Path, &File->getResolvedFile());
1079 }
1080 // Traverse directories.
1082 if (I == E)
1083 return detail::NamedNodeOrError(Path, Dir);
1084 }
1085}
1086
1088 const Twine &Target) {
1089 auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
1090 // Whether symlinks in the hardlink target are followed is
1091 // implementation-defined in POSIX.
1092 // We're following symlinks here to be consistent with macOS.
1093 auto TargetNode = lookupNode(Target, /*FollowFinalSymlink=*/true);
1094 // FromPath must not have been added before. ToPath must have been added
1095 // before. Resolved ToPath must be a File.
1096 if (!TargetNode || NewLinkNode || !isa<detail::InMemoryFile>(*TargetNode))
1097 return false;
1098 return addFile(NewLink, 0, nullptr, std::nullopt, std::nullopt, std::nullopt,
1099 std::nullopt, [&](detail::NewInMemoryNodeInfo NNI) {
1100 return std::make_unique<detail::InMemoryHardLink>(
1101 NNI.Path.str(),
1102 *cast<detail::InMemoryFile>(*TargetNode));
1103 });
1104}
1105
1107 const Twine &NewLink, const Twine &Target, time_t ModificationTime,
1108 std::optional<uint32_t> User, std::optional<uint32_t> Group,
1109 std::optional<llvm::sys::fs::perms> Perms) {
1110 auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
1111 if (NewLinkNode)
1112 return false;
1113
1114 SmallString<128> NewLinkStr, TargetStr;
1115 NewLink.toVector(NewLinkStr);
1116 Target.toVector(TargetStr);
1117
1118 return addFile(NewLinkStr, ModificationTime, nullptr, User, Group,
1121 return std::make_unique<detail::InMemorySymbolicLink>(
1122 NewLinkStr, TargetStr, NNI.makeStatus());
1123 });
1124}
1125
1127 auto Node = lookupNode(Path, /*FollowFinalSymlink=*/true);
1128 if (Node)
1129 return (*Node)->getStatus(Path);
1130 return Node.getError();
1131}
1132
1135 auto Node = lookupNode(Path,/*FollowFinalSymlink=*/true);
1136 if (!Node)
1137 return Node.getError();
1138
1139 // When we have a file provide a heap-allocated wrapper for the memory buffer
1140 // to match the ownership semantics for File.
1142 return std::unique_ptr<File>(
1143 new detail::InMemoryFileAdaptor(*F, Path.str()));
1144
1145 // FIXME: errc::not_a_file?
1147}
1148
1149/// Adaptor from InMemoryDir::iterator to directory_iterator.
1151 const InMemoryFileSystem *FS;
1154 std::string RequestedDirName;
1155
1156 void setCurrentEntry() {
1157 if (I != E) {
1158 SmallString<256> Path(RequestedDirName);
1159 llvm::sys::path::append(Path, I->second->getFileName());
1161 switch (I->second->getKind()) {
1162 case detail::IME_File:
1165 break;
1168 break;
1170 if (auto SymlinkTarget =
1171 FS->lookupNode(Path, /*FollowFinalSymlink=*/true)) {
1172 Path = SymlinkTarget.getName();
1173 Type = (*SymlinkTarget)->getStatus(Path).getType();
1174 }
1175 break;
1176 }
1177 CurrentEntry = directory_entry(std::string(Path), Type);
1178 } else {
1179 // When we're at the end, make CurrentEntry invalid and DirIterImpl will
1180 // do the rest.
1182 }
1183 }
1184
1185public:
1186 DirIterator() = default;
1187
1189 const detail::InMemoryDirectory &Dir,
1190 std::string RequestedDirName)
1191 : FS(FS), I(Dir.begin()), E(Dir.end()),
1192 RequestedDirName(std::move(RequestedDirName)) {
1193 setCurrentEntry();
1194 }
1195
1196 std::error_code increment() override {
1197 ++I;
1198 setCurrentEntry();
1199 return {};
1200 }
1201};
1202
1204 std::error_code &EC) {
1205 auto Node = lookupNode(Dir, /*FollowFinalSymlink=*/true);
1206 if (!Node) {
1207 EC = Node.getError();
1208 return directory_iterator(std::make_shared<DirIterator>());
1209 }
1210
1211 if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
1212 return directory_iterator(
1213 std::make_shared<DirIterator>(this, *DirNode, Dir.str()));
1214
1216 return directory_iterator(std::make_shared<DirIterator>());
1217}
1218
1220 SmallString<128> Path;
1221 P.toVector(Path);
1222
1223 // Fix up relative paths. This just prepends the current working directory.
1224 std::error_code EC = makeAbsolute(Path);
1225 assert(!EC);
1226 (void)EC;
1227
1228 if (useNormalizedPaths())
1229 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1230
1231 if (!Path.empty())
1232 WorkingDirectory = std::string(Path);
1233 return {};
1234}
1235
1236std::error_code InMemoryFileSystem::getRealPath(const Twine &Path,
1237 SmallVectorImpl<char> &Output) {
1238 auto CWD = getCurrentWorkingDirectory();
1239 if (!CWD || CWD->empty())
1241 Path.toVector(Output);
1242 if (auto EC = makeAbsolute(Output))
1243 return EC;
1244 llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true);
1245 return {};
1246}
1247
1248std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
1249 Result = false;
1250 return {};
1251}
1252
1253void InMemoryFileSystem::printImpl(raw_ostream &OS, PrintType PrintContents,
1254 unsigned IndentLevel) const {
1255 printIndent(OS, IndentLevel);
1256 OS << "InMemoryFileSystem\n";
1257}
1258
1259} // namespace vfs
1260} // namespace llvm
1261
1262//===-----------------------------------------------------------------------===/
1263// RedirectingFileSystem implementation
1264//===-----------------------------------------------------------------------===/
1265
1266namespace {
1267
1268static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) {
1269 // Detect the path style in use by checking the first separator.
1271 const size_t n = Path.find_first_of("/\\");
1272 // Can't distinguish between posix and windows_slash here.
1273 if (n != static_cast<size_t>(-1))
1274 style = (Path[n] == '/') ? llvm::sys::path::Style::posix
1275 : llvm::sys::path::Style::windows_backslash;
1276 return style;
1277}
1278
1279/// Removes leading "./" as well as path components like ".." and ".".
1280static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {
1281 // First detect the path style in use by checking the first separator.
1282 llvm::sys::path::Style style = getExistingStyle(Path);
1283
1284 // Now remove the dots. Explicitly specifying the path style prevents the
1285 // direction of the slashes from changing.
1286 llvm::SmallString<256> result =
1288 llvm::sys::path::remove_dots(result, /*remove_dot_dot=*/true, style);
1289 return result;
1290}
1291
1292/// Whether the error and entry specify a file/directory that was not found.
1293static bool isFileNotFound(std::error_code EC,
1294 RedirectingFileSystem::Entry *E = nullptr) {
1296 return false;
1298}
1299
1300} // anonymous namespace
1301
1302
1303RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
1304 : ExternalFS(std::move(FS)) {
1305 assert(ExternalFS && "RedirectingFileSystem requires an external FS");
1306 if (auto ExternalWorkingDirectory = ExternalFS->getCurrentWorkingDirectory())
1307 WorkingDirectory = *ExternalWorkingDirectory;
1308}
1309
1310/// Directory iterator implementation for \c RedirectingFileSystem's
1311/// directory entries.
1314 std::string Dir;
1316
1317 std::error_code incrementImpl(bool IsFirstTime) {
1318 assert((IsFirstTime || Current != End) && "cannot iterate past end");
1319 if (!IsFirstTime)
1320 ++Current;
1321 if (Current != End) {
1322 SmallString<128> PathStr(Dir);
1323 llvm::sys::path::append(PathStr, (*Current)->getName());
1325 switch ((*Current)->getKind()) {
1327 [[fallthrough]];
1330 break;
1333 break;
1334 }
1335 CurrentEntry = directory_entry(std::string(PathStr), Type);
1336 } else {
1338 }
1339 return {};
1340 };
1341
1342public:
1345 RedirectingFileSystem::DirectoryEntry::iterator End, std::error_code &EC)
1346 : Dir(Path.str()), Current(Begin), End(End) {
1347 EC = incrementImpl(/*IsFirstTime=*/true);
1348 }
1349
1350 std::error_code increment() override {
1351 return incrementImpl(/*IsFirstTime=*/false);
1352 }
1353};
1354
1355namespace {
1356/// Directory iterator implementation for \c RedirectingFileSystem's
1357/// directory remap entries that maps the paths reported by the external
1358/// file system's directory iterator back to the virtual directory's path.
1359class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl {
1360 std::string Dir;
1361 llvm::sys::path::Style DirStyle;
1362 llvm::vfs::directory_iterator ExternalIter;
1363
1364public:
1365 RedirectingFSDirRemapIterImpl(std::string DirPath,
1367 : Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)),
1368 ExternalIter(ExtIter) {
1369 if (ExternalIter != llvm::vfs::directory_iterator())
1370 setCurrentEntry();
1371 }
1372
1373 void setCurrentEntry() {
1374 StringRef ExternalPath = ExternalIter->path();
1375 llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath);
1376 StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle);
1377
1378 SmallString<128> NewPath(Dir);
1379 llvm::sys::path::append(NewPath, DirStyle, File);
1380
1381 CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type());
1382 }
1383
1384 std::error_code increment() override {
1385 std::error_code EC;
1386 ExternalIter.increment(EC);
1387 if (!EC && ExternalIter != llvm::vfs::directory_iterator())
1388 setCurrentEntry();
1389 else
1390 CurrentEntry = directory_entry();
1391 return EC;
1392 }
1393};
1394} // namespace
1395
1396llvm::ErrorOr<std::string>
1398 return WorkingDirectory;
1399}
1400
1401std::error_code
1403 // Don't change the working directory if the path doesn't exist.
1404 if (!exists(Path))
1406
1407 SmallString<128> AbsolutePath;
1408 Path.toVector(AbsolutePath);
1409 if (std::error_code EC = makeAbsolute(AbsolutePath))
1410 return EC;
1411 WorkingDirectory = std::string(AbsolutePath);
1412 return {};
1413}
1414
1415std::error_code RedirectingFileSystem::isLocal(const Twine &Path_,
1416 bool &Result) {
1417 SmallString<256> Path;
1418 Path_.toVector(Path);
1419
1420 if (makeAbsolute(Path))
1421 return {};
1422
1423 return ExternalFS->isLocal(Path, Result);
1424}
1425
1426std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1427 // is_absolute(..., Style::windows_*) accepts paths with both slash types.
1431 // This covers windows absolute path with forward slash as well, as the
1432 // forward slashes are treated as path separation in llvm::path
1433 // regardless of what path::Style is used.
1434 return {};
1435
1436 auto WorkingDir = getCurrentWorkingDirectory();
1437 if (!WorkingDir)
1438 return WorkingDir.getError();
1439
1440 return makeAbsolute(WorkingDir.get(), Path);
1441}
1442
1443std::error_code
1444RedirectingFileSystem::makeAbsolute(StringRef WorkingDir,
1445 SmallVectorImpl<char> &Path) const {
1446 // We can't use sys::fs::make_absolute because that assumes the path style
1447 // is native and there is no way to override that. Since we know WorkingDir
1448 // is absolute, we can use it to determine which style we actually have and
1449 // append Path ourselves.
1450 if (!WorkingDir.empty() &&
1452 !sys::path::is_absolute(WorkingDir,
1454 return std::error_code();
1455 }
1459 } else {
1460 // Distinguish between windows_backslash and windows_slash; getExistingStyle
1461 // returns posix for a path with windows_slash.
1462 if (getExistingStyle(WorkingDir) != sys::path::Style::windows_backslash)
1464 }
1465
1466 std::string Result = std::string(WorkingDir);
1467 StringRef Dir(Result);
1468 if (!Dir.ends_with(sys::path::get_separator(style))) {
1470 }
1471 // backslashes '\' are legit path charactors under POSIX. Windows APIs
1472 // like CreateFile accepts forward slashes '/' as path
1473 // separator (even when mixed with backslashes). Therefore,
1474 // `Path` should be directly appended to `WorkingDir` without converting
1475 // path separator.
1476 Result.append(Path.data(), Path.size());
1477 Path.assign(Result.begin(), Result.end());
1478
1479 return {};
1480}
1481
1483 std::error_code &EC) {
1484 SmallString<256> Path;
1485 Dir.toVector(Path);
1486
1487 EC = makeAbsolute(Path);
1488 if (EC)
1489 return {};
1490
1492 if (!Result) {
1493 if (Redirection != RedirectKind::RedirectOnly &&
1494 isFileNotFound(Result.getError()))
1495 return ExternalFS->dir_begin(Path, EC);
1496
1497 EC = Result.getError();
1498 return {};
1499 }
1500
1501 // Use status to make sure the path exists and refers to a directory.
1502 ErrorOr<Status> S = status(Path, Dir, *Result);
1503 if (!S) {
1504 if (Redirection != RedirectKind::RedirectOnly &&
1505 isFileNotFound(S.getError(), Result->E))
1506 return ExternalFS->dir_begin(Dir, EC);
1507
1508 EC = S.getError();
1509 return {};
1510 }
1511
1512 if (!S->isDirectory()) {
1514 return {};
1515 }
1516
1517 // Create the appropriate directory iterator based on whether we found a
1518 // DirectoryRemapEntry or DirectoryEntry.
1519 directory_iterator RedirectIter;
1520 std::error_code RedirectEC;
1521 if (auto ExtRedirect = Result->getExternalRedirect()) {
1522 auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
1523 RedirectIter = ExternalFS->dir_begin(*ExtRedirect, RedirectEC);
1524
1525 if (!RE->useExternalName(UseExternalNames)) {
1526 // Update the paths in the results to use the virtual directory's path.
1527 RedirectIter =
1528 directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>(
1529 std::string(Path), RedirectIter));
1530 }
1531 } else {
1532 auto DE = cast<DirectoryEntry>(Result->E);
1533 RedirectIter =
1534 directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
1535 Path, DE->contents_begin(), DE->contents_end(), RedirectEC));
1536 }
1537
1538 if (RedirectEC) {
1539 if (RedirectEC != errc::no_such_file_or_directory) {
1540 EC = RedirectEC;
1541 return {};
1542 }
1543 RedirectIter = {};
1544 }
1545
1546 if (Redirection == RedirectKind::RedirectOnly) {
1547 EC = RedirectEC;
1548 return RedirectIter;
1549 }
1550
1551 std::error_code ExternalEC;
1552 directory_iterator ExternalIter = ExternalFS->dir_begin(Path, ExternalEC);
1553 if (ExternalEC) {
1554 if (ExternalEC != errc::no_such_file_or_directory) {
1555 EC = ExternalEC;
1556 return {};
1557 }
1558 ExternalIter = {};
1559 }
1560
1562 switch (Redirection) {
1564 Iters.push_back(ExternalIter);
1565 Iters.push_back(RedirectIter);
1566 break;
1568 Iters.push_back(RedirectIter);
1569 Iters.push_back(ExternalIter);
1570 break;
1571 default:
1572 llvm_unreachable("unhandled RedirectKind");
1573 }
1574
1575 directory_iterator Combined{
1576 std::make_shared<CombiningDirIterImpl>(Iters, EC)};
1577 if (EC)
1578 return {};
1579 return Combined;
1580}
1581
1583 OverlayFileDir = Dir.str();
1584}
1585
1587 return OverlayFileDir;
1588}
1589
1597
1600 Redirection = Kind;
1601}
1602
1603std::vector<StringRef> RedirectingFileSystem::getRoots() const {
1604 std::vector<StringRef> R;
1605 R.reserve(Roots.size());
1606 for (const auto &Root : Roots)
1607 R.push_back(Root->getName());
1608 return R;
1609}
1610
1612 unsigned IndentLevel) const {
1613 printIndent(OS, IndentLevel);
1614 OS << "RedirectingFileSystem (UseExternalNames: "
1615 << (UseExternalNames ? "true" : "false") << ")\n";
1616 if (Type == PrintType::Summary)
1617 return;
1618
1619 for (const auto &Root : Roots)
1620 printEntry(OS, Root.get(), IndentLevel);
1621
1622 printIndent(OS, IndentLevel);
1623 OS << "ExternalFS:\n";
1624 ExternalFS->print(OS, Type == PrintType::Contents ? PrintType::Summary : Type,
1625 IndentLevel + 1);
1626}
1627
1630 unsigned IndentLevel) const {
1631 printIndent(OS, IndentLevel);
1632 OS << "'" << E->getName() << "'";
1633
1634 switch (E->getKind()) {
1635 case EK_Directory: {
1637
1638 OS << "\n";
1639 for (std::unique_ptr<Entry> &SubEntry :
1640 llvm::make_range(DE->contents_begin(), DE->contents_end()))
1641 printEntry(OS, SubEntry.get(), IndentLevel + 1);
1642 break;
1643 }
1644 case EK_DirectoryRemap:
1645 case EK_File: {
1647 OS << " -> '" << RE->getExternalContentsPath() << "'";
1648 switch (RE->getUseName()) {
1649 case NK_NotSet:
1650 break;
1651 case NK_External:
1652 OS << " (UseExternalName: true)";
1653 break;
1654 case NK_Virtual:
1655 OS << " (UseExternalName: false)";
1656 break;
1657 }
1658 OS << "\n";
1659 break;
1660 }
1661 }
1662}
1663
1665 if (ExternalFS) {
1666 Callback(*ExternalFS);
1667 ExternalFS->visitChildFileSystems(Callback);
1668 }
1669}
1670
1671/// A helper class to hold the common YAML parsing state.
1673 yaml::Stream &Stream;
1674
1675 void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); }
1676
1677 // false on error
1678 bool parseScalarString(yaml::Node *N, StringRef &Result,
1679 SmallVectorImpl<char> &Storage) {
1681
1682 if (!S) {
1683 error(N, "expected string");
1684 return false;
1685 }
1686 Result = S->getValue(Storage);
1687 return true;
1688 }
1689
1690 // false on error
1691 bool parseScalarBool(yaml::Node *N, bool &Result) {
1692 SmallString<5> Storage;
1694 if (!parseScalarString(N, Value, Storage))
1695 return false;
1696
1697 if (Value.equals_insensitive("true") || Value.equals_insensitive("on") ||
1698 Value.equals_insensitive("yes") || Value == "1") {
1699 Result = true;
1700 return true;
1701 } else if (Value.equals_insensitive("false") ||
1702 Value.equals_insensitive("off") ||
1703 Value.equals_insensitive("no") || Value == "0") {
1704 Result = false;
1705 return true;
1706 }
1707
1708 error(N, "expected boolean value");
1709 return false;
1710 }
1711
1712 std::optional<RedirectingFileSystem::RedirectKind>
1713 parseRedirectKind(yaml::Node *N) {
1714 SmallString<12> Storage;
1716 if (!parseScalarString(N, Value, Storage))
1717 return std::nullopt;
1718
1719 if (Value.equals_insensitive("fallthrough")) {
1721 } else if (Value.equals_insensitive("fallback")) {
1723 } else if (Value.equals_insensitive("redirect-only")) {
1725 }
1726 return std::nullopt;
1727 }
1728
1729 std::optional<RedirectingFileSystem::RootRelativeKind>
1730 parseRootRelativeKind(yaml::Node *N) {
1731 SmallString<12> Storage;
1733 if (!parseScalarString(N, Value, Storage))
1734 return std::nullopt;
1735 if (Value.equals_insensitive("cwd")) {
1737 } else if (Value.equals_insensitive("overlay-dir")) {
1739 }
1740 return std::nullopt;
1741 }
1742
1743 struct KeyStatus {
1744 bool Required;
1745 bool Seen = false;
1746
1747 KeyStatus(bool Required = false) : Required(Required) {}
1748 };
1749
1750 using KeyStatusPair = std::pair<StringRef, KeyStatus>;
1751
1752 // false on error
1753 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
1755 auto It = Keys.find(Key);
1756 if (It == Keys.end()) {
1757 error(KeyNode, "unknown key");
1758 return false;
1759 }
1760 KeyStatus &S = It->second;
1761 if (S.Seen) {
1762 error(KeyNode, Twine("duplicate key '") + Key + "'");
1763 return false;
1764 }
1765 S.Seen = true;
1766 return true;
1767 }
1768
1769 // false on error
1770 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
1771 for (const auto &I : Keys) {
1772 if (I.second.Required && !I.second.Seen) {
1773 error(Obj, Twine("missing key '") + I.first + "'");
1774 return false;
1775 }
1776 }
1777 return true;
1778 }
1779
1780public:
1783 RedirectingFileSystem::Entry *ParentEntry = nullptr) {
1784 if (!ParentEntry) { // Look for a existent root
1785 for (const auto &Root : FS->Roots) {
1786 if (Name == Root->getName()) {
1787 ParentEntry = Root.get();
1788 return ParentEntry;
1789 }
1790 }
1791 } else { // Advance to the next component
1793 for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :
1794 llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1795 auto *DirContent =
1797 if (DirContent && Name == Content->getName())
1798 return DirContent;
1799 }
1800 }
1801
1802 // ... or create a new one
1803 std::unique_ptr<RedirectingFileSystem::Entry> E =
1804 std::make_unique<RedirectingFileSystem::DirectoryEntry>(
1805 Name, Status("", getNextVirtualUniqueID(),
1806 std::chrono::system_clock::now(), 0, 0, 0,
1807 file_type::directory_file, sys::fs::all_all));
1808
1809 if (!ParentEntry) { // Add a new root to the overlay
1810 FS->Roots.push_back(std::move(E));
1811 ParentEntry = FS->Roots.back().get();
1812 return ParentEntry;
1813 }
1814
1815 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
1816 DE->addContent(std::move(E));
1817 return DE->getLastContent();
1818 }
1819
1820private:
1821 void uniqueOverlayTree(RedirectingFileSystem *FS,
1823 RedirectingFileSystem::Entry *NewParentE = nullptr) {
1824 StringRef Name = SrcE->getName();
1825 switch (SrcE->getKind()) {
1828 // Empty directories could be present in the YAML as a way to
1829 // describe a file for a current directory after some of its subdir
1830 // is parsed. This only leads to redundant walks, ignore it.
1831 if (!Name.empty())
1832 NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
1833 for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
1834 llvm::make_range(DE->contents_begin(), DE->contents_end()))
1835 uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
1836 break;
1837 }
1839 assert(NewParentE && "Parent entry must exist");
1841 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1842 DE->addContent(
1843 std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
1844 Name, DR->getExternalContentsPath(), DR->getUseName()));
1845 break;
1846 }
1848 assert(NewParentE && "Parent entry must exist");
1850 auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1851 DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>(
1852 Name, FE->getExternalContentsPath(), FE->getUseName()));
1853 break;
1854 }
1855 }
1856 }
1857
1858 std::unique_ptr<RedirectingFileSystem::Entry>
1859 parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) {
1861 if (!M) {
1862 error(N, "expected mapping node for file or directory entry");
1863 return nullptr;
1864 }
1865
1866 KeyStatusPair Fields[] = {
1867 KeyStatusPair("name", true),
1868 KeyStatusPair("type", true),
1869 KeyStatusPair("contents", false),
1870 KeyStatusPair("external-contents", false),
1871 KeyStatusPair("use-external-name", false),
1872 };
1873
1874 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1875
1876 enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;
1877 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
1878 EntryArrayContents;
1879 SmallString<256> ExternalContentsPath;
1880 SmallString<256> Name;
1881 yaml::Node *NameValueNode = nullptr;
1882 auto UseExternalName = RedirectingFileSystem::NK_NotSet;
1884
1885 for (auto &I : *M) {
1886 StringRef Key;
1887 // Reuse the buffer for key and value, since we don't look at key after
1888 // parsing value.
1889 SmallString<256> Buffer;
1890 if (!parseScalarString(I.getKey(), Key, Buffer))
1891 return nullptr;
1892
1893 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
1894 return nullptr;
1895
1896 StringRef Value;
1897 if (Key == "name") {
1898 if (!parseScalarString(I.getValue(), Value, Buffer))
1899 return nullptr;
1900
1901 NameValueNode = I.getValue();
1902 // Guarantee that old YAML files containing paths with ".." and "."
1903 // are properly canonicalized before read into the VFS.
1904 Name = canonicalize(Value).str();
1905 } else if (Key == "type") {
1906 if (!parseScalarString(I.getValue(), Value, Buffer))
1907 return nullptr;
1908 if (Value == "file")
1910 else if (Value == "directory")
1912 else if (Value == "directory-remap")
1914 else {
1915 error(I.getValue(), "unknown value for 'type'");
1916 return nullptr;
1917 }
1918 } else if (Key == "contents") {
1919 if (ContentsField != CF_NotSet) {
1920 error(I.getKey(),
1921 "entry already has 'contents' or 'external-contents'");
1922 return nullptr;
1923 }
1924 ContentsField = CF_List;
1925 auto *Contents = dyn_cast_if_present<yaml::SequenceNode>(I.getValue());
1926 if (!Contents) {
1927 // FIXME: this is only for directories, what about files?
1928 error(I.getValue(), "expected array");
1929 return nullptr;
1930 }
1931
1932 for (auto &I : *Contents) {
1933 if (std::unique_ptr<RedirectingFileSystem::Entry> E =
1934 parseEntry(&I, FS, /*IsRootEntry*/ false))
1935 EntryArrayContents.push_back(std::move(E));
1936 else
1937 return nullptr;
1938 }
1939 } else if (Key == "external-contents") {
1940 if (ContentsField != CF_NotSet) {
1941 error(I.getKey(),
1942 "entry already has 'contents' or 'external-contents'");
1943 return nullptr;
1944 }
1945 ContentsField = CF_External;
1946 if (!parseScalarString(I.getValue(), Value, Buffer))
1947 return nullptr;
1948
1949 SmallString<256> FullPath;
1950 if (FS->IsRelativeOverlay) {
1951 FullPath = FS->getOverlayFileDir();
1952 assert(!FullPath.empty() &&
1953 "External contents prefix directory must exist");
1954 SmallString<256> AbsFullPath = Value;
1955 if (FS->makeAbsolute(FullPath, AbsFullPath)) {
1956 error(N, "failed to make 'external-contents' absolute");
1957 return nullptr;
1958 }
1959 FullPath = AbsFullPath;
1960 } else {
1961 FullPath = Value;
1962 }
1963
1964 // Guarantee that old YAML files containing paths with ".." and "."
1965 // are properly canonicalized before read into the VFS.
1966 FullPath = canonicalize(FullPath);
1967 ExternalContentsPath = FullPath.str();
1968 } else if (Key == "use-external-name") {
1969 bool Val;
1970 if (!parseScalarBool(I.getValue(), Val))
1971 return nullptr;
1972 UseExternalName = Val ? RedirectingFileSystem::NK_External
1974 } else {
1975 llvm_unreachable("key missing from Keys");
1976 }
1977 }
1978
1979 if (Stream.failed())
1980 return nullptr;
1981
1982 // check for missing keys
1983 if (ContentsField == CF_NotSet) {
1984 error(N, "missing key 'contents' or 'external-contents'");
1985 return nullptr;
1986 }
1987 if (!checkMissingKeys(N, Keys))
1988 return nullptr;
1989
1990 // check invalid configuration
1992 UseExternalName != RedirectingFileSystem::NK_NotSet) {
1993 error(N, "'use-external-name' is not supported for 'directory' entries");
1994 return nullptr;
1995 }
1996
1998 ContentsField == CF_List) {
1999 error(N, "'contents' is not supported for 'directory-remap' entries");
2000 return nullptr;
2001 }
2002
2003 sys::path::Style path_style = sys::path::Style::native;
2004 if (IsRootEntry) {
2005 // VFS root entries may be in either Posix or Windows style. Figure out
2006 // which style we have, and use it consistently.
2007 if (sys::path::is_absolute(Name, sys::path::Style::posix)) {
2008 path_style = sys::path::Style::posix;
2009 } else if (sys::path::is_absolute(Name,
2010 sys::path::Style::windows_backslash)) {
2011 path_style = sys::path::Style::windows_backslash;
2012 } else {
2013 // Relative VFS root entries are made absolute to either the overlay
2014 // directory, or the current working directory, then we can determine
2015 // the path style from that.
2016 std::error_code EC;
2017 if (FS->RootRelative ==
2018 RedirectingFileSystem::RootRelativeKind::OverlayDir) {
2019 StringRef FullPath = FS->getOverlayFileDir();
2020 assert(!FullPath.empty() && "Overlay file directory must exist");
2021 EC = FS->makeAbsolute(FullPath, Name);
2022 Name = canonicalize(Name);
2023 } else {
2024 EC = FS->makeAbsolute(Name);
2025 }
2026 if (EC) {
2027 assert(NameValueNode && "Name presence should be checked earlier");
2028 error(
2029 NameValueNode,
2030 "entry with relative path at the root level is not discoverable");
2031 return nullptr;
2032 }
2033 path_style = sys::path::is_absolute(Name, sys::path::Style::posix)
2034 ? sys::path::Style::posix
2035 : sys::path::Style::windows_backslash;
2036 }
2037 // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will
2038 // return true even if `Name` is using forward slashes. Distinguish
2039 // between windows_backslash and windows_slash.
2040 if (path_style == sys::path::Style::windows_backslash &&
2041 getExistingStyle(Name) != sys::path::Style::windows_backslash)
2042 path_style = sys::path::Style::windows_slash;
2043 }
2044
2045 // Remove trailing slash(es), being careful not to remove the root path
2046 StringRef Trimmed = Name;
2047 size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();
2048 while (Trimmed.size() > RootPathLen &&
2049 sys::path::is_separator(Trimmed.back(), path_style))
2050 Trimmed = Trimmed.slice(0, Trimmed.size() - 1);
2051
2052 // Get the last component
2053 StringRef LastComponent = sys::path::filename(Trimmed, path_style);
2054
2055 std::unique_ptr<RedirectingFileSystem::Entry> Result;
2056 switch (Kind) {
2058 Result = std::make_unique<RedirectingFileSystem::FileEntry>(
2059 LastComponent, std::move(ExternalContentsPath), UseExternalName);
2060 break;
2062 Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
2063 LastComponent, std::move(ExternalContentsPath), UseExternalName);
2064 break;
2066 Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
2067 LastComponent, std::move(EntryArrayContents),
2068 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2069 0, 0, 0, file_type::directory_file, sys::fs::all_all));
2070 break;
2071 }
2072
2073 StringRef Parent = sys::path::parent_path(Trimmed, path_style);
2074 if (Parent.empty())
2075 return Result;
2076
2077 // if 'name' contains multiple components, create implicit directory entries
2078 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style),
2079 E = sys::path::rend(Parent);
2080 I != E; ++I) {
2081 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;
2082 Entries.push_back(std::move(Result));
2083 Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
2084 *I, std::move(Entries),
2085 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2086 0, 0, 0, file_type::directory_file, sys::fs::all_all));
2087 }
2088 return Result;
2089 }
2090
2091public:
2093
2094 // false on error
2096 auto *Top = dyn_cast<yaml::MappingNode>(Root);
2097 if (!Top) {
2098 error(Root, "expected mapping node");
2099 return false;
2100 }
2101
2102 KeyStatusPair Fields[] = {
2103 KeyStatusPair("version", true),
2104 KeyStatusPair("case-sensitive", false),
2105 KeyStatusPair("use-external-names", false),
2106 KeyStatusPair("root-relative", false),
2107 KeyStatusPair("overlay-relative", false),
2108 KeyStatusPair("fallthrough", false),
2109 KeyStatusPair("redirecting-with", false),
2110 KeyStatusPair("roots", true),
2111 };
2112
2113 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
2114 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries;
2115
2116 // Parse configuration and 'roots'
2117 for (auto &I : *Top) {
2118 SmallString<10> KeyBuffer;
2119 StringRef Key;
2120 if (!parseScalarString(I.getKey(), Key, KeyBuffer))
2121 return false;
2122
2123 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
2124 return false;
2125
2126 if (Key == "roots") {
2127 auto *Roots = dyn_cast_if_present<yaml::SequenceNode>(I.getValue());
2128 if (!Roots) {
2129 error(I.getValue(), "expected array");
2130 return false;
2131 }
2132
2133 for (auto &I : *Roots) {
2134 if (std::unique_ptr<RedirectingFileSystem::Entry> E =
2135 parseEntry(&I, FS, /*IsRootEntry*/ true))
2136 RootEntries.push_back(std::move(E));
2137 else
2138 return false;
2139 }
2140 } else if (Key == "version") {
2141 StringRef VersionString;
2142 SmallString<4> Storage;
2143 if (!parseScalarString(I.getValue(), VersionString, Storage))
2144 return false;
2145 int Version;
2146 if (VersionString.getAsInteger<int>(10, Version)) {
2147 error(I.getValue(), "expected integer");
2148 return false;
2149 }
2150 if (Version < 0) {
2151 error(I.getValue(), "invalid version number");
2152 return false;
2153 }
2154 if (Version != 0) {
2155 error(I.getValue(), "version mismatch, expected 0");
2156 return false;
2157 }
2158 } else if (Key == "case-sensitive") {
2159 if (!parseScalarBool(I.getValue(), FS->CaseSensitive))
2160 return false;
2161 } else if (Key == "overlay-relative") {
2162 if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay))
2163 return false;
2164 } else if (Key == "use-external-names") {
2165 if (!parseScalarBool(I.getValue(), FS->UseExternalNames))
2166 return false;
2167 } else if (Key == "fallthrough") {
2168 if (Keys["redirecting-with"].Seen) {
2169 error(I.getValue(),
2170 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2171 return false;
2172 }
2173
2174 bool ShouldFallthrough = false;
2175 if (!parseScalarBool(I.getValue(), ShouldFallthrough))
2176 return false;
2177
2178 if (ShouldFallthrough) {
2180 } else {
2182 }
2183 } else if (Key == "redirecting-with") {
2184 if (Keys["fallthrough"].Seen) {
2185 error(I.getValue(),
2186 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2187 return false;
2188 }
2189
2190 if (auto Kind = parseRedirectKind(I.getValue())) {
2191 FS->Redirection = *Kind;
2192 } else {
2193 error(I.getValue(), "expected valid redirect kind");
2194 return false;
2195 }
2196 } else if (Key == "root-relative") {
2197 if (auto Kind = parseRootRelativeKind(I.getValue())) {
2198 FS->RootRelative = *Kind;
2199 } else {
2200 error(I.getValue(), "expected valid root-relative kind");
2201 return false;
2202 }
2203 } else {
2204 llvm_unreachable("key missing from Keys");
2205 }
2206 }
2207
2208 if (Stream.failed())
2209 return false;
2210
2211 if (!checkMissingKeys(Top, Keys))
2212 return false;
2213
2214 // Now that we sucessefully parsed the YAML file, canonicalize the internal
2215 // representation to a proper directory tree so that we can search faster
2216 // inside the VFS.
2217 for (auto &E : RootEntries)
2218 uniqueOverlayTree(FS, E.get());
2219
2220 return true;
2221 }
2222};
2223
2224std::unique_ptr<RedirectingFileSystem>
2225RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
2227 StringRef YAMLFilePath, void *DiagContext,
2228 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2229 SourceMgr SM;
2230 yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
2231
2232 SM.setDiagHandler(DiagHandler, DiagContext);
2233 yaml::document_iterator DI = Stream.begin();
2234 yaml::Node *Root = DI->getRoot();
2235 if (DI == Stream.end() || !Root) {
2236 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
2237 return nullptr;
2238 }
2239
2241
2242 std::unique_ptr<RedirectingFileSystem> FS(
2243 new RedirectingFileSystem(ExternalFS));
2244
2245 if (!YAMLFilePath.empty()) {
2246 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
2247 // to each 'external-contents' path.
2248 //
2249 // Example:
2250 // -ivfsoverlay dummy.cache/vfs/vfs.yaml
2251 // yields:
2252 // FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs
2253 //
2254 SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
2255 std::error_code EC = FS->makeAbsolute(OverlayAbsDir);
2256 assert(!EC && "Overlay dir final path must be absolute");
2257 (void)EC;
2258 FS->setOverlayFileDir(OverlayAbsDir);
2259 }
2260
2261 if (!P.parse(Root, FS.get()))
2262 return nullptr;
2263
2264 return FS;
2265}
2266
2267std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
2268 ArrayRef<std::pair<std::string, std::string>> RemappedFiles,
2269 bool UseExternalNames, llvm::IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2270 std::unique_ptr<RedirectingFileSystem> FS(
2271 new RedirectingFileSystem(ExternalFS));
2272 FS->UseExternalNames = UseExternalNames;
2273
2275
2276 for (auto &Mapping : llvm::reverse(RemappedFiles)) {
2277 SmallString<128> From = StringRef(Mapping.first);
2278 SmallString<128> To = StringRef(Mapping.second);
2279 {
2280 auto EC = ExternalFS->makeAbsolute(From);
2281 (void)EC;
2282 assert(!EC && "Could not make absolute path");
2283 }
2284
2285 // Check if we've already mapped this file. The first one we see (in the
2286 // reverse iteration) wins.
2287 RedirectingFileSystem::Entry *&ToEntry = Entries[From];
2288 if (ToEntry)
2289 continue;
2290
2291 // Add parent directories.
2292 RedirectingFileSystem::Entry *Parent = nullptr;
2293 StringRef FromDirectory = llvm::sys::path::parent_path(From);
2294 for (auto I = llvm::sys::path::begin(FromDirectory),
2295 E = llvm::sys::path::end(FromDirectory);
2296 I != E; ++I) {
2298 Parent);
2299 }
2300 assert(Parent && "File without a directory?");
2301 {
2302 auto EC = ExternalFS->makeAbsolute(To);
2303 (void)EC;
2304 assert(!EC && "Could not make absolute path");
2305 }
2306
2307 // Add the file.
2308 auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
2309 llvm::sys::path::filename(From), To,
2310 UseExternalNames ? RedirectingFileSystem::NK_External
2312 ToEntry = NewFile.get();
2314 std::move(NewFile));
2315 }
2316
2317 return FS;
2318}
2319
2322 : E(E) {
2323 assert(E != nullptr);
2324 // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
2325 // path of the directory it maps to in the external file system plus any
2326 // remaining path components in the provided iterator.
2328 SmallString<256> Redirect(DRE->getExternalContentsPath());
2329 sys::path::append(Redirect, Start, End,
2330 getExistingStyle(DRE->getExternalContentsPath()));
2331 ExternalRedirect = std::string(Redirect);
2332 }
2333}
2334
2336 llvm::SmallVectorImpl<char> &Result) const {
2337 Result.clear();
2338 for (Entry *Parent : Parents)
2339 llvm::sys::path::append(Result, Parent->getName());
2340 llvm::sys::path::append(Result, E->getName());
2341}
2342
2343std::error_code RedirectingFileSystem::makeCanonicalForLookup(
2344 SmallVectorImpl<char> &Path) const {
2345 if (std::error_code EC = makeAbsolute(Path))
2346 return EC;
2347
2348 llvm::SmallString<256> CanonicalPath =
2349 canonicalize(StringRef(Path.data(), Path.size()));
2350 if (CanonicalPath.empty())
2352
2353 Path.assign(CanonicalPath.begin(), CanonicalPath.end());
2354 return {};
2355}
2356
2359 llvm::SmallString<128> CanonicalPath(Path);
2360 if (std::error_code EC = makeCanonicalForLookup(CanonicalPath))
2361 return EC;
2362
2363 // RedirectOnly means the VFS is always used.
2364 if (UsageTrackingActive && Redirection == RedirectKind::RedirectOnly)
2365 HasBeenUsed = true;
2366
2367 sys::path::const_iterator Start = sys::path::begin(CanonicalPath);
2368 sys::path::const_iterator End = sys::path::end(CanonicalPath);
2370 for (const auto &Root : Roots) {
2372 lookupPathImpl(Start, End, Root.get(), Entries);
2373 if (UsageTrackingActive && Result && isa<RemapEntry>(Result->E))
2374 HasBeenUsed = true;
2375 if (Result) {
2376 Result->Parents = std::move(Entries);
2377 return Result;
2378 }
2379
2380 if (Result.getError() != llvm::errc::no_such_file_or_directory)
2381 return Result;
2382 }
2384}
2385
2387RedirectingFileSystem::lookupPathImpl(
2390 llvm::SmallVectorImpl<Entry *> &Entries) const {
2391 assert(!isTraversalComponent(*Start) &&
2392 !isTraversalComponent(From->getName()) &&
2393 "Paths should not contain traversal components");
2394
2395 StringRef FromName = From->getName();
2396
2397 // Forward the search to the next component in case this is an empty one.
2398 if (!FromName.empty()) {
2399 if (!pathComponentMatches(*Start, FromName))
2401
2402 ++Start;
2403
2404 if (Start == End) {
2405 // Match!
2406 return LookupResult(From, Start, End);
2407 }
2408 }
2409
2412
2414 return LookupResult(From, Start, End);
2415
2417 for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
2418 llvm::make_range(DE->contents_begin(), DE->contents_end())) {
2419 Entries.push_back(From);
2421 lookupPathImpl(Start, End, DirEntry.get(), Entries);
2422 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
2423 return Result;
2424 Entries.pop_back();
2425 }
2426
2428}
2429
2430static Status getRedirectedFileStatus(const Twine &OriginalPath,
2431 bool UseExternalNames,
2432 Status ExternalStatus) {
2433 // The path has been mapped by some nested VFS and exposes an external path,
2434 // don't override it with the original path.
2435 if (ExternalStatus.ExposesExternalVFSPath)
2436 return ExternalStatus;
2437
2438 Status S = ExternalStatus;
2439 if (!UseExternalNames)
2440 S = Status::copyWithNewName(S, OriginalPath);
2441 else
2442 S.ExposesExternalVFSPath = true;
2443 return S;
2444}
2445
2446ErrorOr<Status> RedirectingFileSystem::status(
2447 const Twine &LookupPath, const Twine &OriginalPath,
2448 const RedirectingFileSystem::LookupResult &Result) {
2449 if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
2450 SmallString<256> RemappedPath((*ExtRedirect).str());
2451 if (std::error_code EC = makeAbsolute(RemappedPath))
2452 return EC;
2453
2454 ErrorOr<Status> S = ExternalFS->status(RemappedPath);
2455 if (!S)
2456 return S;
2457 S = Status::copyWithNewName(*S, *ExtRedirect);
2459 return getRedirectedFileStatus(OriginalPath,
2460 RE->useExternalName(UseExternalNames), *S);
2461 }
2462
2464 return Status::copyWithNewName(DE->getStatus(), LookupPath);
2465}
2466
2467ErrorOr<Status>
2468RedirectingFileSystem::getExternalStatus(const Twine &LookupPath,
2469 const Twine &OriginalPath) const {
2470 auto Result = ExternalFS->status(LookupPath);
2471
2472 // The path has been mapped by some nested VFS, don't override it with the
2473 // original path.
2474 if (!Result || Result->ExposesExternalVFSPath)
2475 return Result;
2476 return Status::copyWithNewName(Result.get(), OriginalPath);
2477}
2478
2479ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {
2480 SmallString<256> Path;
2481 OriginalPath.toVector(Path);
2482
2483 if (std::error_code EC = makeAbsolute(Path))
2484 return EC;
2485
2486 if (Redirection == RedirectKind::Fallback) {
2487 // Attempt to find the original file first, only falling back to the
2488 // mapped file if that fails.
2489 ErrorOr<Status> S = getExternalStatus(Path, OriginalPath);
2490 if (S)
2491 return S;
2492 }
2493
2495 if (!Result) {
2496 // Was not able to map file, fallthrough to using the original path if
2497 // that was the specified redirection type.
2498 if (Redirection == RedirectKind::Fallthrough &&
2499 isFileNotFound(Result.getError()))
2500 return getExternalStatus(Path, OriginalPath);
2501 return Result.getError();
2502 }
2503
2504 ErrorOr<Status> S = status(Path, OriginalPath, *Result);
2505 if (!S && Redirection == RedirectKind::Fallthrough &&
2506 isFileNotFound(S.getError(), Result->E)) {
2507 // Mapped the file but it wasn't found in the underlying filesystem,
2508 // fallthrough to using the original path if that was the specified
2509 // redirection type.
2510 return getExternalStatus(Path, OriginalPath);
2511 }
2512
2513 return S;
2514}
2515
2516bool RedirectingFileSystem::exists(const Twine &OriginalPath) {
2517 SmallString<256> Path;
2518 OriginalPath.toVector(Path);
2519
2520 if (makeAbsolute(Path))
2521 return false;
2522
2523 if (Redirection == RedirectKind::Fallback) {
2524 // Attempt to find the original file first, only falling back to the
2525 // mapped file if that fails.
2526 if (ExternalFS->exists(Path))
2527 return true;
2528 }
2529
2531 if (!Result) {
2532 // Was not able to map file, fallthrough to using the original path if
2533 // that was the specified redirection type.
2534 if (Redirection == RedirectKind::Fallthrough &&
2535 isFileNotFound(Result.getError()))
2536 return ExternalFS->exists(Path);
2537 return false;
2538 }
2539
2540 std::optional<StringRef> ExtRedirect = Result->getExternalRedirect();
2541 if (!ExtRedirect) {
2543 return true;
2544 }
2545
2546 SmallString<256> RemappedPath((*ExtRedirect).str());
2547 if (makeAbsolute(RemappedPath))
2548 return false;
2549
2550 if (ExternalFS->exists(RemappedPath))
2551 return true;
2552
2553 if (Redirection == RedirectKind::Fallthrough) {
2554 // Mapped the file but it wasn't found in the underlying filesystem,
2555 // fallthrough to using the original path if that was the specified
2556 // redirection type.
2557 return ExternalFS->exists(Path);
2558 }
2559
2560 return false;
2561}
2562
2563namespace {
2564
2565/// Provide a file wrapper with an overriden status.
2566class FileWithFixedStatus : public File {
2567 std::unique_ptr<File> InnerFile;
2568 Status S;
2569
2570public:
2571 FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
2572 : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
2573
2574 ErrorOr<Status> status() override { return S; }
2576
2577 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
2578 bool IsVolatile) override {
2579 return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
2580 IsVolatile);
2581 }
2582
2583 std::error_code close() override { return InnerFile->close(); }
2584
2585 void setPath(const Twine &Path) override { S = S.copyWithNewName(S, Path); }
2586};
2587
2588} // namespace
2589
2590ErrorOr<std::unique_ptr<File>>
2591File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
2592 // See \c getRedirectedFileStatus - don't update path if it's exposing an
2593 // external path.
2594 if (!Result || (*Result)->status()->ExposesExternalVFSPath)
2595 return Result;
2596
2597 ErrorOr<std::unique_ptr<File>> F = std::move(*Result);
2598 auto Name = F->get()->getName();
2599 if (Name && Name.get() != P.str())
2600 F->get()->setPath(P);
2601 return F;
2602}
2603
2606 SmallString<256> Path;
2607 OriginalPath.toVector(Path);
2608
2609 if (std::error_code EC = makeAbsolute(Path))
2610 return EC;
2611
2612 if (Redirection == RedirectKind::Fallback) {
2613 // Attempt to find the original file first, only falling back to the
2614 // mapped file if that fails.
2615 auto F = File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2616 if (F)
2617 return F;
2618 }
2619
2621 if (!Result) {
2622 // Was not able to map file, fallthrough to using the original path if
2623 // that was the specified redirection type.
2624 if (Redirection == RedirectKind::Fallthrough &&
2625 isFileNotFound(Result.getError()))
2626 return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2627 return Result.getError();
2628 }
2629
2630 if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
2632
2633 StringRef ExtRedirect = *Result->getExternalRedirect();
2634 SmallString<256> RemappedPath(ExtRedirect.str());
2635 if (std::error_code EC = makeAbsolute(RemappedPath))
2636 return EC;
2637
2638 auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
2639
2640 auto ExternalFile =
2641 File::getWithPath(ExternalFS->openFileForRead(RemappedPath), ExtRedirect);
2642 if (!ExternalFile) {
2643 if (Redirection == RedirectKind::Fallthrough &&
2644 isFileNotFound(ExternalFile.getError(), Result->E)) {
2645 // Mapped the file but it wasn't found in the underlying filesystem,
2646 // fallthrough to using the original path if that was the specified
2647 // redirection type.
2648 return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2649 }
2650 return ExternalFile;
2651 }
2652
2653 auto ExternalStatus = (*ExternalFile)->status();
2654 if (!ExternalStatus)
2655 return ExternalStatus.getError();
2656
2657 // Otherwise, the file was successfully remapped. Mark it as such. Also
2658 // replace the underlying path if the external name is being used.
2660 OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus);
2661 return std::unique_ptr<File>(
2662 std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
2663}
2664
2665std::error_code
2667 SmallVectorImpl<char> &Output) {
2668 SmallString<256> Path;
2669 OriginalPath.toVector(Path);
2670
2671 if (std::error_code EC = makeAbsolute(Path))
2672 return EC;
2673
2674 if (Redirection == RedirectKind::Fallback) {
2675 // Attempt to find the original file first, only falling back to the
2676 // mapped file if that fails.
2677 std::error_code EC = ExternalFS->getRealPath(Path, Output);
2678 if (!EC)
2679 return EC;
2680 }
2681
2683 if (!Result) {
2684 // Was not able to map file, fallthrough to using the original path if
2685 // that was the specified redirection type.
2686 if (Redirection == RedirectKind::Fallthrough &&
2687 isFileNotFound(Result.getError()))
2688 return ExternalFS->getRealPath(Path, Output);
2689 return Result.getError();
2690 }
2691
2692 // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2693 // path in the external file system.
2694 if (auto ExtRedirect = Result->getExternalRedirect()) {
2695 auto P = ExternalFS->getRealPath(*ExtRedirect, Output);
2696 if (P && Redirection == RedirectKind::Fallthrough &&
2697 isFileNotFound(P, Result->E)) {
2698 // Mapped the file but it wasn't found in the underlying filesystem,
2699 // fallthrough to using the original path if that was the specified
2700 // redirection type.
2701 return ExternalFS->getRealPath(Path, Output);
2702 }
2703 return P;
2704 }
2705
2706 // We found a DirectoryEntry, which does not have a single external contents
2707 // path. Use the canonical virtual path.
2708 if (Redirection == RedirectKind::Fallthrough) {
2709 Result->getPath(Output);
2710 return {};
2711 }
2713}
2714
2716 const Twine &Dir, SmallVectorImpl<std::string> &Out) {
2717 SmallString<256> Path;
2718 Dir.toVector(Path);
2719
2720 if (makeAbsolute(Path))
2721 return;
2722
2723 // Fallthrough and Fallback both consult ExternalFS, differing only in order.
2724 const bool ConsultsExternalFS = Redirection != RedirectKind::RedirectOnly;
2725
2727 if (!Result) {
2728 // dir_begin() delegates entirely to ExternalFS.
2729 if (ConsultsExternalFS)
2730 ExternalFS->getDirectoryContentRealSources(Path, Out);
2731 return;
2732 }
2733
2734 switch (Result->E->getKind()) {
2735 case EK_File:
2736 return;
2737 case EK_Directory:
2738 // The names come from the overlay, which has no location in ExternalFS.
2739 break;
2740 case EK_DirectoryRemap: {
2741 // Make the target absolute as status() does, so a relative
2742 // 'external-contents' is not passed down raw.
2743 SmallString<256> RemappedPath(*Result->getExternalRedirect());
2744 if (!makeAbsolute(RemappedPath))
2745 ExternalFS->getDirectoryContentRealSources(RemappedPath, Out);
2746 break;
2747 }
2748 }
2749
2750 // dir_begin() also iterates the original path in ExternalFS.
2751 if (ConsultsExternalFS)
2752 ExternalFS->getDirectoryContentRealSources(Path, Out);
2753}
2754
2755std::unique_ptr<FileSystem>
2756vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
2758 StringRef YAMLFilePath, void *DiagContext,
2759 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2760 return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
2761 YAMLFilePath, DiagContext,
2762 std::move(ExternalFS));
2763}
2764
2768 auto Kind = SrcE->getKind();
2771 assert(DE && "Must be a directory");
2772 for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
2773 llvm::make_range(DE->contents_begin(), DE->contents_end())) {
2774 Path.push_back(SubEntry->getName());
2775 getVFSEntries(SubEntry.get(), Path, Entries);
2776 Path.pop_back();
2777 }
2778 return;
2779 }
2780
2783 assert(DR && "Must be a directory remap");
2784 SmallString<128> VPath;
2785 for (auto &Comp : Path)
2786 llvm::sys::path::append(VPath, Comp);
2787 Entries.push_back(
2788 YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
2789 return;
2790 }
2791
2792 assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
2794 assert(FE && "Must be a file");
2795 SmallString<128> VPath;
2796 for (auto &Comp : Path)
2797 llvm::sys::path::append(VPath, Comp);
2798 Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
2799}
2800
2802 SmallVectorImpl<YAMLVFSEntry> &CollectedEntries) {
2804 if (!RootResult)
2805 return;
2806 SmallVector<StringRef, 8> Components;
2807 Components.push_back("/");
2808 getVFSEntries(RootResult->E, Components, CollectedEntries);
2809}
2810
2812 static std::atomic<unsigned> UID;
2813 unsigned ID = ++UID;
2814 // The following assumes that uint64_t max will never collide with a real
2815 // dev_t value from the OS.
2816 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
2817}
2818
2819void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath,
2820 bool IsDirectory) {
2821 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
2822 assert(sys::path::is_absolute(RealPath) && "real path not absolute");
2823 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
2824 Mappings.emplace_back(VirtualPath, RealPath, IsDirectory);
2825}
2826
2828 addEntry(VirtualPath, RealPath, /*IsDirectory=*/false);
2829}
2830
2832 StringRef RealPath) {
2833 addEntry(VirtualPath, RealPath, /*IsDirectory=*/true);
2834}
2835
2836namespace {
2837
2838class JSONWriter {
2841
2842 unsigned getDirIndent() { return 4 * DirStack.size(); }
2843 unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
2844 bool containedIn(StringRef Parent, StringRef Path);
2845 StringRef containedPart(StringRef Parent, StringRef Path);
2846 void startDirectory(StringRef Path);
2847 void endDirectory();
2848 void writeEntry(StringRef VPath, StringRef RPath);
2849
2850public:
2851 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
2852
2853 void write(ArrayRef<YAMLVFSEntry> Entries,
2854 std::optional<bool> UseExternalNames,
2855 std::optional<bool> IsCaseSensitive,
2856 std::optional<bool> IsOverlayRelative, StringRef OverlayDir);
2857};
2858
2859} // namespace
2860
2861bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
2862 using namespace llvm::sys;
2863
2864 // Compare each path component.
2865 auto IParent = path::begin(Parent), EParent = path::end(Parent);
2866 for (auto IChild = path::begin(Path), EChild = path::end(Path);
2867 IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
2868 if (*IParent != *IChild)
2869 return false;
2870 }
2871 // Have we exhausted the parent path?
2872 return IParent == EParent;
2873}
2874
2875StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
2876 assert(!Parent.empty());
2877 assert(containedIn(Parent, Path));
2878 return Path.substr(Parent.size() + 1);
2879}
2880
2881void JSONWriter::startDirectory(StringRef Path) {
2882 StringRef Name =
2883 DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
2884 DirStack.push_back(Path);
2885 unsigned Indent = getDirIndent();
2886 OS.indent(Indent) << "{\n";
2887 OS.indent(Indent + 2) << "'type': 'directory',\n";
2888 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
2889 OS.indent(Indent + 2) << "'contents': [\n";
2890}
2891
2892void JSONWriter::endDirectory() {
2893 unsigned Indent = getDirIndent();
2894 OS.indent(Indent + 2) << "]\n";
2895 OS.indent(Indent) << "}";
2896
2897 DirStack.pop_back();
2898}
2899
2900void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
2901 unsigned Indent = getFileIndent();
2902 OS.indent(Indent) << "{\n";
2903 OS.indent(Indent + 2) << "'type': 'file',\n";
2904 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
2905 OS.indent(Indent + 2) << "'external-contents': \""
2906 << llvm::yaml::escape(RPath) << "\"\n";
2907 OS.indent(Indent) << "}";
2908}
2909
2910void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
2911 std::optional<bool> UseExternalNames,
2912 std::optional<bool> IsCaseSensitive,
2913 std::optional<bool> IsOverlayRelative,
2914 StringRef OverlayDir) {
2915 using namespace llvm::sys;
2916
2917 OS << "{\n"
2918 " 'version': 0,\n";
2919 if (IsCaseSensitive)
2920 OS << " 'case-sensitive': '" << (*IsCaseSensitive ? "true" : "false")
2921 << "',\n";
2922 if (UseExternalNames)
2923 OS << " 'use-external-names': '" << (*UseExternalNames ? "true" : "false")
2924 << "',\n";
2925 bool UseOverlayRelative = false;
2926 if (IsOverlayRelative) {
2927 UseOverlayRelative = *IsOverlayRelative;
2928 OS << " 'overlay-relative': '" << (UseOverlayRelative ? "true" : "false")
2929 << "',\n";
2930 }
2931 OS << " 'roots': [\n";
2932
2933 if (!Entries.empty()) {
2934 const YAMLVFSEntry &Entry = Entries.front();
2935
2936 startDirectory(
2937 Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath)
2938 );
2939
2940 StringRef RPath = Entry.RPath;
2941 if (UseOverlayRelative) {
2942 assert(RPath.starts_with(OverlayDir) &&
2943 "Overlay dir must be contained in RPath");
2944 RPath = RPath.substr(OverlayDir.size());
2945 }
2946
2947 bool IsCurrentDirEmpty = true;
2948 if (!Entry.IsDirectory) {
2949 writeEntry(path::filename(Entry.VPath), RPath);
2950 IsCurrentDirEmpty = false;
2951 }
2952
2953 for (const auto &Entry : Entries.slice(1)) {
2954 StringRef Dir =
2955 Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath);
2956 if (Dir == DirStack.back()) {
2957 if (!IsCurrentDirEmpty) {
2958 OS << ",\n";
2959 }
2960 } else {
2961 bool IsDirPoppedFromStack = false;
2962 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
2963 OS << "\n";
2964 endDirectory();
2965 IsDirPoppedFromStack = true;
2966 }
2967 if (IsDirPoppedFromStack || !IsCurrentDirEmpty) {
2968 OS << ",\n";
2969 }
2970 startDirectory(Dir);
2971 IsCurrentDirEmpty = true;
2972 }
2973 StringRef RPath = Entry.RPath;
2974 if (UseOverlayRelative) {
2975 assert(RPath.starts_with(OverlayDir) &&
2976 "Overlay dir must be contained in RPath");
2977 RPath = RPath.substr(OverlayDir.size());
2978 }
2979 if (!Entry.IsDirectory) {
2980 writeEntry(path::filename(Entry.VPath), RPath);
2981 IsCurrentDirEmpty = false;
2982 }
2983 }
2984
2985 while (!DirStack.empty()) {
2986 OS << "\n";
2987 endDirectory();
2988 }
2989 OS << "\n";
2990 }
2991
2992 OS << " ]\n"
2993 << "}\n";
2994}
2995
2997 llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
2998 return LHS.VPath < RHS.VPath;
2999 });
3000
3001 JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
3002 IsOverlayRelative, OverlayDir);
3003}
3004
3006 FileSystem &FS_, const Twine &Path, std::error_code &EC)
3007 : FS(&FS_) {
3008 directory_iterator I = FS->dir_begin(Path, EC);
3009 if (I != directory_iterator()) {
3010 State = std::make_shared<detail::RecDirIterState>();
3011 State->Stack.push_back(I);
3012 }
3013}
3014
3017 assert(FS && State && !State->Stack.empty() && "incrementing past end");
3018 assert(!State->Stack.back()->path().empty() && "non-canonical end iterator");
3020
3021 if (State->HasNoPushRequest)
3022 State->HasNoPushRequest = false;
3023 else {
3024 if (State->Stack.back()->type() == sys::fs::file_type::directory_file) {
3026 FS->dir_begin(State->Stack.back()->path(), EC);
3027 if (I != End) {
3028 State->Stack.push_back(I);
3029 return *this;
3030 }
3031 }
3032 }
3033
3034 while (!State->Stack.empty() && State->Stack.back().increment(EC) == End)
3035 State->Stack.pop_back();
3036
3037 if (State->Stack.empty())
3038 State.reset(); // end iterator
3039
3040 return *this;
3041}
3042
3043const char FileSystem::ID = 0;
3044const char OverlayFileSystem::ID = 0;
3045const char ProxyFileSystem::ID = 0;
3046const char InMemoryFileSystem::ID = 0;
3047const char RedirectingFileSystem::ID = 0;
3048
3049unsigned ::llvm::IntrusiveRefCntPtrInfo<FileSystem>::useCount(
3050 const FileSystem *FS) {
3051 return FS->UseCount();
3052}
3053
3054void ::llvm::IntrusiveRefCntPtrInfo<FileSystem>::retain(FileSystem *FS) {
3055 FS->Retain();
3056}
3057
3058void ::llvm::IntrusiveRefCntPtrInfo<FileSystem>::release(FileSystem *FS) {
3059 FS->Release();
3060}
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:767
iterator end()
Definition DenseMap.h:687
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
void getDirectoryContentRealSources(const Twine &Dir, SmallVectorImpl< std::string > &Out) 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
void getDirectoryContentRealSources(const Twine &Dir, SmallVectorImpl< std::string > &Out) override
Collect the paths in the real file system that contribute to a potentially virtual directory when ite...
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:550
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