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