1 //===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements the VirtualFileSystem interface.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/Support/VirtualFileSystem.h"
14 #include "llvm/ADT/ArrayRef.h"
15 #include "llvm/ADT/DenseMap.h"
16 #include "llvm/ADT/IntrusiveRefCntPtr.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/SmallString.h"
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/ADT/StringSet.h"
22 #include "llvm/ADT/Twine.h"
23 #include "llvm/ADT/iterator_range.h"
24 #include "llvm/Config/llvm-config.h"
25 #include "llvm/Support/Casting.h"
26 #include "llvm/Support/Chrono.h"
27 #include "llvm/Support/Compiler.h"
28 #include "llvm/Support/Debug.h"
29 #include "llvm/Support/Errc.h"
30 #include "llvm/Support/ErrorHandling.h"
31 #include "llvm/Support/ErrorOr.h"
32 #include "llvm/Support/FileSystem.h"
33 #include "llvm/Support/FileSystem/UniqueID.h"
34 #include "llvm/Support/MemoryBuffer.h"
35 #include "llvm/Support/Path.h"
36 #include "llvm/Support/SMLoc.h"
37 #include "llvm/Support/SourceMgr.h"
38 #include "llvm/Support/YAMLParser.h"
39 #include "llvm/Support/raw_ostream.h"
50 #include <system_error>
55 using namespace llvm::vfs
;
57 using llvm::sys::fs::file_t
;
58 using llvm::sys::fs::file_status
;
59 using llvm::sys::fs::file_type
;
60 using llvm::sys::fs::kInvalidFile
;
61 using llvm::sys::fs::perms
;
62 using llvm::sys::fs::UniqueID
;
64 Status::Status(const file_status
&Status
)
65 : UID(Status
.getUniqueID()), MTime(Status
.getLastModificationTime()),
66 User(Status
.getUser()), Group(Status
.getGroup()), Size(Status
.getSize()),
67 Type(Status
.type()), Perms(Status
.permissions()) {}
69 Status::Status(const Twine
&Name
, UniqueID UID
, sys::TimePoint
<> MTime
,
70 uint32_t User
, uint32_t Group
, uint64_t Size
, file_type Type
,
72 : Name(Name
.str()), UID(UID
), MTime(MTime
), User(User
), Group(Group
),
73 Size(Size
), Type(Type
), Perms(Perms
) {}
75 Status
Status::copyWithNewSize(const Status
&In
, uint64_t NewSize
) {
76 return Status(In
.getName(), In
.getUniqueID(), In
.getLastModificationTime(),
77 In
.getUser(), In
.getGroup(), NewSize
, In
.getType(),
81 Status
Status::copyWithNewName(const Status
&In
, const Twine
&NewName
) {
82 return Status(NewName
, In
.getUniqueID(), In
.getLastModificationTime(),
83 In
.getUser(), In
.getGroup(), In
.getSize(), In
.getType(),
87 Status
Status::copyWithNewName(const file_status
&In
, const Twine
&NewName
) {
88 return Status(NewName
, In
.getUniqueID(), In
.getLastModificationTime(),
89 In
.getUser(), In
.getGroup(), In
.getSize(), In
.type(),
93 bool Status::equivalent(const Status
&Other
) const {
94 assert(isStatusKnown() && Other
.isStatusKnown());
95 return getUniqueID() == Other
.getUniqueID();
98 bool Status::isDirectory() const { return Type
== file_type::directory_file
; }
100 bool Status::isRegularFile() const { return Type
== file_type::regular_file
; }
102 bool Status::isOther() const {
103 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
106 bool Status::isSymlink() const { return Type
== file_type::symlink_file
; }
108 bool Status::isStatusKnown() const { return Type
!= file_type::status_error
; }
110 bool Status::exists() const {
111 return isStatusKnown() && Type
!= file_type::file_not_found
;
114 File::~File() = default;
116 FileSystem::~FileSystem() = default;
118 ErrorOr
<std::unique_ptr
<MemoryBuffer
>>
119 FileSystem::getBufferForFile(const llvm::Twine
&Name
, int64_t FileSize
,
120 bool RequiresNullTerminator
, bool IsVolatile
) {
121 auto F
= openFileForRead(Name
);
125 return (*F
)->getBuffer(Name
, FileSize
, RequiresNullTerminator
, IsVolatile
);
128 std::error_code
FileSystem::makeAbsolute(SmallVectorImpl
<char> &Path
) const {
129 if (llvm::sys::path::is_absolute(Path
))
132 auto WorkingDir
= getCurrentWorkingDirectory();
134 return WorkingDir
.getError();
136 llvm::sys::fs::make_absolute(WorkingDir
.get(), Path
);
140 std::error_code
FileSystem::getRealPath(const Twine
&Path
,
141 SmallVectorImpl
<char> &Output
) const {
142 return errc::operation_not_permitted
;
145 std::error_code
FileSystem::isLocal(const Twine
&Path
, bool &Result
) {
146 return errc::operation_not_permitted
;
149 bool FileSystem::exists(const Twine
&Path
) {
150 auto Status
= status(Path
);
151 return Status
&& Status
->exists();
154 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
155 void FileSystem::dump() const { print(dbgs(), PrintType::RecursiveContents
); }
159 static bool isTraversalComponent(StringRef Component
) {
160 return Component
.equals("..") || Component
.equals(".");
163 static bool pathHasTraversal(StringRef Path
) {
164 using namespace llvm::sys
;
166 for (StringRef Comp
: llvm::make_range(path::begin(Path
), path::end(Path
)))
167 if (isTraversalComponent(Comp
))
173 //===-----------------------------------------------------------------------===/
174 // RealFileSystem implementation
175 //===-----------------------------------------------------------------------===/
179 /// Wrapper around a raw file descriptor.
180 class RealFile
: public File
{
181 friend class RealFileSystem
;
185 std::string RealName
;
187 RealFile(file_t RawFD
, StringRef NewName
, StringRef NewRealPathName
)
188 : FD(RawFD
), S(NewName
, {}, {}, {}, {}, {},
189 llvm::sys::fs::file_type::status_error
, {}),
190 RealName(NewRealPathName
.str()) {
191 assert(FD
!= kInvalidFile
&& "Invalid or inactive file descriptor");
195 ~RealFile() override
;
197 ErrorOr
<Status
> status() override
;
198 ErrorOr
<std::string
> getName() override
;
199 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> getBuffer(const Twine
&Name
,
201 bool RequiresNullTerminator
,
202 bool IsVolatile
) override
;
203 std::error_code
close() override
;
204 void setPath(const Twine
&Path
) override
;
209 RealFile::~RealFile() { close(); }
211 ErrorOr
<Status
> RealFile::status() {
212 assert(FD
!= kInvalidFile
&& "cannot stat closed file");
213 if (!S
.isStatusKnown()) {
214 file_status RealStatus
;
215 if (std::error_code EC
= sys::fs::status(FD
, RealStatus
))
217 S
= Status::copyWithNewName(RealStatus
, S
.getName());
222 ErrorOr
<std::string
> RealFile::getName() {
223 return RealName
.empty() ? S
.getName().str() : RealName
;
226 ErrorOr
<std::unique_ptr
<MemoryBuffer
>>
227 RealFile::getBuffer(const Twine
&Name
, int64_t FileSize
,
228 bool RequiresNullTerminator
, bool IsVolatile
) {
229 assert(FD
!= kInvalidFile
&& "cannot get buffer for closed file");
230 return MemoryBuffer::getOpenFile(FD
, Name
, FileSize
, RequiresNullTerminator
,
234 std::error_code
RealFile::close() {
235 std::error_code EC
= sys::fs::closeFile(FD
);
240 void RealFile::setPath(const Twine
&Path
) {
241 RealName
= Path
.str();
242 if (auto Status
= status())
243 S
= Status
.get().copyWithNewName(Status
.get(), Path
);
248 /// A file system according to your operating system.
249 /// This may be linked to the process's working directory, or maintain its own.
251 /// Currently, its own working directory is emulated by storing the path and
252 /// sending absolute paths to llvm::sys::fs:: functions.
253 /// A more principled approach would be to push this down a level, modelling
254 /// the working dir as an llvm::sys::fs::WorkingDir or similar.
255 /// This would enable the use of openat()-style functions on some platforms.
256 class RealFileSystem
: public FileSystem
{
258 explicit RealFileSystem(bool LinkCWDToProcess
) {
259 if (!LinkCWDToProcess
) {
260 SmallString
<128> PWD
, RealPWD
;
261 if (std::error_code EC
= llvm::sys::fs::current_path(PWD
))
263 else if (llvm::sys::fs::real_path(PWD
, RealPWD
))
264 WD
= WorkingDirectory
{PWD
, PWD
};
266 WD
= WorkingDirectory
{PWD
, RealPWD
};
270 ErrorOr
<Status
> status(const Twine
&Path
) override
;
271 ErrorOr
<std::unique_ptr
<File
>> openFileForRead(const Twine
&Path
) override
;
272 directory_iterator
dir_begin(const Twine
&Dir
, std::error_code
&EC
) override
;
274 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
;
275 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
;
276 std::error_code
isLocal(const Twine
&Path
, bool &Result
) override
;
277 std::error_code
getRealPath(const Twine
&Path
,
278 SmallVectorImpl
<char> &Output
) const override
;
281 void printImpl(raw_ostream
&OS
, PrintType Type
,
282 unsigned IndentLevel
) const override
;
285 // If this FS has its own working dir, use it to make Path absolute.
286 // The returned twine is safe to use as long as both Storage and Path live.
287 Twine
adjustPath(const Twine
&Path
, SmallVectorImpl
<char> &Storage
) const {
290 Path
.toVector(Storage
);
291 sys::fs::make_absolute(WD
->get().Resolved
, Storage
);
295 struct WorkingDirectory
{
296 // The current working directory, without symlinks resolved. (echo $PWD).
297 SmallString
<128> Specified
;
298 // The current working directory, with links resolved. (readlink .).
299 SmallString
<128> Resolved
;
301 std::optional
<llvm::ErrorOr
<WorkingDirectory
>> WD
;
306 ErrorOr
<Status
> RealFileSystem::status(const Twine
&Path
) {
307 SmallString
<256> Storage
;
308 sys::fs::file_status RealStatus
;
309 if (std::error_code EC
=
310 sys::fs::status(adjustPath(Path
, Storage
), RealStatus
))
312 return Status::copyWithNewName(RealStatus
, Path
);
315 ErrorOr
<std::unique_ptr
<File
>>
316 RealFileSystem::openFileForRead(const Twine
&Name
) {
317 SmallString
<256> RealName
, Storage
;
318 Expected
<file_t
> FDOrErr
= sys::fs::openNativeFileForRead(
319 adjustPath(Name
, Storage
), sys::fs::OF_None
, &RealName
);
321 return errorToErrorCode(FDOrErr
.takeError());
322 return std::unique_ptr
<File
>(
323 new RealFile(*FDOrErr
, Name
.str(), RealName
.str()));
326 llvm::ErrorOr
<std::string
> RealFileSystem::getCurrentWorkingDirectory() const {
328 return std::string(WD
->get().Specified
);
330 return WD
->getError();
332 SmallString
<128> Dir
;
333 if (std::error_code EC
= llvm::sys::fs::current_path(Dir
))
335 return std::string(Dir
);
338 std::error_code
RealFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
340 return llvm::sys::fs::set_current_path(Path
);
342 SmallString
<128> Absolute
, Resolved
, Storage
;
343 adjustPath(Path
, Storage
).toVector(Absolute
);
345 if (auto Err
= llvm::sys::fs::is_directory(Absolute
, IsDir
))
348 return std::make_error_code(std::errc::not_a_directory
);
349 if (auto Err
= llvm::sys::fs::real_path(Absolute
, Resolved
))
351 WD
= WorkingDirectory
{Absolute
, Resolved
};
352 return std::error_code();
355 std::error_code
RealFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
356 SmallString
<256> Storage
;
357 return llvm::sys::fs::is_local(adjustPath(Path
, Storage
), Result
);
361 RealFileSystem::getRealPath(const Twine
&Path
,
362 SmallVectorImpl
<char> &Output
) const {
363 SmallString
<256> Storage
;
364 return llvm::sys::fs::real_path(adjustPath(Path
, Storage
), Output
);
367 void RealFileSystem::printImpl(raw_ostream
&OS
, PrintType Type
,
368 unsigned IndentLevel
) const {
369 printIndent(OS
, IndentLevel
);
370 OS
<< "RealFileSystem using ";
378 IntrusiveRefCntPtr
<FileSystem
> vfs::getRealFileSystem() {
379 static IntrusiveRefCntPtr
<FileSystem
> FS(new RealFileSystem(true));
383 std::unique_ptr
<FileSystem
> vfs::createPhysicalFileSystem() {
384 return std::make_unique
<RealFileSystem
>(false);
389 class RealFSDirIter
: public llvm::vfs::detail::DirIterImpl
{
390 llvm::sys::fs::directory_iterator Iter
;
393 RealFSDirIter(const Twine
&Path
, std::error_code
&EC
) : Iter(Path
, EC
) {
394 if (Iter
!= llvm::sys::fs::directory_iterator())
395 CurrentEntry
= directory_entry(Iter
->path(), Iter
->type());
398 std::error_code
increment() override
{
401 CurrentEntry
= (Iter
== llvm::sys::fs::directory_iterator())
403 : directory_entry(Iter
->path(), Iter
->type());
410 directory_iterator
RealFileSystem::dir_begin(const Twine
&Dir
,
411 std::error_code
&EC
) {
412 SmallString
<128> Storage
;
413 return directory_iterator(
414 std::make_shared
<RealFSDirIter
>(adjustPath(Dir
, Storage
), EC
));
417 //===-----------------------------------------------------------------------===/
418 // OverlayFileSystem implementation
419 //===-----------------------------------------------------------------------===/
421 OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr
<FileSystem
> BaseFS
) {
422 FSList
.push_back(std::move(BaseFS
));
425 void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr
<FileSystem
> FS
) {
426 FSList
.push_back(FS
);
427 // Synchronize added file systems by duplicating the working directory from
428 // the first one in the list.
429 FS
->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
432 ErrorOr
<Status
> OverlayFileSystem::status(const Twine
&Path
) {
433 // FIXME: handle symlinks that cross file systems
434 for (iterator I
= overlays_begin(), E
= overlays_end(); I
!= E
; ++I
) {
435 ErrorOr
<Status
> Status
= (*I
)->status(Path
);
436 if (Status
|| Status
.getError() != llvm::errc::no_such_file_or_directory
)
439 return make_error_code(llvm::errc::no_such_file_or_directory
);
442 ErrorOr
<std::unique_ptr
<File
>>
443 OverlayFileSystem::openFileForRead(const llvm::Twine
&Path
) {
444 // FIXME: handle symlinks that cross file systems
445 for (iterator I
= overlays_begin(), E
= overlays_end(); I
!= E
; ++I
) {
446 auto Result
= (*I
)->openFileForRead(Path
);
447 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
450 return make_error_code(llvm::errc::no_such_file_or_directory
);
453 llvm::ErrorOr
<std::string
>
454 OverlayFileSystem::getCurrentWorkingDirectory() const {
455 // All file systems are synchronized, just take the first working directory.
456 return FSList
.front()->getCurrentWorkingDirectory();
460 OverlayFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
461 for (auto &FS
: FSList
)
462 if (std::error_code EC
= FS
->setCurrentWorkingDirectory(Path
))
467 std::error_code
OverlayFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
468 for (auto &FS
: FSList
)
469 if (FS
->exists(Path
))
470 return FS
->isLocal(Path
, Result
);
471 return errc::no_such_file_or_directory
;
475 OverlayFileSystem::getRealPath(const Twine
&Path
,
476 SmallVectorImpl
<char> &Output
) const {
477 for (const auto &FS
: FSList
)
478 if (FS
->exists(Path
))
479 return FS
->getRealPath(Path
, Output
);
480 return errc::no_such_file_or_directory
;
483 void OverlayFileSystem::printImpl(raw_ostream
&OS
, PrintType Type
,
484 unsigned IndentLevel
) const {
485 printIndent(OS
, IndentLevel
);
486 OS
<< "OverlayFileSystem\n";
487 if (Type
== PrintType::Summary
)
490 if (Type
== PrintType::Contents
)
491 Type
= PrintType::Summary
;
492 for (const auto &FS
: overlays_range())
493 FS
->print(OS
, Type
, IndentLevel
+ 1);
496 llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;
500 /// Combines and deduplicates directory entries across multiple file systems.
501 class CombiningDirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
502 using FileSystemPtr
= llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
>;
504 /// Iterators to combine, processed in reverse order.
505 SmallVector
<directory_iterator
, 8> IterList
;
506 /// The iterator currently being traversed.
507 directory_iterator CurrentDirIter
;
508 /// The set of names already returned as entries.
509 llvm::StringSet
<> SeenNames
;
511 /// Sets \c CurrentDirIter to the next iterator in the list, or leaves it as
512 /// is (at its end position) if we've already gone through them all.
513 std::error_code
incrementIter(bool IsFirstTime
) {
514 while (!IterList
.empty()) {
515 CurrentDirIter
= IterList
.back();
517 if (CurrentDirIter
!= directory_iterator())
521 if (IsFirstTime
&& CurrentDirIter
== directory_iterator())
522 return errc::no_such_file_or_directory
;
526 std::error_code
incrementDirIter(bool IsFirstTime
) {
527 assert((IsFirstTime
|| CurrentDirIter
!= directory_iterator()) &&
528 "incrementing past end");
531 CurrentDirIter
.increment(EC
);
532 if (!EC
&& CurrentDirIter
== directory_iterator())
533 EC
= incrementIter(IsFirstTime
);
537 std::error_code
incrementImpl(bool IsFirstTime
) {
539 std::error_code EC
= incrementDirIter(IsFirstTime
);
540 if (EC
|| CurrentDirIter
== directory_iterator()) {
541 CurrentEntry
= directory_entry();
544 CurrentEntry
= *CurrentDirIter
;
545 StringRef Name
= llvm::sys::path::filename(CurrentEntry
.path());
546 if (SeenNames
.insert(Name
).second
)
547 return EC
; // name not seen before
549 llvm_unreachable("returned above");
553 CombiningDirIterImpl(ArrayRef
<FileSystemPtr
> FileSystems
, std::string Dir
,
554 std::error_code
&EC
) {
555 for (const auto &FS
: FileSystems
) {
557 directory_iterator Iter
= FS
->dir_begin(Dir
, FEC
);
558 if (FEC
&& FEC
!= errc::no_such_file_or_directory
) {
563 IterList
.push_back(Iter
);
565 EC
= incrementImpl(true);
568 CombiningDirIterImpl(ArrayRef
<directory_iterator
> DirIters
,
570 : IterList(DirIters
.begin(), DirIters
.end()) {
571 EC
= incrementImpl(true);
574 std::error_code
increment() override
{ return incrementImpl(false); }
579 directory_iterator
OverlayFileSystem::dir_begin(const Twine
&Dir
,
580 std::error_code
&EC
) {
581 directory_iterator Combined
= directory_iterator(
582 std::make_shared
<CombiningDirIterImpl
>(FSList
, Dir
.str(), EC
));
588 void ProxyFileSystem::anchor() {}
595 enum InMemoryNodeKind
{
602 /// The in memory file system is a tree of Nodes. Every node can either be a
603 /// file, symlink, hardlink or a directory.
605 InMemoryNodeKind Kind
;
606 std::string FileName
;
609 InMemoryNode(llvm::StringRef FileName
, InMemoryNodeKind Kind
)
610 : Kind(Kind
), FileName(std::string(llvm::sys::path::filename(FileName
))) {
612 virtual ~InMemoryNode() = default;
614 /// Return the \p Status for this node. \p RequestedName should be the name
615 /// through which the caller referred to this node. It will override
616 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
617 virtual Status
getStatus(const Twine
&RequestedName
) const = 0;
619 /// Get the filename of this node (the name without the directory part).
620 StringRef
getFileName() const { return FileName
; }
621 InMemoryNodeKind
getKind() const { return Kind
; }
622 virtual std::string
toString(unsigned Indent
) const = 0;
625 class InMemoryFile
: public InMemoryNode
{
627 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
;
630 InMemoryFile(Status Stat
, std::unique_ptr
<llvm::MemoryBuffer
> Buffer
)
631 : InMemoryNode(Stat
.getName(), IME_File
), Stat(std::move(Stat
)),
632 Buffer(std::move(Buffer
)) {}
634 Status
getStatus(const Twine
&RequestedName
) const override
{
635 return Status::copyWithNewName(Stat
, RequestedName
);
637 llvm::MemoryBuffer
*getBuffer() const { return Buffer
.get(); }
639 std::string
toString(unsigned Indent
) const override
{
640 return (std::string(Indent
, ' ') + Stat
.getName() + "\n").str();
643 static bool classof(const InMemoryNode
*N
) {
644 return N
->getKind() == IME_File
;
650 class InMemoryHardLink
: public InMemoryNode
{
651 const InMemoryFile
&ResolvedFile
;
654 InMemoryHardLink(StringRef Path
, const InMemoryFile
&ResolvedFile
)
655 : InMemoryNode(Path
, IME_HardLink
), ResolvedFile(ResolvedFile
) {}
656 const InMemoryFile
&getResolvedFile() const { return ResolvedFile
; }
658 Status
getStatus(const Twine
&RequestedName
) const override
{
659 return ResolvedFile
.getStatus(RequestedName
);
662 std::string
toString(unsigned Indent
) const override
{
663 return std::string(Indent
, ' ') + "HardLink to -> " +
664 ResolvedFile
.toString(0);
667 static bool classof(const InMemoryNode
*N
) {
668 return N
->getKind() == IME_HardLink
;
672 class InMemorySymbolicLink
: public InMemoryNode
{
673 std::string TargetPath
;
677 InMemorySymbolicLink(StringRef Path
, StringRef TargetPath
, Status Stat
)
678 : InMemoryNode(Path
, IME_SymbolicLink
), TargetPath(std::move(TargetPath
)),
681 std::string
toString(unsigned Indent
) const override
{
682 return std::string(Indent
, ' ') + "SymbolicLink to -> " + TargetPath
;
685 Status
getStatus(const Twine
&RequestedName
) const override
{
686 return Status::copyWithNewName(Stat
, RequestedName
);
689 StringRef
getTargetPath() const { return TargetPath
; }
691 static bool classof(const InMemoryNode
*N
) {
692 return N
->getKind() == IME_SymbolicLink
;
696 /// Adapt a InMemoryFile for VFS' File interface. The goal is to make
697 /// \p InMemoryFileAdaptor mimic as much as possible the behavior of
699 class InMemoryFileAdaptor
: public File
{
700 const InMemoryFile
&Node
;
701 /// The name to use when returning a Status for this file.
702 std::string RequestedName
;
705 explicit InMemoryFileAdaptor(const InMemoryFile
&Node
,
706 std::string RequestedName
)
707 : Node(Node
), RequestedName(std::move(RequestedName
)) {}
709 llvm::ErrorOr
<Status
> status() override
{
710 return Node
.getStatus(RequestedName
);
713 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
714 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
715 bool IsVolatile
) override
{
716 llvm::MemoryBuffer
*Buf
= Node
.getBuffer();
717 return llvm::MemoryBuffer::getMemBuffer(
718 Buf
->getBuffer(), Buf
->getBufferIdentifier(), RequiresNullTerminator
);
721 std::error_code
close() override
{ return {}; }
723 void setPath(const Twine
&Path
) override
{ RequestedName
= Path
.str(); }
727 class InMemoryDirectory
: public InMemoryNode
{
729 std::map
<std::string
, std::unique_ptr
<InMemoryNode
>> Entries
;
732 InMemoryDirectory(Status Stat
)
733 : InMemoryNode(Stat
.getName(), IME_Directory
), Stat(std::move(Stat
)) {}
735 /// Return the \p Status for this node. \p RequestedName should be the name
736 /// through which the caller referred to this node. It will override
737 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
738 Status
getStatus(const Twine
&RequestedName
) const override
{
739 return Status::copyWithNewName(Stat
, RequestedName
);
742 UniqueID
getUniqueID() const { return Stat
.getUniqueID(); }
744 InMemoryNode
*getChild(StringRef Name
) const {
745 auto I
= Entries
.find(Name
.str());
746 if (I
!= Entries
.end())
747 return I
->second
.get();
751 InMemoryNode
*addChild(StringRef Name
, std::unique_ptr
<InMemoryNode
> Child
) {
752 return Entries
.emplace(Name
, std::move(Child
)).first
->second
.get();
755 using const_iterator
= decltype(Entries
)::const_iterator
;
757 const_iterator
begin() const { return Entries
.begin(); }
758 const_iterator
end() const { return Entries
.end(); }
760 std::string
toString(unsigned Indent
) const override
{
762 (std::string(Indent
, ' ') + Stat
.getName() + "\n").str();
763 for (const auto &Entry
: Entries
)
764 Result
+= Entry
.second
->toString(Indent
+ 2);
768 static bool classof(const InMemoryNode
*N
) {
769 return N
->getKind() == IME_Directory
;
773 } // namespace detail
775 // The UniqueID of in-memory files is derived from path and content.
776 // This avoids difficulties in creating exactly equivalent in-memory FSes,
777 // as often needed in multithreaded programs.
778 static sys::fs::UniqueID
getUniqueID(hash_code Hash
) {
779 return sys::fs::UniqueID(std::numeric_limits
<uint64_t>::max(),
780 uint64_t(size_t(Hash
)));
782 static sys::fs::UniqueID
getFileID(sys::fs::UniqueID Parent
,
783 llvm::StringRef Name
,
784 llvm::StringRef Contents
) {
785 return getUniqueID(llvm::hash_combine(Parent
.getFile(), Name
, Contents
));
787 static sys::fs::UniqueID
getDirectoryID(sys::fs::UniqueID Parent
,
788 llvm::StringRef Name
) {
789 return getUniqueID(llvm::hash_combine(Parent
.getFile(), Name
));
792 Status
detail::NewInMemoryNodeInfo::makeStatus() const {
794 (Type
== sys::fs::file_type::directory_file
)
795 ? getDirectoryID(DirUID
, Name
)
796 : getFileID(DirUID
, Name
, Buffer
? Buffer
->getBuffer() : "");
798 return Status(Path
, UID
, llvm::sys::toTimePoint(ModificationTime
), User
,
799 Group
, Buffer
? Buffer
->getBufferSize() : 0, Type
, Perms
);
802 InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths
)
803 : Root(new detail::InMemoryDirectory(
804 Status("", getDirectoryID(llvm::sys::fs::UniqueID(), ""),
805 llvm::sys::TimePoint
<>(), 0, 0, 0,
806 llvm::sys::fs::file_type::directory_file
,
807 llvm::sys::fs::perms::all_all
))),
808 UseNormalizedPaths(UseNormalizedPaths
) {}
810 InMemoryFileSystem::~InMemoryFileSystem() = default;
812 std::string
InMemoryFileSystem::toString() const {
813 return Root
->toString(/*Indent=*/0);
816 bool InMemoryFileSystem::addFile(const Twine
&P
, time_t ModificationTime
,
817 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
,
818 std::optional
<uint32_t> User
,
819 std::optional
<uint32_t> Group
,
820 std::optional
<llvm::sys::fs::file_type
> Type
,
821 std::optional
<llvm::sys::fs::perms
> Perms
,
822 MakeNodeFn MakeNode
) {
823 SmallString
<128> Path
;
826 // Fix up relative paths. This just prepends the current working directory.
827 std::error_code EC
= makeAbsolute(Path
);
831 if (useNormalizedPaths())
832 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
837 detail::InMemoryDirectory
*Dir
= Root
.get();
838 auto I
= llvm::sys::path::begin(Path
), E
= sys::path::end(Path
);
839 const auto ResolvedUser
= User
.value_or(0);
840 const auto ResolvedGroup
= Group
.value_or(0);
841 const auto ResolvedType
= Type
.value_or(sys::fs::file_type::regular_file
);
842 const auto ResolvedPerms
= Perms
.value_or(sys::fs::all_all
);
843 // Any intermediate directories we create should be accessible by
844 // the owner, even if Perms says otherwise for the final path.
845 const auto NewDirectoryPerms
= ResolvedPerms
| sys::fs::owner_all
;
848 detail::InMemoryNode
*Node
= Dir
->getChild(Name
);
854 Name
, MakeNode({Dir
->getUniqueID(), Path
, Name
, ModificationTime
,
855 std::move(Buffer
), ResolvedUser
, ResolvedGroup
,
856 ResolvedType
, ResolvedPerms
}));
860 // Create a new directory. Use the path up to here.
862 StringRef(Path
.str().begin(), Name
.end() - Path
.str().begin()),
863 getDirectoryID(Dir
->getUniqueID(), Name
),
864 llvm::sys::toTimePoint(ModificationTime
), ResolvedUser
, ResolvedGroup
,
865 0, sys::fs::file_type::directory_file
, NewDirectoryPerms
);
866 Dir
= cast
<detail::InMemoryDirectory
>(Dir
->addChild(
867 Name
, std::make_unique
<detail::InMemoryDirectory
>(std::move(Stat
))));
871 if (auto *NewDir
= dyn_cast
<detail::InMemoryDirectory
>(Node
)) {
874 assert((isa
<detail::InMemoryFile
>(Node
) ||
875 isa
<detail::InMemoryHardLink
>(Node
)) &&
876 "Must be either file, hardlink or directory!");
878 // Trying to insert a directory in place of a file.
882 // Return false only if the new file is different from the existing one.
883 if (auto Link
= dyn_cast
<detail::InMemoryHardLink
>(Node
)) {
884 return Link
->getResolvedFile().getBuffer()->getBuffer() ==
887 return cast
<detail::InMemoryFile
>(Node
)->getBuffer()->getBuffer() ==
893 bool InMemoryFileSystem::addFile(const Twine
&P
, time_t ModificationTime
,
894 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
,
895 std::optional
<uint32_t> User
,
896 std::optional
<uint32_t> Group
,
897 std::optional
<llvm::sys::fs::file_type
> Type
,
898 std::optional
<llvm::sys::fs::perms
> Perms
) {
899 return addFile(P
, ModificationTime
, std::move(Buffer
), User
, Group
, Type
,
901 [](detail::NewInMemoryNodeInfo NNI
)
902 -> std::unique_ptr
<detail::InMemoryNode
> {
903 Status Stat
= NNI
.makeStatus();
904 if (Stat
.getType() == sys::fs::file_type::directory_file
)
905 return std::make_unique
<detail::InMemoryDirectory
>(Stat
);
906 return std::make_unique
<detail::InMemoryFile
>(
907 Stat
, std::move(NNI
.Buffer
));
911 bool InMemoryFileSystem::addFileNoOwn(
912 const Twine
&P
, time_t ModificationTime
,
913 const llvm::MemoryBufferRef
&Buffer
, std::optional
<uint32_t> User
,
914 std::optional
<uint32_t> Group
, std::optional
<llvm::sys::fs::file_type
> Type
,
915 std::optional
<llvm::sys::fs::perms
> Perms
) {
916 return addFile(P
, ModificationTime
, llvm::MemoryBuffer::getMemBuffer(Buffer
),
917 std::move(User
), std::move(Group
), std::move(Type
),
919 [](detail::NewInMemoryNodeInfo NNI
)
920 -> std::unique_ptr
<detail::InMemoryNode
> {
921 Status Stat
= NNI
.makeStatus();
922 if (Stat
.getType() == sys::fs::file_type::directory_file
)
923 return std::make_unique
<detail::InMemoryDirectory
>(Stat
);
924 return std::make_unique
<detail::InMemoryFile
>(
925 Stat
, std::move(NNI
.Buffer
));
929 detail::NamedNodeOrError
930 InMemoryFileSystem::lookupNode(const Twine
&P
, bool FollowFinalSymlink
,
931 size_t SymlinkDepth
) const {
932 SmallString
<128> Path
;
935 // Fix up relative paths. This just prepends the current working directory.
936 std::error_code EC
= makeAbsolute(Path
);
940 if (useNormalizedPaths())
941 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
943 const detail::InMemoryDirectory
*Dir
= Root
.get();
945 return detail::NamedNodeOrError(Path
, Dir
);
947 auto I
= llvm::sys::path::begin(Path
), E
= llvm::sys::path::end(Path
);
949 detail::InMemoryNode
*Node
= Dir
->getChild(*I
);
952 return errc::no_such_file_or_directory
;
954 if (auto Symlink
= dyn_cast
<detail::InMemorySymbolicLink
>(Node
)) {
955 // If we're at the end of the path, and we're not following through
956 // terminal symlinks, then we're done.
957 if (I
== E
&& !FollowFinalSymlink
)
958 return detail::NamedNodeOrError(Path
, Symlink
);
960 if (SymlinkDepth
> InMemoryFileSystem::MaxSymlinkDepth
)
961 return errc::no_such_file_or_directory
;
963 SmallString
<128> TargetPath
= Symlink
->getTargetPath();
964 if (std::error_code EC
= makeAbsolute(TargetPath
))
967 // Keep going with the target. We always want to follow symlinks here
968 // because we're either at the end of a path that we want to follow, or
969 // not at the end of a path, in which case we need to follow the symlink
972 lookupNode(TargetPath
, /*FollowFinalSymlink=*/true, SymlinkDepth
+ 1);
973 if (!Target
|| I
== E
)
976 if (!isa
<detail::InMemoryDirectory
>(*Target
))
977 return errc::no_such_file_or_directory
;
979 // Otherwise, continue on the search in the symlinked directory.
980 Dir
= cast
<detail::InMemoryDirectory
>(*Target
);
984 // Return the file if it's at the end of the path.
985 if (auto File
= dyn_cast
<detail::InMemoryFile
>(Node
)) {
987 return detail::NamedNodeOrError(Path
, File
);
988 return errc::no_such_file_or_directory
;
991 // If Node is HardLink then return the resolved file.
992 if (auto File
= dyn_cast
<detail::InMemoryHardLink
>(Node
)) {
994 return detail::NamedNodeOrError(Path
, &File
->getResolvedFile());
995 return errc::no_such_file_or_directory
;
997 // Traverse directories.
998 Dir
= cast
<detail::InMemoryDirectory
>(Node
);
1000 return detail::NamedNodeOrError(Path
, Dir
);
1004 bool InMemoryFileSystem::addHardLink(const Twine
&NewLink
,
1005 const Twine
&Target
) {
1006 auto NewLinkNode
= lookupNode(NewLink
, /*FollowFinalSymlink=*/false);
1007 // Whether symlinks in the hardlink target are followed is
1008 // implementation-defined in POSIX.
1009 // We're following symlinks here to be consistent with macOS.
1010 auto TargetNode
= lookupNode(Target
, /*FollowFinalSymlink=*/true);
1011 // FromPath must not have been added before. ToPath must have been added
1012 // before. Resolved ToPath must be a File.
1013 if (!TargetNode
|| NewLinkNode
|| !isa
<detail::InMemoryFile
>(*TargetNode
))
1015 return addFile(NewLink
, 0, nullptr, std::nullopt
, std::nullopt
, std::nullopt
,
1016 std::nullopt
, [&](detail::NewInMemoryNodeInfo NNI
) {
1017 return std::make_unique
<detail::InMemoryHardLink
>(
1019 *cast
<detail::InMemoryFile
>(*TargetNode
));
1023 bool InMemoryFileSystem::addSymbolicLink(
1024 const Twine
&NewLink
, const Twine
&Target
, time_t ModificationTime
,
1025 std::optional
<uint32_t> User
, std::optional
<uint32_t> Group
,
1026 std::optional
<llvm::sys::fs::perms
> Perms
) {
1027 auto NewLinkNode
= lookupNode(NewLink
, /*FollowFinalSymlink=*/false);
1031 SmallString
<128> NewLinkStr
, TargetStr
;
1032 NewLink
.toVector(NewLinkStr
);
1033 Target
.toVector(TargetStr
);
1035 return addFile(NewLinkStr
, ModificationTime
, nullptr, User
, Group
,
1036 sys::fs::file_type::symlink_file
, Perms
,
1037 [&](detail::NewInMemoryNodeInfo NNI
) {
1038 return std::make_unique
<detail::InMemorySymbolicLink
>(
1039 NewLinkStr
, TargetStr
, NNI
.makeStatus());
1043 llvm::ErrorOr
<Status
> InMemoryFileSystem::status(const Twine
&Path
) {
1044 auto Node
= lookupNode(Path
, /*FollowFinalSymlink=*/true);
1046 return (*Node
)->getStatus(Path
);
1047 return Node
.getError();
1050 llvm::ErrorOr
<std::unique_ptr
<File
>>
1051 InMemoryFileSystem::openFileForRead(const Twine
&Path
) {
1052 auto Node
= lookupNode(Path
,/*FollowFinalSymlink=*/true);
1054 return Node
.getError();
1056 // When we have a file provide a heap-allocated wrapper for the memory buffer
1057 // to match the ownership semantics for File.
1058 if (auto *F
= dyn_cast
<detail::InMemoryFile
>(*Node
))
1059 return std::unique_ptr
<File
>(
1060 new detail::InMemoryFileAdaptor(*F
, Path
.str()));
1062 // FIXME: errc::not_a_file?
1063 return make_error_code(llvm::errc::invalid_argument
);
1066 /// Adaptor from InMemoryDir::iterator to directory_iterator.
1067 class InMemoryFileSystem::DirIterator
: public llvm::vfs::detail::DirIterImpl
{
1068 const InMemoryFileSystem
*FS
;
1069 detail::InMemoryDirectory::const_iterator I
;
1070 detail::InMemoryDirectory::const_iterator E
;
1071 std::string RequestedDirName
;
1073 void setCurrentEntry() {
1075 SmallString
<256> Path(RequestedDirName
);
1076 llvm::sys::path::append(Path
, I
->second
->getFileName());
1077 sys::fs::file_type Type
= sys::fs::file_type::type_unknown
;
1078 switch (I
->second
->getKind()) {
1079 case detail::IME_File
:
1080 case detail::IME_HardLink
:
1081 Type
= sys::fs::file_type::regular_file
;
1083 case detail::IME_Directory
:
1084 Type
= sys::fs::file_type::directory_file
;
1086 case detail::IME_SymbolicLink
:
1087 if (auto SymlinkTarget
=
1088 FS
->lookupNode(Path
, /*FollowFinalSymlink=*/true)) {
1089 Path
= SymlinkTarget
.getName();
1090 Type
= (*SymlinkTarget
)->getStatus(Path
).getType();
1094 CurrentEntry
= directory_entry(std::string(Path
), Type
);
1096 // When we're at the end, make CurrentEntry invalid and DirIterImpl will
1098 CurrentEntry
= directory_entry();
1103 DirIterator() = default;
1105 DirIterator(const InMemoryFileSystem
*FS
,
1106 const detail::InMemoryDirectory
&Dir
,
1107 std::string RequestedDirName
)
1108 : FS(FS
), I(Dir
.begin()), E(Dir
.end()),
1109 RequestedDirName(std::move(RequestedDirName
)) {
1113 std::error_code
increment() override
{
1120 directory_iterator
InMemoryFileSystem::dir_begin(const Twine
&Dir
,
1121 std::error_code
&EC
) {
1122 auto Node
= lookupNode(Dir
, /*FollowFinalSymlink=*/true);
1124 EC
= Node
.getError();
1125 return directory_iterator(std::make_shared
<DirIterator
>());
1128 if (auto *DirNode
= dyn_cast
<detail::InMemoryDirectory
>(*Node
))
1129 return directory_iterator(
1130 std::make_shared
<DirIterator
>(this, *DirNode
, Dir
.str()));
1132 EC
= make_error_code(llvm::errc::not_a_directory
);
1133 return directory_iterator(std::make_shared
<DirIterator
>());
1136 std::error_code
InMemoryFileSystem::setCurrentWorkingDirectory(const Twine
&P
) {
1137 SmallString
<128> Path
;
1140 // Fix up relative paths. This just prepends the current working directory.
1141 std::error_code EC
= makeAbsolute(Path
);
1145 if (useNormalizedPaths())
1146 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
1149 WorkingDirectory
= std::string(Path
);
1154 InMemoryFileSystem::getRealPath(const Twine
&Path
,
1155 SmallVectorImpl
<char> &Output
) const {
1156 auto CWD
= getCurrentWorkingDirectory();
1157 if (!CWD
|| CWD
->empty())
1158 return errc::operation_not_permitted
;
1159 Path
.toVector(Output
);
1160 if (auto EC
= makeAbsolute(Output
))
1162 llvm::sys::path::remove_dots(Output
, /*remove_dot_dot=*/true);
1166 std::error_code
InMemoryFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
1171 void InMemoryFileSystem::printImpl(raw_ostream
&OS
, PrintType PrintContents
,
1172 unsigned IndentLevel
) const {
1173 printIndent(OS
, IndentLevel
);
1174 OS
<< "InMemoryFileSystem\n";
1180 //===-----------------------------------------------------------------------===/
1181 // RedirectingFileSystem implementation
1182 //===-----------------------------------------------------------------------===/
1186 static llvm::sys::path::Style
getExistingStyle(llvm::StringRef Path
) {
1187 // Detect the path style in use by checking the first separator.
1188 llvm::sys::path::Style style
= llvm::sys::path::Style::native
;
1189 const size_t n
= Path
.find_first_of("/\\");
1190 // Can't distinguish between posix and windows_slash here.
1191 if (n
!= static_cast<size_t>(-1))
1192 style
= (Path
[n
] == '/') ? llvm::sys::path::Style::posix
1193 : llvm::sys::path::Style::windows_backslash
;
1197 /// Removes leading "./" as well as path components like ".." and ".".
1198 static llvm::SmallString
<256> canonicalize(llvm::StringRef Path
) {
1199 // First detect the path style in use by checking the first separator.
1200 llvm::sys::path::Style style
= getExistingStyle(Path
);
1202 // Now remove the dots. Explicitly specifying the path style prevents the
1203 // direction of the slashes from changing.
1204 llvm::SmallString
<256> result
=
1205 llvm::sys::path::remove_leading_dotslash(Path
, style
);
1206 llvm::sys::path::remove_dots(result
, /*remove_dot_dot=*/true, style
);
1210 /// Whether the error and entry specify a file/directory that was not found.
1211 static bool isFileNotFound(std::error_code EC
,
1212 RedirectingFileSystem::Entry
*E
= nullptr) {
1213 if (E
&& !isa
<RedirectingFileSystem::DirectoryRemapEntry
>(E
))
1215 return EC
== llvm::errc::no_such_file_or_directory
;
1218 } // anonymous namespace
1221 RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr
<FileSystem
> FS
)
1222 : ExternalFS(std::move(FS
)) {
1224 if (auto ExternalWorkingDirectory
=
1225 ExternalFS
->getCurrentWorkingDirectory()) {
1226 WorkingDirectory
= *ExternalWorkingDirectory
;
1230 /// Directory iterator implementation for \c RedirectingFileSystem's
1231 /// directory entries.
1232 class llvm::vfs::RedirectingFSDirIterImpl
1233 : public llvm::vfs::detail::DirIterImpl
{
1235 RedirectingFileSystem::DirectoryEntry::iterator Current
, End
;
1237 std::error_code
incrementImpl(bool IsFirstTime
) {
1238 assert((IsFirstTime
|| Current
!= End
) && "cannot iterate past end");
1241 if (Current
!= End
) {
1242 SmallString
<128> PathStr(Dir
);
1243 llvm::sys::path::append(PathStr
, (*Current
)->getName());
1244 sys::fs::file_type Type
= sys::fs::file_type::type_unknown
;
1245 switch ((*Current
)->getKind()) {
1246 case RedirectingFileSystem::EK_Directory
:
1248 case RedirectingFileSystem::EK_DirectoryRemap
:
1249 Type
= sys::fs::file_type::directory_file
;
1251 case RedirectingFileSystem::EK_File
:
1252 Type
= sys::fs::file_type::regular_file
;
1255 CurrentEntry
= directory_entry(std::string(PathStr
), Type
);
1257 CurrentEntry
= directory_entry();
1263 RedirectingFSDirIterImpl(
1264 const Twine
&Path
, RedirectingFileSystem::DirectoryEntry::iterator Begin
,
1265 RedirectingFileSystem::DirectoryEntry::iterator End
, std::error_code
&EC
)
1266 : Dir(Path
.str()), Current(Begin
), End(End
) {
1267 EC
= incrementImpl(/*IsFirstTime=*/true);
1270 std::error_code
increment() override
{
1271 return incrementImpl(/*IsFirstTime=*/false);
1276 /// Directory iterator implementation for \c RedirectingFileSystem's
1277 /// directory remap entries that maps the paths reported by the external
1278 /// file system's directory iterator back to the virtual directory's path.
1279 class RedirectingFSDirRemapIterImpl
: public llvm::vfs::detail::DirIterImpl
{
1281 llvm::sys::path::Style DirStyle
;
1282 llvm::vfs::directory_iterator ExternalIter
;
1285 RedirectingFSDirRemapIterImpl(std::string DirPath
,
1286 llvm::vfs::directory_iterator ExtIter
)
1287 : Dir(std::move(DirPath
)), DirStyle(getExistingStyle(Dir
)),
1288 ExternalIter(ExtIter
) {
1289 if (ExternalIter
!= llvm::vfs::directory_iterator())
1293 void setCurrentEntry() {
1294 StringRef ExternalPath
= ExternalIter
->path();
1295 llvm::sys::path::Style ExternalStyle
= getExistingStyle(ExternalPath
);
1296 StringRef File
= llvm::sys::path::filename(ExternalPath
, ExternalStyle
);
1298 SmallString
<128> NewPath(Dir
);
1299 llvm::sys::path::append(NewPath
, DirStyle
, File
);
1301 CurrentEntry
= directory_entry(std::string(NewPath
), ExternalIter
->type());
1304 std::error_code
increment() override
{
1306 ExternalIter
.increment(EC
);
1307 if (!EC
&& ExternalIter
!= llvm::vfs::directory_iterator())
1310 CurrentEntry
= directory_entry();
1316 llvm::ErrorOr
<std::string
>
1317 RedirectingFileSystem::getCurrentWorkingDirectory() const {
1318 return WorkingDirectory
;
1322 RedirectingFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
1323 // Don't change the working directory if the path doesn't exist.
1325 return errc::no_such_file_or_directory
;
1327 SmallString
<128> AbsolutePath
;
1328 Path
.toVector(AbsolutePath
);
1329 if (std::error_code EC
= makeAbsolute(AbsolutePath
))
1331 WorkingDirectory
= std::string(AbsolutePath
);
1335 std::error_code
RedirectingFileSystem::isLocal(const Twine
&Path_
,
1337 SmallString
<256> Path
;
1338 Path_
.toVector(Path
);
1340 if (makeCanonical(Path
))
1343 return ExternalFS
->isLocal(Path
, Result
);
1346 std::error_code
RedirectingFileSystem::makeAbsolute(SmallVectorImpl
<char> &Path
) const {
1347 // is_absolute(..., Style::windows_*) accepts paths with both slash types.
1348 if (llvm::sys::path::is_absolute(Path
, llvm::sys::path::Style::posix
) ||
1349 llvm::sys::path::is_absolute(Path
,
1350 llvm::sys::path::Style::windows_backslash
))
1351 // This covers windows absolute path with forward slash as well, as the
1352 // forward slashes are treated as path seperation in llvm::path
1353 // regardless of what path::Style is used.
1356 auto WorkingDir
= getCurrentWorkingDirectory();
1358 return WorkingDir
.getError();
1360 return makeAbsolute(WorkingDir
.get(), Path
);
1364 RedirectingFileSystem::makeAbsolute(StringRef WorkingDir
,
1365 SmallVectorImpl
<char> &Path
) const {
1366 // We can't use sys::fs::make_absolute because that assumes the path style
1367 // is native and there is no way to override that. Since we know WorkingDir
1368 // is absolute, we can use it to determine which style we actually have and
1369 // append Path ourselves.
1370 if (!WorkingDir
.empty() &&
1371 !sys::path::is_absolute(WorkingDir
, sys::path::Style::posix
) &&
1372 !sys::path::is_absolute(WorkingDir
,
1373 sys::path::Style::windows_backslash
)) {
1374 return std::error_code();
1376 sys::path::Style style
= sys::path::Style::windows_backslash
;
1377 if (sys::path::is_absolute(WorkingDir
, sys::path::Style::posix
)) {
1378 style
= sys::path::Style::posix
;
1380 // Distinguish between windows_backslash and windows_slash; getExistingStyle
1381 // returns posix for a path with windows_slash.
1382 if (getExistingStyle(WorkingDir
) != sys::path::Style::windows_backslash
)
1383 style
= sys::path::Style::windows_slash
;
1386 std::string Result
= std::string(WorkingDir
);
1387 StringRef
Dir(Result
);
1388 if (!Dir
.ends_with(sys::path::get_separator(style
))) {
1389 Result
+= sys::path::get_separator(style
);
1391 // backslashes '\' are legit path charactors under POSIX. Windows APIs
1392 // like CreateFile accepts forward slashes '/' as path
1393 // separator (even when mixed with backslashes). Therefore,
1394 // `Path` should be directly appended to `WorkingDir` without converting
1396 Result
.append(Path
.data(), Path
.size());
1397 Path
.assign(Result
.begin(), Result
.end());
1402 directory_iterator
RedirectingFileSystem::dir_begin(const Twine
&Dir
,
1403 std::error_code
&EC
) {
1404 SmallString
<256> Path
;
1407 EC
= makeCanonical(Path
);
1411 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
= lookupPath(Path
);
1413 if (Redirection
!= RedirectKind::RedirectOnly
&&
1414 isFileNotFound(Result
.getError()))
1415 return ExternalFS
->dir_begin(Path
, EC
);
1417 EC
= Result
.getError();
1421 // Use status to make sure the path exists and refers to a directory.
1422 ErrorOr
<Status
> S
= status(Path
, Dir
, *Result
);
1424 if (Redirection
!= RedirectKind::RedirectOnly
&&
1425 isFileNotFound(S
.getError(), Result
->E
))
1426 return ExternalFS
->dir_begin(Dir
, EC
);
1432 if (!S
->isDirectory()) {
1433 EC
= errc::not_a_directory
;
1437 // Create the appropriate directory iterator based on whether we found a
1438 // DirectoryRemapEntry or DirectoryEntry.
1439 directory_iterator RedirectIter
;
1440 std::error_code RedirectEC
;
1441 if (auto ExtRedirect
= Result
->getExternalRedirect()) {
1442 auto RE
= cast
<RedirectingFileSystem::RemapEntry
>(Result
->E
);
1443 RedirectIter
= ExternalFS
->dir_begin(*ExtRedirect
, RedirectEC
);
1445 if (!RE
->useExternalName(UseExternalNames
)) {
1446 // Update the paths in the results to use the virtual directory's path.
1448 directory_iterator(std::make_shared
<RedirectingFSDirRemapIterImpl
>(
1449 std::string(Path
), RedirectIter
));
1452 auto DE
= cast
<DirectoryEntry
>(Result
->E
);
1454 directory_iterator(std::make_shared
<RedirectingFSDirIterImpl
>(
1455 Path
, DE
->contents_begin(), DE
->contents_end(), RedirectEC
));
1459 if (RedirectEC
!= errc::no_such_file_or_directory
) {
1466 if (Redirection
== RedirectKind::RedirectOnly
) {
1468 return RedirectIter
;
1471 std::error_code ExternalEC
;
1472 directory_iterator ExternalIter
= ExternalFS
->dir_begin(Path
, ExternalEC
);
1474 if (ExternalEC
!= errc::no_such_file_or_directory
) {
1481 SmallVector
<directory_iterator
, 2> Iters
;
1482 switch (Redirection
) {
1483 case RedirectKind::Fallthrough
:
1484 Iters
.push_back(ExternalIter
);
1485 Iters
.push_back(RedirectIter
);
1487 case RedirectKind::Fallback
:
1488 Iters
.push_back(RedirectIter
);
1489 Iters
.push_back(ExternalIter
);
1492 llvm_unreachable("unhandled RedirectKind");
1495 directory_iterator Combined
{
1496 std::make_shared
<CombiningDirIterImpl
>(Iters
, EC
)};
1502 void RedirectingFileSystem::setOverlayFileDir(StringRef Dir
) {
1503 OverlayFileDir
= Dir
.str();
1506 StringRef
RedirectingFileSystem::getOverlayFileDir() const {
1507 return OverlayFileDir
;
1510 void RedirectingFileSystem::setFallthrough(bool Fallthrough
) {
1512 Redirection
= RedirectingFileSystem::RedirectKind::Fallthrough
;
1514 Redirection
= RedirectingFileSystem::RedirectKind::RedirectOnly
;
1518 void RedirectingFileSystem::setRedirection(
1519 RedirectingFileSystem::RedirectKind Kind
) {
1523 std::vector
<StringRef
> RedirectingFileSystem::getRoots() const {
1524 std::vector
<StringRef
> R
;
1525 R
.reserve(Roots
.size());
1526 for (const auto &Root
: Roots
)
1527 R
.push_back(Root
->getName());
1531 void RedirectingFileSystem::printImpl(raw_ostream
&OS
, PrintType Type
,
1532 unsigned IndentLevel
) const {
1533 printIndent(OS
, IndentLevel
);
1534 OS
<< "RedirectingFileSystem (UseExternalNames: "
1535 << (UseExternalNames
? "true" : "false") << ")\n";
1536 if (Type
== PrintType::Summary
)
1539 for (const auto &Root
: Roots
)
1540 printEntry(OS
, Root
.get(), IndentLevel
);
1542 printIndent(OS
, IndentLevel
);
1543 OS
<< "ExternalFS:\n";
1544 ExternalFS
->print(OS
, Type
== PrintType::Contents
? PrintType::Summary
: Type
,
1548 void RedirectingFileSystem::printEntry(raw_ostream
&OS
,
1549 RedirectingFileSystem::Entry
*E
,
1550 unsigned IndentLevel
) const {
1551 printIndent(OS
, IndentLevel
);
1552 OS
<< "'" << E
->getName() << "'";
1554 switch (E
->getKind()) {
1555 case EK_Directory
: {
1556 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(E
);
1559 for (std::unique_ptr
<Entry
> &SubEntry
:
1560 llvm::make_range(DE
->contents_begin(), DE
->contents_end()))
1561 printEntry(OS
, SubEntry
.get(), IndentLevel
+ 1);
1564 case EK_DirectoryRemap
:
1566 auto *RE
= cast
<RedirectingFileSystem::RemapEntry
>(E
);
1567 OS
<< " -> '" << RE
->getExternalContentsPath() << "'";
1568 switch (RE
->getUseName()) {
1572 OS
<< " (UseExternalName: true)";
1575 OS
<< " (UseExternalName: false)";
1584 /// A helper class to hold the common YAML parsing state.
1585 class llvm::vfs::RedirectingFileSystemParser
{
1586 yaml::Stream
&Stream
;
1588 void error(yaml::Node
*N
, const Twine
&Msg
) { Stream
.printError(N
, Msg
); }
1591 bool parseScalarString(yaml::Node
*N
, StringRef
&Result
,
1592 SmallVectorImpl
<char> &Storage
) {
1593 const auto *S
= dyn_cast
<yaml::ScalarNode
>(N
);
1596 error(N
, "expected string");
1599 Result
= S
->getValue(Storage
);
1604 bool parseScalarBool(yaml::Node
*N
, bool &Result
) {
1605 SmallString
<5> Storage
;
1607 if (!parseScalarString(N
, Value
, Storage
))
1610 if (Value
.equals_insensitive("true") || Value
.equals_insensitive("on") ||
1611 Value
.equals_insensitive("yes") || Value
== "1") {
1614 } else if (Value
.equals_insensitive("false") ||
1615 Value
.equals_insensitive("off") ||
1616 Value
.equals_insensitive("no") || Value
== "0") {
1621 error(N
, "expected boolean value");
1625 std::optional
<RedirectingFileSystem::RedirectKind
>
1626 parseRedirectKind(yaml::Node
*N
) {
1627 SmallString
<12> Storage
;
1629 if (!parseScalarString(N
, Value
, Storage
))
1630 return std::nullopt
;
1632 if (Value
.equals_insensitive("fallthrough")) {
1633 return RedirectingFileSystem::RedirectKind::Fallthrough
;
1634 } else if (Value
.equals_insensitive("fallback")) {
1635 return RedirectingFileSystem::RedirectKind::Fallback
;
1636 } else if (Value
.equals_insensitive("redirect-only")) {
1637 return RedirectingFileSystem::RedirectKind::RedirectOnly
;
1639 return std::nullopt
;
1642 std::optional
<RedirectingFileSystem::RootRelativeKind
>
1643 parseRootRelativeKind(yaml::Node
*N
) {
1644 SmallString
<12> Storage
;
1646 if (!parseScalarString(N
, Value
, Storage
))
1647 return std::nullopt
;
1648 if (Value
.equals_insensitive("cwd")) {
1649 return RedirectingFileSystem::RootRelativeKind::CWD
;
1650 } else if (Value
.equals_insensitive("overlay-dir")) {
1651 return RedirectingFileSystem::RootRelativeKind::OverlayDir
;
1653 return std::nullopt
;
1660 KeyStatus(bool Required
= false) : Required(Required
) {}
1663 using KeyStatusPair
= std::pair
<StringRef
, KeyStatus
>;
1666 bool checkDuplicateOrUnknownKey(yaml::Node
*KeyNode
, StringRef Key
,
1667 DenseMap
<StringRef
, KeyStatus
> &Keys
) {
1668 if (!Keys
.count(Key
)) {
1669 error(KeyNode
, "unknown key");
1672 KeyStatus
&S
= Keys
[Key
];
1674 error(KeyNode
, Twine("duplicate key '") + Key
+ "'");
1682 bool checkMissingKeys(yaml::Node
*Obj
, DenseMap
<StringRef
, KeyStatus
> &Keys
) {
1683 for (const auto &I
: Keys
) {
1684 if (I
.second
.Required
&& !I
.second
.Seen
) {
1685 error(Obj
, Twine("missing key '") + I
.first
+ "'");
1693 static RedirectingFileSystem::Entry
*
1694 lookupOrCreateEntry(RedirectingFileSystem
*FS
, StringRef Name
,
1695 RedirectingFileSystem::Entry
*ParentEntry
= nullptr) {
1696 if (!ParentEntry
) { // Look for a existent root
1697 for (const auto &Root
: FS
->Roots
) {
1698 if (Name
.equals(Root
->getName())) {
1699 ParentEntry
= Root
.get();
1703 } else { // Advance to the next component
1704 auto *DE
= dyn_cast
<RedirectingFileSystem::DirectoryEntry
>(ParentEntry
);
1705 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &Content
:
1706 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
1708 dyn_cast
<RedirectingFileSystem::DirectoryEntry
>(Content
.get());
1709 if (DirContent
&& Name
.equals(Content
->getName()))
1714 // ... or create a new one
1715 std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
1716 std::make_unique
<RedirectingFileSystem::DirectoryEntry
>(
1717 Name
, Status("", getNextVirtualUniqueID(),
1718 std::chrono::system_clock::now(), 0, 0, 0,
1719 file_type::directory_file
, sys::fs::all_all
));
1721 if (!ParentEntry
) { // Add a new root to the overlay
1722 FS
->Roots
.push_back(std::move(E
));
1723 ParentEntry
= FS
->Roots
.back().get();
1727 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(ParentEntry
);
1728 DE
->addContent(std::move(E
));
1729 return DE
->getLastContent();
1733 void uniqueOverlayTree(RedirectingFileSystem
*FS
,
1734 RedirectingFileSystem::Entry
*SrcE
,
1735 RedirectingFileSystem::Entry
*NewParentE
= nullptr) {
1736 StringRef Name
= SrcE
->getName();
1737 switch (SrcE
->getKind()) {
1738 case RedirectingFileSystem::EK_Directory
: {
1739 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(SrcE
);
1740 // Empty directories could be present in the YAML as a way to
1741 // describe a file for a current directory after some of its subdir
1742 // is parsed. This only leads to redundant walks, ignore it.
1744 NewParentE
= lookupOrCreateEntry(FS
, Name
, NewParentE
);
1745 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &SubEntry
:
1746 llvm::make_range(DE
->contents_begin(), DE
->contents_end()))
1747 uniqueOverlayTree(FS
, SubEntry
.get(), NewParentE
);
1750 case RedirectingFileSystem::EK_DirectoryRemap
: {
1751 assert(NewParentE
&& "Parent entry must exist");
1752 auto *DR
= cast
<RedirectingFileSystem::DirectoryRemapEntry
>(SrcE
);
1753 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(NewParentE
);
1755 std::make_unique
<RedirectingFileSystem::DirectoryRemapEntry
>(
1756 Name
, DR
->getExternalContentsPath(), DR
->getUseName()));
1759 case RedirectingFileSystem::EK_File
: {
1760 assert(NewParentE
&& "Parent entry must exist");
1761 auto *FE
= cast
<RedirectingFileSystem::FileEntry
>(SrcE
);
1762 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(NewParentE
);
1763 DE
->addContent(std::make_unique
<RedirectingFileSystem::FileEntry
>(
1764 Name
, FE
->getExternalContentsPath(), FE
->getUseName()));
1770 std::unique_ptr
<RedirectingFileSystem::Entry
>
1771 parseEntry(yaml::Node
*N
, RedirectingFileSystem
*FS
, bool IsRootEntry
) {
1772 auto *M
= dyn_cast
<yaml::MappingNode
>(N
);
1774 error(N
, "expected mapping node for file or directory entry");
1778 KeyStatusPair Fields
[] = {
1779 KeyStatusPair("name", true),
1780 KeyStatusPair("type", true),
1781 KeyStatusPair("contents", false),
1782 KeyStatusPair("external-contents", false),
1783 KeyStatusPair("use-external-name", false),
1786 DenseMap
<StringRef
, KeyStatus
> Keys(std::begin(Fields
), std::end(Fields
));
1788 enum { CF_NotSet
, CF_List
, CF_External
} ContentsField
= CF_NotSet
;
1789 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>>
1791 SmallString
<256> ExternalContentsPath
;
1792 SmallString
<256> Name
;
1793 yaml::Node
*NameValueNode
= nullptr;
1794 auto UseExternalName
= RedirectingFileSystem::NK_NotSet
;
1795 RedirectingFileSystem::EntryKind Kind
;
1797 for (auto &I
: *M
) {
1799 // Reuse the buffer for key and value, since we don't look at key after
1801 SmallString
<256> Buffer
;
1802 if (!parseScalarString(I
.getKey(), Key
, Buffer
))
1805 if (!checkDuplicateOrUnknownKey(I
.getKey(), Key
, Keys
))
1809 if (Key
== "name") {
1810 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1813 NameValueNode
= I
.getValue();
1814 // Guarantee that old YAML files containing paths with ".." and "."
1815 // are properly canonicalized before read into the VFS.
1816 Name
= canonicalize(Value
).str();
1817 } else if (Key
== "type") {
1818 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1820 if (Value
== "file")
1821 Kind
= RedirectingFileSystem::EK_File
;
1822 else if (Value
== "directory")
1823 Kind
= RedirectingFileSystem::EK_Directory
;
1824 else if (Value
== "directory-remap")
1825 Kind
= RedirectingFileSystem::EK_DirectoryRemap
;
1827 error(I
.getValue(), "unknown value for 'type'");
1830 } else if (Key
== "contents") {
1831 if (ContentsField
!= CF_NotSet
) {
1833 "entry already has 'contents' or 'external-contents'");
1836 ContentsField
= CF_List
;
1837 auto *Contents
= dyn_cast
<yaml::SequenceNode
>(I
.getValue());
1839 // FIXME: this is only for directories, what about files?
1840 error(I
.getValue(), "expected array");
1844 for (auto &I
: *Contents
) {
1845 if (std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
1846 parseEntry(&I
, FS
, /*IsRootEntry*/ false))
1847 EntryArrayContents
.push_back(std::move(E
));
1851 } else if (Key
== "external-contents") {
1852 if (ContentsField
!= CF_NotSet
) {
1854 "entry already has 'contents' or 'external-contents'");
1857 ContentsField
= CF_External
;
1858 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1861 SmallString
<256> FullPath
;
1862 if (FS
->IsRelativeOverlay
) {
1863 FullPath
= FS
->getOverlayFileDir();
1864 assert(!FullPath
.empty() &&
1865 "External contents prefix directory must exist");
1866 llvm::sys::path::append(FullPath
, Value
);
1871 // Guarantee that old YAML files containing paths with ".." and "."
1872 // are properly canonicalized before read into the VFS.
1873 FullPath
= canonicalize(FullPath
);
1874 ExternalContentsPath
= FullPath
.str();
1875 } else if (Key
== "use-external-name") {
1877 if (!parseScalarBool(I
.getValue(), Val
))
1879 UseExternalName
= Val
? RedirectingFileSystem::NK_External
1880 : RedirectingFileSystem::NK_Virtual
;
1882 llvm_unreachable("key missing from Keys");
1886 if (Stream
.failed())
1889 // check for missing keys
1890 if (ContentsField
== CF_NotSet
) {
1891 error(N
, "missing key 'contents' or 'external-contents'");
1894 if (!checkMissingKeys(N
, Keys
))
1897 // check invalid configuration
1898 if (Kind
== RedirectingFileSystem::EK_Directory
&&
1899 UseExternalName
!= RedirectingFileSystem::NK_NotSet
) {
1900 error(N
, "'use-external-name' is not supported for 'directory' entries");
1904 if (Kind
== RedirectingFileSystem::EK_DirectoryRemap
&&
1905 ContentsField
== CF_List
) {
1906 error(N
, "'contents' is not supported for 'directory-remap' entries");
1910 sys::path::Style path_style
= sys::path::Style::native
;
1912 // VFS root entries may be in either Posix or Windows style. Figure out
1913 // which style we have, and use it consistently.
1914 if (sys::path::is_absolute(Name
, sys::path::Style::posix
)) {
1915 path_style
= sys::path::Style::posix
;
1916 } else if (sys::path::is_absolute(Name
,
1917 sys::path::Style::windows_backslash
)) {
1918 path_style
= sys::path::Style::windows_backslash
;
1920 // Relative VFS root entries are made absolute to either the overlay
1921 // directory, or the current working directory, then we can determine
1922 // the path style from that.
1924 if (FS
->RootRelative
==
1925 RedirectingFileSystem::RootRelativeKind::OverlayDir
) {
1926 StringRef FullPath
= FS
->getOverlayFileDir();
1927 assert(!FullPath
.empty() && "Overlay file directory must exist");
1928 EC
= FS
->makeAbsolute(FullPath
, Name
);
1929 Name
= canonicalize(Name
);
1931 EC
= sys::fs::make_absolute(Name
);
1934 assert(NameValueNode
&& "Name presence should be checked earlier");
1937 "entry with relative path at the root level is not discoverable");
1940 path_style
= sys::path::is_absolute(Name
, sys::path::Style::posix
)
1941 ? sys::path::Style::posix
1942 : sys::path::Style::windows_backslash
;
1944 // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will
1945 // return true even if `Name` is using forward slashes. Distinguish
1946 // between windows_backslash and windows_slash.
1947 if (path_style
== sys::path::Style::windows_backslash
&&
1948 getExistingStyle(Name
) != sys::path::Style::windows_backslash
)
1949 path_style
= sys::path::Style::windows_slash
;
1952 // Remove trailing slash(es), being careful not to remove the root path
1953 StringRef Trimmed
= Name
;
1954 size_t RootPathLen
= sys::path::root_path(Trimmed
, path_style
).size();
1955 while (Trimmed
.size() > RootPathLen
&&
1956 sys::path::is_separator(Trimmed
.back(), path_style
))
1957 Trimmed
= Trimmed
.slice(0, Trimmed
.size() - 1);
1959 // Get the last component
1960 StringRef LastComponent
= sys::path::filename(Trimmed
, path_style
);
1962 std::unique_ptr
<RedirectingFileSystem::Entry
> Result
;
1964 case RedirectingFileSystem::EK_File
:
1965 Result
= std::make_unique
<RedirectingFileSystem::FileEntry
>(
1966 LastComponent
, std::move(ExternalContentsPath
), UseExternalName
);
1968 case RedirectingFileSystem::EK_DirectoryRemap
:
1969 Result
= std::make_unique
<RedirectingFileSystem::DirectoryRemapEntry
>(
1970 LastComponent
, std::move(ExternalContentsPath
), UseExternalName
);
1972 case RedirectingFileSystem::EK_Directory
:
1973 Result
= std::make_unique
<RedirectingFileSystem::DirectoryEntry
>(
1974 LastComponent
, std::move(EntryArrayContents
),
1975 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1976 0, 0, 0, file_type::directory_file
, sys::fs::all_all
));
1980 StringRef Parent
= sys::path::parent_path(Trimmed
, path_style
);
1984 // if 'name' contains multiple components, create implicit directory entries
1985 for (sys::path::reverse_iterator I
= sys::path::rbegin(Parent
, path_style
),
1986 E
= sys::path::rend(Parent
);
1988 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>> Entries
;
1989 Entries
.push_back(std::move(Result
));
1990 Result
= std::make_unique
<RedirectingFileSystem::DirectoryEntry
>(
1991 *I
, std::move(Entries
),
1992 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1993 0, 0, 0, file_type::directory_file
, sys::fs::all_all
));
1999 RedirectingFileSystemParser(yaml::Stream
&S
) : Stream(S
) {}
2002 bool parse(yaml::Node
*Root
, RedirectingFileSystem
*FS
) {
2003 auto *Top
= dyn_cast
<yaml::MappingNode
>(Root
);
2005 error(Root
, "expected mapping node");
2009 KeyStatusPair Fields
[] = {
2010 KeyStatusPair("version", true),
2011 KeyStatusPair("case-sensitive", false),
2012 KeyStatusPair("use-external-names", false),
2013 KeyStatusPair("root-relative", false),
2014 KeyStatusPair("overlay-relative", false),
2015 KeyStatusPair("fallthrough", false),
2016 KeyStatusPair("redirecting-with", false),
2017 KeyStatusPair("roots", true),
2020 DenseMap
<StringRef
, KeyStatus
> Keys(std::begin(Fields
), std::end(Fields
));
2021 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>> RootEntries
;
2023 // Parse configuration and 'roots'
2024 for (auto &I
: *Top
) {
2025 SmallString
<10> KeyBuffer
;
2027 if (!parseScalarString(I
.getKey(), Key
, KeyBuffer
))
2030 if (!checkDuplicateOrUnknownKey(I
.getKey(), Key
, Keys
))
2033 if (Key
== "roots") {
2034 auto *Roots
= dyn_cast
<yaml::SequenceNode
>(I
.getValue());
2036 error(I
.getValue(), "expected array");
2040 for (auto &I
: *Roots
) {
2041 if (std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
2042 parseEntry(&I
, FS
, /*IsRootEntry*/ true))
2043 RootEntries
.push_back(std::move(E
));
2047 } else if (Key
== "version") {
2048 StringRef VersionString
;
2049 SmallString
<4> Storage
;
2050 if (!parseScalarString(I
.getValue(), VersionString
, Storage
))
2053 if (VersionString
.getAsInteger
<int>(10, Version
)) {
2054 error(I
.getValue(), "expected integer");
2058 error(I
.getValue(), "invalid version number");
2062 error(I
.getValue(), "version mismatch, expected 0");
2065 } else if (Key
== "case-sensitive") {
2066 if (!parseScalarBool(I
.getValue(), FS
->CaseSensitive
))
2068 } else if (Key
== "overlay-relative") {
2069 if (!parseScalarBool(I
.getValue(), FS
->IsRelativeOverlay
))
2071 } else if (Key
== "use-external-names") {
2072 if (!parseScalarBool(I
.getValue(), FS
->UseExternalNames
))
2074 } else if (Key
== "fallthrough") {
2075 if (Keys
["redirecting-with"].Seen
) {
2077 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2081 bool ShouldFallthrough
= false;
2082 if (!parseScalarBool(I
.getValue(), ShouldFallthrough
))
2085 if (ShouldFallthrough
) {
2086 FS
->Redirection
= RedirectingFileSystem::RedirectKind::Fallthrough
;
2088 FS
->Redirection
= RedirectingFileSystem::RedirectKind::RedirectOnly
;
2090 } else if (Key
== "redirecting-with") {
2091 if (Keys
["fallthrough"].Seen
) {
2093 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2097 if (auto Kind
= parseRedirectKind(I
.getValue())) {
2098 FS
->Redirection
= *Kind
;
2100 error(I
.getValue(), "expected valid redirect kind");
2103 } else if (Key
== "root-relative") {
2104 if (auto Kind
= parseRootRelativeKind(I
.getValue())) {
2105 FS
->RootRelative
= *Kind
;
2107 error(I
.getValue(), "expected valid root-relative kind");
2111 llvm_unreachable("key missing from Keys");
2115 if (Stream
.failed())
2118 if (!checkMissingKeys(Top
, Keys
))
2121 // Now that we sucessefully parsed the YAML file, canonicalize the internal
2122 // representation to a proper directory tree so that we can search faster
2124 for (auto &E
: RootEntries
)
2125 uniqueOverlayTree(FS
, E
.get());
2131 std::unique_ptr
<RedirectingFileSystem
>
2132 RedirectingFileSystem::create(std::unique_ptr
<MemoryBuffer
> Buffer
,
2133 SourceMgr::DiagHandlerTy DiagHandler
,
2134 StringRef YAMLFilePath
, void *DiagContext
,
2135 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
2137 yaml::Stream
Stream(Buffer
->getMemBufferRef(), SM
);
2139 SM
.setDiagHandler(DiagHandler
, DiagContext
);
2140 yaml::document_iterator DI
= Stream
.begin();
2141 yaml::Node
*Root
= DI
->getRoot();
2142 if (DI
== Stream
.end() || !Root
) {
2143 SM
.PrintMessage(SMLoc(), SourceMgr::DK_Error
, "expected root node");
2147 RedirectingFileSystemParser
P(Stream
);
2149 std::unique_ptr
<RedirectingFileSystem
> FS(
2150 new RedirectingFileSystem(ExternalFS
));
2152 if (!YAMLFilePath
.empty()) {
2153 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
2154 // to each 'external-contents' path.
2157 // -ivfsoverlay dummy.cache/vfs/vfs.yaml
2159 // FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs
2161 SmallString
<256> OverlayAbsDir
= sys::path::parent_path(YAMLFilePath
);
2162 std::error_code EC
= llvm::sys::fs::make_absolute(OverlayAbsDir
);
2163 assert(!EC
&& "Overlay dir final path must be absolute");
2165 FS
->setOverlayFileDir(OverlayAbsDir
);
2168 if (!P
.parse(Root
, FS
.get()))
2174 std::unique_ptr
<RedirectingFileSystem
> RedirectingFileSystem::create(
2175 ArrayRef
<std::pair
<std::string
, std::string
>> RemappedFiles
,
2176 bool UseExternalNames
, FileSystem
&ExternalFS
) {
2177 std::unique_ptr
<RedirectingFileSystem
> FS(
2178 new RedirectingFileSystem(&ExternalFS
));
2179 FS
->UseExternalNames
= UseExternalNames
;
2181 StringMap
<RedirectingFileSystem::Entry
*> Entries
;
2183 for (auto &Mapping
: llvm::reverse(RemappedFiles
)) {
2184 SmallString
<128> From
= StringRef(Mapping
.first
);
2185 SmallString
<128> To
= StringRef(Mapping
.second
);
2187 auto EC
= ExternalFS
.makeAbsolute(From
);
2189 assert(!EC
&& "Could not make absolute path");
2192 // Check if we've already mapped this file. The first one we see (in the
2193 // reverse iteration) wins.
2194 RedirectingFileSystem::Entry
*&ToEntry
= Entries
[From
];
2198 // Add parent directories.
2199 RedirectingFileSystem::Entry
*Parent
= nullptr;
2200 StringRef FromDirectory
= llvm::sys::path::parent_path(From
);
2201 for (auto I
= llvm::sys::path::begin(FromDirectory
),
2202 E
= llvm::sys::path::end(FromDirectory
);
2204 Parent
= RedirectingFileSystemParser::lookupOrCreateEntry(FS
.get(), *I
,
2207 assert(Parent
&& "File without a directory?");
2209 auto EC
= ExternalFS
.makeAbsolute(To
);
2211 assert(!EC
&& "Could not make absolute path");
2215 auto NewFile
= std::make_unique
<RedirectingFileSystem::FileEntry
>(
2216 llvm::sys::path::filename(From
), To
,
2217 UseExternalNames
? RedirectingFileSystem::NK_External
2218 : RedirectingFileSystem::NK_Virtual
);
2219 ToEntry
= NewFile
.get();
2220 cast
<RedirectingFileSystem::DirectoryEntry
>(Parent
)->addContent(
2221 std::move(NewFile
));
2227 RedirectingFileSystem::LookupResult::LookupResult(
2228 Entry
*E
, sys::path::const_iterator Start
, sys::path::const_iterator End
)
2230 assert(E
!= nullptr);
2231 // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
2232 // path of the directory it maps to in the external file system plus any
2233 // remaining path components in the provided iterator.
2234 if (auto *DRE
= dyn_cast
<RedirectingFileSystem::DirectoryRemapEntry
>(E
)) {
2235 SmallString
<256> Redirect(DRE
->getExternalContentsPath());
2236 sys::path::append(Redirect
, Start
, End
,
2237 getExistingStyle(DRE
->getExternalContentsPath()));
2238 ExternalRedirect
= std::string(Redirect
);
2242 void RedirectingFileSystem::LookupResult::getPath(
2243 llvm::SmallVectorImpl
<char> &Result
) const {
2245 for (Entry
*Parent
: Parents
)
2246 llvm::sys::path::append(Result
, Parent
->getName());
2247 llvm::sys::path::append(Result
, E
->getName());
2251 RedirectingFileSystem::makeCanonical(SmallVectorImpl
<char> &Path
) const {
2252 if (std::error_code EC
= makeAbsolute(Path
))
2255 llvm::SmallString
<256> CanonicalPath
=
2256 canonicalize(StringRef(Path
.data(), Path
.size()));
2257 if (CanonicalPath
.empty())
2258 return make_error_code(llvm::errc::invalid_argument
);
2260 Path
.assign(CanonicalPath
.begin(), CanonicalPath
.end());
2264 ErrorOr
<RedirectingFileSystem::LookupResult
>
2265 RedirectingFileSystem::lookupPath(StringRef Path
) const {
2266 sys::path::const_iterator Start
= sys::path::begin(Path
);
2267 sys::path::const_iterator End
= sys::path::end(Path
);
2268 llvm::SmallVector
<Entry
*, 32> Entries
;
2269 for (const auto &Root
: Roots
) {
2270 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
=
2271 lookupPathImpl(Start
, End
, Root
.get(), Entries
);
2272 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
) {
2273 Result
->Parents
= std::move(Entries
);
2277 return make_error_code(llvm::errc::no_such_file_or_directory
);
2280 ErrorOr
<RedirectingFileSystem::LookupResult
>
2281 RedirectingFileSystem::lookupPathImpl(
2282 sys::path::const_iterator Start
, sys::path::const_iterator End
,
2283 RedirectingFileSystem::Entry
*From
,
2284 llvm::SmallVectorImpl
<Entry
*> &Entries
) const {
2285 assert(!isTraversalComponent(*Start
) &&
2286 !isTraversalComponent(From
->getName()) &&
2287 "Paths should not contain traversal components");
2289 StringRef FromName
= From
->getName();
2291 // Forward the search to the next component in case this is an empty one.
2292 if (!FromName
.empty()) {
2293 if (!pathComponentMatches(*Start
, FromName
))
2294 return make_error_code(llvm::errc::no_such_file_or_directory
);
2300 return LookupResult(From
, Start
, End
);
2304 if (isa
<RedirectingFileSystem::FileEntry
>(From
))
2305 return make_error_code(llvm::errc::not_a_directory
);
2307 if (isa
<RedirectingFileSystem::DirectoryRemapEntry
>(From
))
2308 return LookupResult(From
, Start
, End
);
2310 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(From
);
2311 for (const std::unique_ptr
<RedirectingFileSystem::Entry
> &DirEntry
:
2312 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
2313 Entries
.push_back(From
);
2314 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
=
2315 lookupPathImpl(Start
, End
, DirEntry
.get(), Entries
);
2316 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
2321 return make_error_code(llvm::errc::no_such_file_or_directory
);
2324 static Status
getRedirectedFileStatus(const Twine
&OriginalPath
,
2325 bool UseExternalNames
,
2326 Status ExternalStatus
) {
2327 // The path has been mapped by some nested VFS and exposes an external path,
2328 // don't override it with the original path.
2329 if (ExternalStatus
.ExposesExternalVFSPath
)
2330 return ExternalStatus
;
2332 Status S
= ExternalStatus
;
2333 if (!UseExternalNames
)
2334 S
= Status::copyWithNewName(S
, OriginalPath
);
2336 S
.ExposesExternalVFSPath
= true;
2337 S
.IsVFSMapped
= true;
2341 ErrorOr
<Status
> RedirectingFileSystem::status(
2342 const Twine
&CanonicalPath
, const Twine
&OriginalPath
,
2343 const RedirectingFileSystem::LookupResult
&Result
) {
2344 if (std::optional
<StringRef
> ExtRedirect
= Result
.getExternalRedirect()) {
2345 SmallString
<256> CanonicalRemappedPath((*ExtRedirect
).str());
2346 if (std::error_code EC
= makeCanonical(CanonicalRemappedPath
))
2349 ErrorOr
<Status
> S
= ExternalFS
->status(CanonicalRemappedPath
);
2352 S
= Status::copyWithNewName(*S
, *ExtRedirect
);
2353 auto *RE
= cast
<RedirectingFileSystem::RemapEntry
>(Result
.E
);
2354 return getRedirectedFileStatus(OriginalPath
,
2355 RE
->useExternalName(UseExternalNames
), *S
);
2358 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(Result
.E
);
2359 return Status::copyWithNewName(DE
->getStatus(), CanonicalPath
);
2363 RedirectingFileSystem::getExternalStatus(const Twine
&CanonicalPath
,
2364 const Twine
&OriginalPath
) const {
2365 auto Result
= ExternalFS
->status(CanonicalPath
);
2367 // The path has been mapped by some nested VFS, don't override it with the
2369 if (!Result
|| Result
->ExposesExternalVFSPath
)
2371 return Status::copyWithNewName(Result
.get(), OriginalPath
);
2374 ErrorOr
<Status
> RedirectingFileSystem::status(const Twine
&OriginalPath
) {
2375 SmallString
<256> CanonicalPath
;
2376 OriginalPath
.toVector(CanonicalPath
);
2378 if (std::error_code EC
= makeCanonical(CanonicalPath
))
2381 if (Redirection
== RedirectKind::Fallback
) {
2382 // Attempt to find the original file first, only falling back to the
2383 // mapped file if that fails.
2384 ErrorOr
<Status
> S
= getExternalStatus(CanonicalPath
, OriginalPath
);
2389 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
=
2390 lookupPath(CanonicalPath
);
2392 // Was not able to map file, fallthrough to using the original path if
2393 // that was the specified redirection type.
2394 if (Redirection
== RedirectKind::Fallthrough
&&
2395 isFileNotFound(Result
.getError()))
2396 return getExternalStatus(CanonicalPath
, OriginalPath
);
2397 return Result
.getError();
2400 ErrorOr
<Status
> S
= status(CanonicalPath
, OriginalPath
, *Result
);
2401 if (!S
&& Redirection
== RedirectKind::Fallthrough
&&
2402 isFileNotFound(S
.getError(), Result
->E
)) {
2403 // Mapped the file but it wasn't found in the underlying filesystem,
2404 // fallthrough to using the original path if that was the specified
2405 // redirection type.
2406 return getExternalStatus(CanonicalPath
, OriginalPath
);
2414 /// Provide a file wrapper with an overriden status.
2415 class FileWithFixedStatus
: public File
{
2416 std::unique_ptr
<File
> InnerFile
;
2420 FileWithFixedStatus(std::unique_ptr
<File
> InnerFile
, Status S
)
2421 : InnerFile(std::move(InnerFile
)), S(std::move(S
)) {}
2423 ErrorOr
<Status
> status() override
{ return S
; }
2424 ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
2426 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
2427 bool IsVolatile
) override
{
2428 return InnerFile
->getBuffer(Name
, FileSize
, RequiresNullTerminator
,
2432 std::error_code
close() override
{ return InnerFile
->close(); }
2434 void setPath(const Twine
&Path
) override
{ S
= S
.copyWithNewName(S
, Path
); }
2439 ErrorOr
<std::unique_ptr
<File
>>
2440 File::getWithPath(ErrorOr
<std::unique_ptr
<File
>> Result
, const Twine
&P
) {
2441 // See \c getRedirectedFileStatus - don't update path if it's exposing an
2443 if (!Result
|| (*Result
)->status()->ExposesExternalVFSPath
)
2446 ErrorOr
<std::unique_ptr
<File
>> F
= std::move(*Result
);
2447 auto Name
= F
->get()->getName();
2448 if (Name
&& Name
.get() != P
.str())
2449 F
->get()->setPath(P
);
2453 ErrorOr
<std::unique_ptr
<File
>>
2454 RedirectingFileSystem::openFileForRead(const Twine
&OriginalPath
) {
2455 SmallString
<256> CanonicalPath
;
2456 OriginalPath
.toVector(CanonicalPath
);
2458 if (std::error_code EC
= makeCanonical(CanonicalPath
))
2461 if (Redirection
== RedirectKind::Fallback
) {
2462 // Attempt to find the original file first, only falling back to the
2463 // mapped file if that fails.
2464 auto F
= File::getWithPath(ExternalFS
->openFileForRead(CanonicalPath
),
2470 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
=
2471 lookupPath(CanonicalPath
);
2473 // Was not able to map file, fallthrough to using the original path if
2474 // that was the specified redirection type.
2475 if (Redirection
== RedirectKind::Fallthrough
&&
2476 isFileNotFound(Result
.getError()))
2477 return File::getWithPath(ExternalFS
->openFileForRead(CanonicalPath
),
2479 return Result
.getError();
2482 if (!Result
->getExternalRedirect()) // FIXME: errc::not_a_file?
2483 return make_error_code(llvm::errc::invalid_argument
);
2485 StringRef ExtRedirect
= *Result
->getExternalRedirect();
2486 SmallString
<256> CanonicalRemappedPath(ExtRedirect
.str());
2487 if (std::error_code EC
= makeCanonical(CanonicalRemappedPath
))
2490 auto *RE
= cast
<RedirectingFileSystem::RemapEntry
>(Result
->E
);
2492 auto ExternalFile
= File::getWithPath(
2493 ExternalFS
->openFileForRead(CanonicalRemappedPath
), ExtRedirect
);
2494 if (!ExternalFile
) {
2495 if (Redirection
== RedirectKind::Fallthrough
&&
2496 isFileNotFound(ExternalFile
.getError(), Result
->E
)) {
2497 // Mapped the file but it wasn't found in the underlying filesystem,
2498 // fallthrough to using the original path if that was the specified
2499 // redirection type.
2500 return File::getWithPath(ExternalFS
->openFileForRead(CanonicalPath
),
2503 return ExternalFile
;
2506 auto ExternalStatus
= (*ExternalFile
)->status();
2507 if (!ExternalStatus
)
2508 return ExternalStatus
.getError();
2510 // Otherwise, the file was successfully remapped. Mark it as such. Also
2511 // replace the underlying path if the external name is being used.
2512 Status S
= getRedirectedFileStatus(
2513 OriginalPath
, RE
->useExternalName(UseExternalNames
), *ExternalStatus
);
2514 return std::unique_ptr
<File
>(
2515 std::make_unique
<FileWithFixedStatus
>(std::move(*ExternalFile
), S
));
2519 RedirectingFileSystem::getRealPath(const Twine
&OriginalPath
,
2520 SmallVectorImpl
<char> &Output
) const {
2521 SmallString
<256> CanonicalPath
;
2522 OriginalPath
.toVector(CanonicalPath
);
2524 if (std::error_code EC
= makeCanonical(CanonicalPath
))
2527 if (Redirection
== RedirectKind::Fallback
) {
2528 // Attempt to find the original file first, only falling back to the
2529 // mapped file if that fails.
2530 std::error_code EC
= ExternalFS
->getRealPath(CanonicalPath
, Output
);
2535 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
=
2536 lookupPath(CanonicalPath
);
2538 // Was not able to map file, fallthrough to using the original path if
2539 // that was the specified redirection type.
2540 if (Redirection
== RedirectKind::Fallthrough
&&
2541 isFileNotFound(Result
.getError()))
2542 return ExternalFS
->getRealPath(CanonicalPath
, Output
);
2543 return Result
.getError();
2546 // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2547 // path in the external file system.
2548 if (auto ExtRedirect
= Result
->getExternalRedirect()) {
2549 auto P
= ExternalFS
->getRealPath(*ExtRedirect
, Output
);
2550 if (P
&& Redirection
== RedirectKind::Fallthrough
&&
2551 isFileNotFound(P
, Result
->E
)) {
2552 // Mapped the file but it wasn't found in the underlying filesystem,
2553 // fallthrough to using the original path if that was the specified
2554 // redirection type.
2555 return ExternalFS
->getRealPath(CanonicalPath
, Output
);
2560 // We found a DirectoryEntry, which does not have a single external contents
2561 // path. Use the canonical virtual path.
2562 if (Redirection
== RedirectKind::Fallthrough
) {
2563 Result
->getPath(Output
);
2566 return llvm::errc::invalid_argument
;
2569 std::unique_ptr
<FileSystem
>
2570 vfs::getVFSFromYAML(std::unique_ptr
<MemoryBuffer
> Buffer
,
2571 SourceMgr::DiagHandlerTy DiagHandler
,
2572 StringRef YAMLFilePath
, void *DiagContext
,
2573 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
2574 return RedirectingFileSystem::create(std::move(Buffer
), DiagHandler
,
2575 YAMLFilePath
, DiagContext
,
2576 std::move(ExternalFS
));
2579 static void getVFSEntries(RedirectingFileSystem::Entry
*SrcE
,
2580 SmallVectorImpl
<StringRef
> &Path
,
2581 SmallVectorImpl
<YAMLVFSEntry
> &Entries
) {
2582 auto Kind
= SrcE
->getKind();
2583 if (Kind
== RedirectingFileSystem::EK_Directory
) {
2584 auto *DE
= dyn_cast
<RedirectingFileSystem::DirectoryEntry
>(SrcE
);
2585 assert(DE
&& "Must be a directory");
2586 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &SubEntry
:
2587 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
2588 Path
.push_back(SubEntry
->getName());
2589 getVFSEntries(SubEntry
.get(), Path
, Entries
);
2595 if (Kind
== RedirectingFileSystem::EK_DirectoryRemap
) {
2596 auto *DR
= dyn_cast
<RedirectingFileSystem::DirectoryRemapEntry
>(SrcE
);
2597 assert(DR
&& "Must be a directory remap");
2598 SmallString
<128> VPath
;
2599 for (auto &Comp
: Path
)
2600 llvm::sys::path::append(VPath
, Comp
);
2602 YAMLVFSEntry(VPath
.c_str(), DR
->getExternalContentsPath()));
2606 assert(Kind
== RedirectingFileSystem::EK_File
&& "Must be a EK_File");
2607 auto *FE
= dyn_cast
<RedirectingFileSystem::FileEntry
>(SrcE
);
2608 assert(FE
&& "Must be a file");
2609 SmallString
<128> VPath
;
2610 for (auto &Comp
: Path
)
2611 llvm::sys::path::append(VPath
, Comp
);
2612 Entries
.push_back(YAMLVFSEntry(VPath
.c_str(), FE
->getExternalContentsPath()));
2615 void vfs::collectVFSFromYAML(std::unique_ptr
<MemoryBuffer
> Buffer
,
2616 SourceMgr::DiagHandlerTy DiagHandler
,
2617 StringRef YAMLFilePath
,
2618 SmallVectorImpl
<YAMLVFSEntry
> &CollectedEntries
,
2620 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
2621 std::unique_ptr
<RedirectingFileSystem
> VFS
= RedirectingFileSystem::create(
2622 std::move(Buffer
), DiagHandler
, YAMLFilePath
, DiagContext
,
2623 std::move(ExternalFS
));
2626 ErrorOr
<RedirectingFileSystem::LookupResult
> RootResult
=
2627 VFS
->lookupPath("/");
2630 SmallVector
<StringRef
, 8> Components
;
2631 Components
.push_back("/");
2632 getVFSEntries(RootResult
->E
, Components
, CollectedEntries
);
2635 UniqueID
vfs::getNextVirtualUniqueID() {
2636 static std::atomic
<unsigned> UID
;
2637 unsigned ID
= ++UID
;
2638 // The following assumes that uint64_t max will never collide with a real
2639 // dev_t value from the OS.
2640 return UniqueID(std::numeric_limits
<uint64_t>::max(), ID
);
2643 void YAMLVFSWriter::addEntry(StringRef VirtualPath
, StringRef RealPath
,
2645 assert(sys::path::is_absolute(VirtualPath
) && "virtual path not absolute");
2646 assert(sys::path::is_absolute(RealPath
) && "real path not absolute");
2647 assert(!pathHasTraversal(VirtualPath
) && "path traversal is not supported");
2648 Mappings
.emplace_back(VirtualPath
, RealPath
, IsDirectory
);
2651 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath
, StringRef RealPath
) {
2652 addEntry(VirtualPath
, RealPath
, /*IsDirectory=*/false);
2655 void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath
,
2656 StringRef RealPath
) {
2657 addEntry(VirtualPath
, RealPath
, /*IsDirectory=*/true);
2663 llvm::raw_ostream
&OS
;
2664 SmallVector
<StringRef
, 16> DirStack
;
2666 unsigned getDirIndent() { return 4 * DirStack
.size(); }
2667 unsigned getFileIndent() { return 4 * (DirStack
.size() + 1); }
2668 bool containedIn(StringRef Parent
, StringRef Path
);
2669 StringRef
containedPart(StringRef Parent
, StringRef Path
);
2670 void startDirectory(StringRef Path
);
2671 void endDirectory();
2672 void writeEntry(StringRef VPath
, StringRef RPath
);
2675 JSONWriter(llvm::raw_ostream
&OS
) : OS(OS
) {}
2677 void write(ArrayRef
<YAMLVFSEntry
> Entries
,
2678 std::optional
<bool> UseExternalNames
,
2679 std::optional
<bool> IsCaseSensitive
,
2680 std::optional
<bool> IsOverlayRelative
, StringRef OverlayDir
);
2685 bool JSONWriter::containedIn(StringRef Parent
, StringRef Path
) {
2686 using namespace llvm::sys
;
2688 // Compare each path component.
2689 auto IParent
= path::begin(Parent
), EParent
= path::end(Parent
);
2690 for (auto IChild
= path::begin(Path
), EChild
= path::end(Path
);
2691 IParent
!= EParent
&& IChild
!= EChild
; ++IParent
, ++IChild
) {
2692 if (*IParent
!= *IChild
)
2695 // Have we exhausted the parent path?
2696 return IParent
== EParent
;
2699 StringRef
JSONWriter::containedPart(StringRef Parent
, StringRef Path
) {
2700 assert(!Parent
.empty());
2701 assert(containedIn(Parent
, Path
));
2702 return Path
.slice(Parent
.size() + 1, StringRef::npos
);
2705 void JSONWriter::startDirectory(StringRef Path
) {
2707 DirStack
.empty() ? Path
: containedPart(DirStack
.back(), Path
);
2708 DirStack
.push_back(Path
);
2709 unsigned Indent
= getDirIndent();
2710 OS
.indent(Indent
) << "{\n";
2711 OS
.indent(Indent
+ 2) << "'type': 'directory',\n";
2712 OS
.indent(Indent
+ 2) << "'name': \"" << llvm::yaml::escape(Name
) << "\",\n";
2713 OS
.indent(Indent
+ 2) << "'contents': [\n";
2716 void JSONWriter::endDirectory() {
2717 unsigned Indent
= getDirIndent();
2718 OS
.indent(Indent
+ 2) << "]\n";
2719 OS
.indent(Indent
) << "}";
2721 DirStack
.pop_back();
2724 void JSONWriter::writeEntry(StringRef VPath
, StringRef RPath
) {
2725 unsigned Indent
= getFileIndent();
2726 OS
.indent(Indent
) << "{\n";
2727 OS
.indent(Indent
+ 2) << "'type': 'file',\n";
2728 OS
.indent(Indent
+ 2) << "'name': \"" << llvm::yaml::escape(VPath
) << "\",\n";
2729 OS
.indent(Indent
+ 2) << "'external-contents': \""
2730 << llvm::yaml::escape(RPath
) << "\"\n";
2731 OS
.indent(Indent
) << "}";
2734 void JSONWriter::write(ArrayRef
<YAMLVFSEntry
> Entries
,
2735 std::optional
<bool> UseExternalNames
,
2736 std::optional
<bool> IsCaseSensitive
,
2737 std::optional
<bool> IsOverlayRelative
,
2738 StringRef OverlayDir
) {
2739 using namespace llvm::sys
;
2743 if (IsCaseSensitive
)
2744 OS
<< " 'case-sensitive': '" << (*IsCaseSensitive
? "true" : "false")
2746 if (UseExternalNames
)
2747 OS
<< " 'use-external-names': '" << (*UseExternalNames
? "true" : "false")
2749 bool UseOverlayRelative
= false;
2750 if (IsOverlayRelative
) {
2751 UseOverlayRelative
= *IsOverlayRelative
;
2752 OS
<< " 'overlay-relative': '" << (UseOverlayRelative
? "true" : "false")
2755 OS
<< " 'roots': [\n";
2757 if (!Entries
.empty()) {
2758 const YAMLVFSEntry
&Entry
= Entries
.front();
2761 Entry
.IsDirectory
? Entry
.VPath
: path::parent_path(Entry
.VPath
)
2764 StringRef RPath
= Entry
.RPath
;
2765 if (UseOverlayRelative
) {
2766 unsigned OverlayDirLen
= OverlayDir
.size();
2767 assert(RPath
.substr(0, OverlayDirLen
) == OverlayDir
&&
2768 "Overlay dir must be contained in RPath");
2769 RPath
= RPath
.slice(OverlayDirLen
, RPath
.size());
2772 bool IsCurrentDirEmpty
= true;
2773 if (!Entry
.IsDirectory
) {
2774 writeEntry(path::filename(Entry
.VPath
), RPath
);
2775 IsCurrentDirEmpty
= false;
2778 for (const auto &Entry
: Entries
.slice(1)) {
2780 Entry
.IsDirectory
? Entry
.VPath
: path::parent_path(Entry
.VPath
);
2781 if (Dir
== DirStack
.back()) {
2782 if (!IsCurrentDirEmpty
) {
2786 bool IsDirPoppedFromStack
= false;
2787 while (!DirStack
.empty() && !containedIn(DirStack
.back(), Dir
)) {
2790 IsDirPoppedFromStack
= true;
2792 if (IsDirPoppedFromStack
|| !IsCurrentDirEmpty
) {
2795 startDirectory(Dir
);
2796 IsCurrentDirEmpty
= true;
2798 StringRef RPath
= Entry
.RPath
;
2799 if (UseOverlayRelative
) {
2800 unsigned OverlayDirLen
= OverlayDir
.size();
2801 assert(RPath
.substr(0, OverlayDirLen
) == OverlayDir
&&
2802 "Overlay dir must be contained in RPath");
2803 RPath
= RPath
.slice(OverlayDirLen
, RPath
.size());
2805 if (!Entry
.IsDirectory
) {
2806 writeEntry(path::filename(Entry
.VPath
), RPath
);
2807 IsCurrentDirEmpty
= false;
2811 while (!DirStack
.empty()) {
2822 void YAMLVFSWriter::write(llvm::raw_ostream
&OS
) {
2823 llvm::sort(Mappings
, [](const YAMLVFSEntry
&LHS
, const YAMLVFSEntry
&RHS
) {
2824 return LHS
.VPath
< RHS
.VPath
;
2827 JSONWriter(OS
).write(Mappings
, UseExternalNames
, IsCaseSensitive
,
2828 IsOverlayRelative
, OverlayDir
);
2831 vfs::recursive_directory_iterator::recursive_directory_iterator(
2832 FileSystem
&FS_
, const Twine
&Path
, std::error_code
&EC
)
2834 directory_iterator I
= FS
->dir_begin(Path
, EC
);
2835 if (I
!= directory_iterator()) {
2836 State
= std::make_shared
<detail::RecDirIterState
>();
2837 State
->Stack
.push(I
);
2841 vfs::recursive_directory_iterator
&
2842 recursive_directory_iterator::increment(std::error_code
&EC
) {
2843 assert(FS
&& State
&& !State
->Stack
.empty() && "incrementing past end");
2844 assert(!State
->Stack
.top()->path().empty() && "non-canonical end iterator");
2845 vfs::directory_iterator End
;
2847 if (State
->HasNoPushRequest
)
2848 State
->HasNoPushRequest
= false;
2850 if (State
->Stack
.top()->type() == sys::fs::file_type::directory_file
) {
2851 vfs::directory_iterator I
= FS
->dir_begin(State
->Stack
.top()->path(), EC
);
2853 State
->Stack
.push(I
);
2859 while (!State
->Stack
.empty() && State
->Stack
.top().increment(EC
) == End
)
2862 if (State
->Stack
.empty())
2863 State
.reset(); // end iterator