1 //===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file implements the VirtualFileSystem interface.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/Support/VirtualFileSystem.h"
15 #include "llvm/ADT/ArrayRef.h"
16 #include "llvm/ADT/DenseMap.h"
17 #include "llvm/ADT/IntrusiveRefCntPtr.h"
18 #include "llvm/ADT/None.h"
19 #include "llvm/ADT/Optional.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/ADT/StringSet.h"
25 #include "llvm/ADT/Twine.h"
26 #include "llvm/ADT/iterator_range.h"
27 #include "llvm/Config/llvm-config.h"
28 #include "llvm/Support/Casting.h"
29 #include "llvm/Support/Chrono.h"
30 #include "llvm/Support/Compiler.h"
31 #include "llvm/Support/Debug.h"
32 #include "llvm/Support/Errc.h"
33 #include "llvm/Support/ErrorHandling.h"
34 #include "llvm/Support/ErrorOr.h"
35 #include "llvm/Support/FileSystem.h"
36 #include "llvm/Support/MemoryBuffer.h"
37 #include "llvm/Support/Path.h"
38 #include "llvm/Support/Process.h"
39 #include "llvm/Support/SMLoc.h"
40 #include "llvm/Support/SourceMgr.h"
41 #include "llvm/Support/YAMLParser.h"
42 #include "llvm/Support/raw_ostream.h"
53 #include <system_error>
58 using namespace llvm::vfs
;
60 using llvm::sys::fs::file_status
;
61 using llvm::sys::fs::file_type
;
62 using llvm::sys::fs::perms
;
63 using llvm::sys::fs::UniqueID
;
65 Status::Status(const file_status
&Status
)
66 : UID(Status
.getUniqueID()), MTime(Status
.getLastModificationTime()),
67 User(Status
.getUser()), Group(Status
.getGroup()), Size(Status
.getSize()),
68 Type(Status
.type()), Perms(Status
.permissions()) {}
70 Status::Status(StringRef Name
, UniqueID UID
, sys::TimePoint
<> MTime
,
71 uint32_t User
, uint32_t Group
, uint64_t Size
, file_type Type
,
73 : Name(Name
), UID(UID
), MTime(MTime
), User(User
), Group(Group
), Size(Size
),
74 Type(Type
), Perms(Perms
) {}
76 Status
Status::copyWithNewName(const Status
&In
, StringRef NewName
) {
77 return Status(NewName
, In
.getUniqueID(), In
.getLastModificationTime(),
78 In
.getUser(), In
.getGroup(), In
.getSize(), In
.getType(),
82 Status
Status::copyWithNewName(const file_status
&In
, StringRef NewName
) {
83 return Status(NewName
, In
.getUniqueID(), In
.getLastModificationTime(),
84 In
.getUser(), In
.getGroup(), In
.getSize(), In
.type(),
88 bool Status::equivalent(const Status
&Other
) const {
89 assert(isStatusKnown() && Other
.isStatusKnown());
90 return getUniqueID() == Other
.getUniqueID();
93 bool Status::isDirectory() const { return Type
== file_type::directory_file
; }
95 bool Status::isRegularFile() const { return Type
== file_type::regular_file
; }
97 bool Status::isOther() const {
98 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
101 bool Status::isSymlink() const { return Type
== file_type::symlink_file
; }
103 bool Status::isStatusKnown() const { return Type
!= file_type::status_error
; }
105 bool Status::exists() const {
106 return isStatusKnown() && Type
!= file_type::file_not_found
;
109 File::~File() = default;
111 FileSystem::~FileSystem() = default;
113 ErrorOr
<std::unique_ptr
<MemoryBuffer
>>
114 FileSystem::getBufferForFile(const llvm::Twine
&Name
, int64_t FileSize
,
115 bool RequiresNullTerminator
, bool IsVolatile
) {
116 auto F
= openFileForRead(Name
);
120 return (*F
)->getBuffer(Name
, FileSize
, RequiresNullTerminator
, IsVolatile
);
123 std::error_code
FileSystem::makeAbsolute(SmallVectorImpl
<char> &Path
) const {
124 if (llvm::sys::path::is_absolute(Path
))
127 auto WorkingDir
= getCurrentWorkingDirectory();
129 return WorkingDir
.getError();
131 return llvm::sys::fs::make_absolute(WorkingDir
.get(), Path
);
134 std::error_code
FileSystem::getRealPath(const Twine
&Path
,
135 SmallVectorImpl
<char> &Output
) const {
136 return errc::operation_not_permitted
;
139 std::error_code
FileSystem::isLocal(const Twine
&Path
, bool &Result
) {
140 return errc::operation_not_permitted
;
143 bool FileSystem::exists(const Twine
&Path
) {
144 auto Status
= status(Path
);
145 return Status
&& Status
->exists();
149 static bool isTraversalComponent(StringRef Component
) {
150 return Component
.equals("..") || Component
.equals(".");
153 static bool pathHasTraversal(StringRef Path
) {
154 using namespace llvm::sys
;
156 for (StringRef Comp
: llvm::make_range(path::begin(Path
), path::end(Path
)))
157 if (isTraversalComponent(Comp
))
163 //===-----------------------------------------------------------------------===/
164 // RealFileSystem implementation
165 //===-----------------------------------------------------------------------===/
169 /// Wrapper around a raw file descriptor.
170 class RealFile
: public File
{
171 friend class RealFileSystem
;
175 std::string RealName
;
177 RealFile(int FD
, StringRef NewName
, StringRef NewRealPathName
)
178 : FD(FD
), S(NewName
, {}, {}, {}, {}, {},
179 llvm::sys::fs::file_type::status_error
, {}),
180 RealName(NewRealPathName
.str()) {
181 assert(FD
>= 0 && "Invalid or inactive file descriptor");
185 ~RealFile() override
;
187 ErrorOr
<Status
> status() override
;
188 ErrorOr
<std::string
> getName() override
;
189 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> getBuffer(const Twine
&Name
,
191 bool RequiresNullTerminator
,
192 bool IsVolatile
) override
;
193 std::error_code
close() override
;
198 RealFile::~RealFile() { close(); }
200 ErrorOr
<Status
> RealFile::status() {
201 assert(FD
!= -1 && "cannot stat closed file");
202 if (!S
.isStatusKnown()) {
203 file_status RealStatus
;
204 if (std::error_code EC
= sys::fs::status(FD
, RealStatus
))
206 S
= Status::copyWithNewName(RealStatus
, S
.getName());
211 ErrorOr
<std::string
> RealFile::getName() {
212 return RealName
.empty() ? S
.getName().str() : RealName
;
215 ErrorOr
<std::unique_ptr
<MemoryBuffer
>>
216 RealFile::getBuffer(const Twine
&Name
, int64_t FileSize
,
217 bool RequiresNullTerminator
, bool IsVolatile
) {
218 assert(FD
!= -1 && "cannot get buffer for closed file");
219 return MemoryBuffer::getOpenFile(FD
, Name
, FileSize
, RequiresNullTerminator
,
223 std::error_code
RealFile::close() {
224 std::error_code EC
= sys::Process::SafelyCloseFileDescriptor(FD
);
231 /// The file system according to your operating system.
232 class RealFileSystem
: public FileSystem
{
234 ErrorOr
<Status
> status(const Twine
&Path
) override
;
235 ErrorOr
<std::unique_ptr
<File
>> openFileForRead(const Twine
&Path
) override
;
236 directory_iterator
dir_begin(const Twine
&Dir
, std::error_code
&EC
) override
;
238 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
;
239 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
;
240 std::error_code
isLocal(const Twine
&Path
, bool &Result
) override
;
241 std::error_code
getRealPath(const Twine
&Path
,
242 SmallVectorImpl
<char> &Output
) const override
;
245 mutable std::mutex CWDMutex
;
246 mutable std::string CWDCache
;
251 ErrorOr
<Status
> RealFileSystem::status(const Twine
&Path
) {
252 sys::fs::file_status RealStatus
;
253 if (std::error_code EC
= sys::fs::status(Path
, RealStatus
))
255 return Status::copyWithNewName(RealStatus
, Path
.str());
258 ErrorOr
<std::unique_ptr
<File
>>
259 RealFileSystem::openFileForRead(const Twine
&Name
) {
261 SmallString
<256> RealName
;
262 if (std::error_code EC
=
263 sys::fs::openFileForRead(Name
, FD
, sys::fs::OF_None
, &RealName
))
265 return std::unique_ptr
<File
>(new RealFile(FD
, Name
.str(), RealName
.str()));
268 llvm::ErrorOr
<std::string
> RealFileSystem::getCurrentWorkingDirectory() const {
269 std::lock_guard
<std::mutex
> Lock(CWDMutex
);
270 if (!CWDCache
.empty())
272 SmallString
<256> Dir
;
273 if (std::error_code EC
= llvm::sys::fs::current_path(Dir
))
275 CWDCache
= Dir
.str();
279 std::error_code
RealFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
280 // FIXME: chdir is thread hostile; on the other hand, creating the same
281 // behavior as chdir is complex: chdir resolves the path once, thus
282 // guaranteeing that all subsequent relative path operations work
283 // on the same path the original chdir resulted in. This makes a
284 // difference for example on network filesystems, where symlinks might be
285 // switched during runtime of the tool. Fixing this depends on having a
286 // file system abstraction that allows openat() style interactions.
287 if (auto EC
= llvm::sys::fs::set_current_path(Path
))
291 std::lock_guard
<std::mutex
> Lock(CWDMutex
);
293 return std::error_code();
296 std::error_code
RealFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
297 return llvm::sys::fs::is_local(Path
, Result
);
301 RealFileSystem::getRealPath(const Twine
&Path
,
302 SmallVectorImpl
<char> &Output
) const {
303 return llvm::sys::fs::real_path(Path
, Output
);
306 IntrusiveRefCntPtr
<FileSystem
> vfs::getRealFileSystem() {
307 static IntrusiveRefCntPtr
<FileSystem
> FS
= new RealFileSystem();
313 class RealFSDirIter
: public llvm::vfs::detail::DirIterImpl
{
314 llvm::sys::fs::directory_iterator Iter
;
317 RealFSDirIter(const Twine
&Path
, std::error_code
&EC
) : Iter(Path
, EC
) {
318 if (Iter
!= llvm::sys::fs::directory_iterator())
319 CurrentEntry
= directory_entry(Iter
->path(), Iter
->type());
322 std::error_code
increment() override
{
325 CurrentEntry
= (Iter
== llvm::sys::fs::directory_iterator())
327 : directory_entry(Iter
->path(), Iter
->type());
334 directory_iterator
RealFileSystem::dir_begin(const Twine
&Dir
,
335 std::error_code
&EC
) {
336 return directory_iterator(std::make_shared
<RealFSDirIter
>(Dir
, EC
));
339 //===-----------------------------------------------------------------------===/
340 // OverlayFileSystem implementation
341 //===-----------------------------------------------------------------------===/
343 OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr
<FileSystem
> BaseFS
) {
344 FSList
.push_back(std::move(BaseFS
));
347 void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr
<FileSystem
> FS
) {
348 FSList
.push_back(FS
);
349 // Synchronize added file systems by duplicating the working directory from
350 // the first one in the list.
351 FS
->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
354 ErrorOr
<Status
> OverlayFileSystem::status(const Twine
&Path
) {
355 // FIXME: handle symlinks that cross file systems
356 for (iterator I
= overlays_begin(), E
= overlays_end(); I
!= E
; ++I
) {
357 ErrorOr
<Status
> Status
= (*I
)->status(Path
);
358 if (Status
|| Status
.getError() != llvm::errc::no_such_file_or_directory
)
361 return make_error_code(llvm::errc::no_such_file_or_directory
);
364 ErrorOr
<std::unique_ptr
<File
>>
365 OverlayFileSystem::openFileForRead(const llvm::Twine
&Path
) {
366 // FIXME: handle symlinks that cross file systems
367 for (iterator I
= overlays_begin(), E
= overlays_end(); I
!= E
; ++I
) {
368 auto Result
= (*I
)->openFileForRead(Path
);
369 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
372 return make_error_code(llvm::errc::no_such_file_or_directory
);
375 llvm::ErrorOr
<std::string
>
376 OverlayFileSystem::getCurrentWorkingDirectory() const {
377 // All file systems are synchronized, just take the first working directory.
378 return FSList
.front()->getCurrentWorkingDirectory();
382 OverlayFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
383 for (auto &FS
: FSList
)
384 if (std::error_code EC
= FS
->setCurrentWorkingDirectory(Path
))
389 std::error_code
OverlayFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
390 for (auto &FS
: FSList
)
391 if (FS
->exists(Path
))
392 return FS
->isLocal(Path
, Result
);
393 return errc::no_such_file_or_directory
;
397 OverlayFileSystem::getRealPath(const Twine
&Path
,
398 SmallVectorImpl
<char> &Output
) const {
399 for (auto &FS
: FSList
)
400 if (FS
->exists(Path
))
401 return FS
->getRealPath(Path
, Output
);
402 return errc::no_such_file_or_directory
;
405 llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;
409 class OverlayFSDirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
410 OverlayFileSystem
&Overlays
;
412 OverlayFileSystem::iterator CurrentFS
;
413 directory_iterator CurrentDirIter
;
414 llvm::StringSet
<> SeenNames
;
416 std::error_code
incrementFS() {
417 assert(CurrentFS
!= Overlays
.overlays_end() && "incrementing past end");
419 for (auto E
= Overlays
.overlays_end(); CurrentFS
!= E
; ++CurrentFS
) {
421 CurrentDirIter
= (*CurrentFS
)->dir_begin(Path
, EC
);
422 if (EC
&& EC
!= errc::no_such_file_or_directory
)
424 if (CurrentDirIter
!= directory_iterator())
430 std::error_code
incrementDirIter(bool IsFirstTime
) {
431 assert((IsFirstTime
|| CurrentDirIter
!= directory_iterator()) &&
432 "incrementing past end");
435 CurrentDirIter
.increment(EC
);
436 if (!EC
&& CurrentDirIter
== directory_iterator())
441 std::error_code
incrementImpl(bool IsFirstTime
) {
443 std::error_code EC
= incrementDirIter(IsFirstTime
);
444 if (EC
|| CurrentDirIter
== directory_iterator()) {
445 CurrentEntry
= directory_entry();
448 CurrentEntry
= *CurrentDirIter
;
449 StringRef Name
= llvm::sys::path::filename(CurrentEntry
.path());
450 if (SeenNames
.insert(Name
).second
)
451 return EC
; // name not seen before
453 llvm_unreachable("returned above");
457 OverlayFSDirIterImpl(const Twine
&Path
, OverlayFileSystem
&FS
,
459 : Overlays(FS
), Path(Path
.str()), CurrentFS(Overlays
.overlays_begin()) {
460 CurrentDirIter
= (*CurrentFS
)->dir_begin(Path
, EC
);
461 EC
= incrementImpl(true);
464 std::error_code
increment() override
{ return incrementImpl(false); }
469 directory_iterator
OverlayFileSystem::dir_begin(const Twine
&Dir
,
470 std::error_code
&EC
) {
471 return directory_iterator(
472 std::make_shared
<OverlayFSDirIterImpl
>(Dir
, *this, EC
));
475 void ProxyFileSystem::anchor() {}
482 enum InMemoryNodeKind
{ IME_File
, IME_Directory
, IME_HardLink
};
484 /// The in memory file system is a tree of Nodes. Every node can either be a
485 /// file , hardlink or a directory.
487 InMemoryNodeKind Kind
;
488 std::string FileName
;
491 InMemoryNode(llvm::StringRef FileName
, InMemoryNodeKind Kind
)
492 : Kind(Kind
), FileName(llvm::sys::path::filename(FileName
)) {}
493 virtual ~InMemoryNode() = default;
495 /// Get the filename of this node (the name without the directory part).
496 StringRef
getFileName() const { return FileName
; }
497 InMemoryNodeKind
getKind() const { return Kind
; }
498 virtual std::string
toString(unsigned Indent
) const = 0;
501 class InMemoryFile
: public InMemoryNode
{
503 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
;
506 InMemoryFile(Status Stat
, std::unique_ptr
<llvm::MemoryBuffer
> Buffer
)
507 : InMemoryNode(Stat
.getName(), IME_File
), Stat(std::move(Stat
)),
508 Buffer(std::move(Buffer
)) {}
510 /// Return the \p Status for this node. \p RequestedName should be the name
511 /// through which the caller referred to this node. It will override
512 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
513 Status
getStatus(StringRef RequestedName
) const {
514 return Status::copyWithNewName(Stat
, RequestedName
);
516 llvm::MemoryBuffer
*getBuffer() const { return Buffer
.get(); }
518 std::string
toString(unsigned Indent
) const override
{
519 return (std::string(Indent
, ' ') + Stat
.getName() + "\n").str();
522 static bool classof(const InMemoryNode
*N
) {
523 return N
->getKind() == IME_File
;
529 class InMemoryHardLink
: public InMemoryNode
{
530 const InMemoryFile
&ResolvedFile
;
533 InMemoryHardLink(StringRef Path
, const InMemoryFile
&ResolvedFile
)
534 : InMemoryNode(Path
, IME_HardLink
), ResolvedFile(ResolvedFile
) {}
535 const InMemoryFile
&getResolvedFile() const { return ResolvedFile
; }
537 std::string
toString(unsigned Indent
) const override
{
538 return std::string(Indent
, ' ') + "HardLink to -> " +
539 ResolvedFile
.toString(0);
542 static bool classof(const InMemoryNode
*N
) {
543 return N
->getKind() == IME_HardLink
;
547 /// Adapt a InMemoryFile for VFS' File interface. The goal is to make
548 /// \p InMemoryFileAdaptor mimic as much as possible the behavior of
550 class InMemoryFileAdaptor
: public File
{
551 const InMemoryFile
&Node
;
552 /// The name to use when returning a Status for this file.
553 std::string RequestedName
;
556 explicit InMemoryFileAdaptor(const InMemoryFile
&Node
,
557 std::string RequestedName
)
558 : Node(Node
), RequestedName(std::move(RequestedName
)) {}
560 llvm::ErrorOr
<Status
> status() override
{
561 return Node
.getStatus(RequestedName
);
564 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
565 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
566 bool IsVolatile
) override
{
567 llvm::MemoryBuffer
*Buf
= Node
.getBuffer();
568 return llvm::MemoryBuffer::getMemBuffer(
569 Buf
->getBuffer(), Buf
->getBufferIdentifier(), RequiresNullTerminator
);
572 std::error_code
close() override
{ return {}; }
576 class InMemoryDirectory
: public InMemoryNode
{
578 llvm::StringMap
<std::unique_ptr
<InMemoryNode
>> Entries
;
581 InMemoryDirectory(Status Stat
)
582 : InMemoryNode(Stat
.getName(), IME_Directory
), Stat(std::move(Stat
)) {}
584 /// Return the \p Status for this node. \p RequestedName should be the name
585 /// through which the caller referred to this node. It will override
586 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
587 Status
getStatus(StringRef RequestedName
) const {
588 return Status::copyWithNewName(Stat
, RequestedName
);
590 InMemoryNode
*getChild(StringRef Name
) {
591 auto I
= Entries
.find(Name
);
592 if (I
!= Entries
.end())
593 return I
->second
.get();
597 InMemoryNode
*addChild(StringRef Name
, std::unique_ptr
<InMemoryNode
> Child
) {
598 return Entries
.insert(make_pair(Name
, std::move(Child
)))
599 .first
->second
.get();
602 using const_iterator
= decltype(Entries
)::const_iterator
;
604 const_iterator
begin() const { return Entries
.begin(); }
605 const_iterator
end() const { return Entries
.end(); }
607 std::string
toString(unsigned Indent
) const override
{
609 (std::string(Indent
, ' ') + Stat
.getName() + "\n").str();
610 for (const auto &Entry
: Entries
)
611 Result
+= Entry
.second
->toString(Indent
+ 2);
615 static bool classof(const InMemoryNode
*N
) {
616 return N
->getKind() == IME_Directory
;
621 Status
getNodeStatus(const InMemoryNode
*Node
, StringRef RequestedName
) {
622 if (auto Dir
= dyn_cast
<detail::InMemoryDirectory
>(Node
))
623 return Dir
->getStatus(RequestedName
);
624 if (auto File
= dyn_cast
<detail::InMemoryFile
>(Node
))
625 return File
->getStatus(RequestedName
);
626 if (auto Link
= dyn_cast
<detail::InMemoryHardLink
>(Node
))
627 return Link
->getResolvedFile().getStatus(RequestedName
);
628 llvm_unreachable("Unknown node type");
631 } // namespace detail
633 InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths
)
634 : Root(new detail::InMemoryDirectory(
635 Status("", getNextVirtualUniqueID(), llvm::sys::TimePoint
<>(), 0, 0,
636 0, llvm::sys::fs::file_type::directory_file
,
637 llvm::sys::fs::perms::all_all
))),
638 UseNormalizedPaths(UseNormalizedPaths
) {}
640 InMemoryFileSystem::~InMemoryFileSystem() = default;
642 std::string
InMemoryFileSystem::toString() const {
643 return Root
->toString(/*Indent=*/0);
646 bool InMemoryFileSystem::addFile(const Twine
&P
, time_t ModificationTime
,
647 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
,
648 Optional
<uint32_t> User
,
649 Optional
<uint32_t> Group
,
650 Optional
<llvm::sys::fs::file_type
> Type
,
651 Optional
<llvm::sys::fs::perms
> Perms
,
652 const detail::InMemoryFile
*HardLinkTarget
) {
653 SmallString
<128> Path
;
656 // Fix up relative paths. This just prepends the current working directory.
657 std::error_code EC
= makeAbsolute(Path
);
661 if (useNormalizedPaths())
662 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
667 detail::InMemoryDirectory
*Dir
= Root
.get();
668 auto I
= llvm::sys::path::begin(Path
), E
= sys::path::end(Path
);
669 const auto ResolvedUser
= User
.getValueOr(0);
670 const auto ResolvedGroup
= Group
.getValueOr(0);
671 const auto ResolvedType
= Type
.getValueOr(sys::fs::file_type::regular_file
);
672 const auto ResolvedPerms
= Perms
.getValueOr(sys::fs::all_all
);
673 assert(!(HardLinkTarget
&& Buffer
) && "HardLink cannot have a buffer");
674 // Any intermediate directories we create should be accessible by
675 // the owner, even if Perms says otherwise for the final path.
676 const auto NewDirectoryPerms
= ResolvedPerms
| sys::fs::owner_all
;
679 detail::InMemoryNode
*Node
= Dir
->getChild(Name
);
684 std::unique_ptr
<detail::InMemoryNode
> Child
;
686 Child
.reset(new detail::InMemoryHardLink(P
.str(), *HardLinkTarget
));
688 // Create a new file or directory.
689 Status
Stat(P
.str(), getNextVirtualUniqueID(),
690 llvm::sys::toTimePoint(ModificationTime
), ResolvedUser
,
691 ResolvedGroup
, Buffer
->getBufferSize(), ResolvedType
,
693 if (ResolvedType
== sys::fs::file_type::directory_file
) {
694 Child
.reset(new detail::InMemoryDirectory(std::move(Stat
)));
697 new detail::InMemoryFile(std::move(Stat
), std::move(Buffer
)));
700 Dir
->addChild(Name
, std::move(Child
));
704 // Create a new directory. Use the path up to here.
706 StringRef(Path
.str().begin(), Name
.end() - Path
.str().begin()),
707 getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime
),
708 ResolvedUser
, ResolvedGroup
, 0, sys::fs::file_type::directory_file
,
710 Dir
= cast
<detail::InMemoryDirectory
>(Dir
->addChild(
711 Name
, llvm::make_unique
<detail::InMemoryDirectory
>(std::move(Stat
))));
715 if (auto *NewDir
= dyn_cast
<detail::InMemoryDirectory
>(Node
)) {
718 assert((isa
<detail::InMemoryFile
>(Node
) ||
719 isa
<detail::InMemoryHardLink
>(Node
)) &&
720 "Must be either file, hardlink or directory!");
722 // Trying to insert a directory in place of a file.
726 // Return false only if the new file is different from the existing one.
727 if (auto Link
= dyn_cast
<detail::InMemoryHardLink
>(Node
)) {
728 return Link
->getResolvedFile().getBuffer()->getBuffer() ==
731 return cast
<detail::InMemoryFile
>(Node
)->getBuffer()->getBuffer() ==
737 bool InMemoryFileSystem::addFile(const Twine
&P
, time_t ModificationTime
,
738 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
,
739 Optional
<uint32_t> User
,
740 Optional
<uint32_t> Group
,
741 Optional
<llvm::sys::fs::file_type
> Type
,
742 Optional
<llvm::sys::fs::perms
> Perms
) {
743 return addFile(P
, ModificationTime
, std::move(Buffer
), User
, Group
, Type
,
744 Perms
, /*HardLinkTarget=*/nullptr);
747 bool InMemoryFileSystem::addFileNoOwn(const Twine
&P
, time_t ModificationTime
,
748 llvm::MemoryBuffer
*Buffer
,
749 Optional
<uint32_t> User
,
750 Optional
<uint32_t> Group
,
751 Optional
<llvm::sys::fs::file_type
> Type
,
752 Optional
<llvm::sys::fs::perms
> Perms
) {
753 return addFile(P
, ModificationTime
,
754 llvm::MemoryBuffer::getMemBuffer(
755 Buffer
->getBuffer(), Buffer
->getBufferIdentifier()),
756 std::move(User
), std::move(Group
), std::move(Type
),
760 static ErrorOr
<const detail::InMemoryNode
*>
761 lookupInMemoryNode(const InMemoryFileSystem
&FS
, detail::InMemoryDirectory
*Dir
,
763 SmallString
<128> Path
;
766 // Fix up relative paths. This just prepends the current working directory.
767 std::error_code EC
= FS
.makeAbsolute(Path
);
771 if (FS
.useNormalizedPaths())
772 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
777 auto I
= llvm::sys::path::begin(Path
), E
= llvm::sys::path::end(Path
);
779 detail::InMemoryNode
*Node
= Dir
->getChild(*I
);
782 return errc::no_such_file_or_directory
;
784 // Return the file if it's at the end of the path.
785 if (auto File
= dyn_cast
<detail::InMemoryFile
>(Node
)) {
788 return errc::no_such_file_or_directory
;
791 // If Node is HardLink then return the resolved file.
792 if (auto File
= dyn_cast
<detail::InMemoryHardLink
>(Node
)) {
794 return &File
->getResolvedFile();
795 return errc::no_such_file_or_directory
;
797 // Traverse directories.
798 Dir
= cast
<detail::InMemoryDirectory
>(Node
);
804 bool InMemoryFileSystem::addHardLink(const Twine
&FromPath
,
805 const Twine
&ToPath
) {
806 auto FromNode
= lookupInMemoryNode(*this, Root
.get(), FromPath
);
807 auto ToNode
= lookupInMemoryNode(*this, Root
.get(), ToPath
);
808 // FromPath must not have been added before. ToPath must have been added
809 // before. Resolved ToPath must be a File.
810 if (!ToNode
|| FromNode
|| !isa
<detail::InMemoryFile
>(*ToNode
))
812 return this->addFile(FromPath
, 0, nullptr, None
, None
, None
, None
,
813 cast
<detail::InMemoryFile
>(*ToNode
));
816 llvm::ErrorOr
<Status
> InMemoryFileSystem::status(const Twine
&Path
) {
817 auto Node
= lookupInMemoryNode(*this, Root
.get(), Path
);
819 return detail::getNodeStatus(*Node
, Path
.str());
820 return Node
.getError();
823 llvm::ErrorOr
<std::unique_ptr
<File
>>
824 InMemoryFileSystem::openFileForRead(const Twine
&Path
) {
825 auto Node
= lookupInMemoryNode(*this, Root
.get(), Path
);
827 return Node
.getError();
829 // When we have a file provide a heap-allocated wrapper for the memory buffer
830 // to match the ownership semantics for File.
831 if (auto *F
= dyn_cast
<detail::InMemoryFile
>(*Node
))
832 return std::unique_ptr
<File
>(
833 new detail::InMemoryFileAdaptor(*F
, Path
.str()));
835 // FIXME: errc::not_a_file?
836 return make_error_code(llvm::errc::invalid_argument
);
841 /// Adaptor from InMemoryDir::iterator to directory_iterator.
842 class InMemoryDirIterator
: public llvm::vfs::detail::DirIterImpl
{
843 detail::InMemoryDirectory::const_iterator I
;
844 detail::InMemoryDirectory::const_iterator E
;
845 std::string RequestedDirName
;
847 void setCurrentEntry() {
849 SmallString
<256> Path(RequestedDirName
);
850 llvm::sys::path::append(Path
, I
->second
->getFileName());
851 sys::fs::file_type Type
;
852 switch (I
->second
->getKind()) {
853 case detail::IME_File
:
854 case detail::IME_HardLink
:
855 Type
= sys::fs::file_type::regular_file
;
857 case detail::IME_Directory
:
858 Type
= sys::fs::file_type::directory_file
;
861 CurrentEntry
= directory_entry(Path
.str(), Type
);
863 // When we're at the end, make CurrentEntry invalid and DirIterImpl will
865 CurrentEntry
= directory_entry();
870 InMemoryDirIterator() = default;
872 explicit InMemoryDirIterator(const detail::InMemoryDirectory
&Dir
,
873 std::string RequestedDirName
)
874 : I(Dir
.begin()), E(Dir
.end()),
875 RequestedDirName(std::move(RequestedDirName
)) {
879 std::error_code
increment() override
{
888 directory_iterator
InMemoryFileSystem::dir_begin(const Twine
&Dir
,
889 std::error_code
&EC
) {
890 auto Node
= lookupInMemoryNode(*this, Root
.get(), Dir
);
892 EC
= Node
.getError();
893 return directory_iterator(std::make_shared
<InMemoryDirIterator
>());
896 if (auto *DirNode
= dyn_cast
<detail::InMemoryDirectory
>(*Node
))
897 return directory_iterator(
898 std::make_shared
<InMemoryDirIterator
>(*DirNode
, Dir
.str()));
900 EC
= make_error_code(llvm::errc::not_a_directory
);
901 return directory_iterator(std::make_shared
<InMemoryDirIterator
>());
904 std::error_code
InMemoryFileSystem::setCurrentWorkingDirectory(const Twine
&P
) {
905 SmallString
<128> Path
;
908 // Fix up relative paths. This just prepends the current working directory.
909 std::error_code EC
= makeAbsolute(Path
);
913 if (useNormalizedPaths())
914 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
917 WorkingDirectory
= Path
.str();
922 InMemoryFileSystem::getRealPath(const Twine
&Path
,
923 SmallVectorImpl
<char> &Output
) const {
924 auto CWD
= getCurrentWorkingDirectory();
925 if (!CWD
|| CWD
->empty())
926 return errc::operation_not_permitted
;
927 Path
.toVector(Output
);
928 if (auto EC
= makeAbsolute(Output
))
930 llvm::sys::path::remove_dots(Output
, /*remove_dot_dot=*/true);
934 std::error_code
InMemoryFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
942 //===-----------------------------------------------------------------------===/
943 // RedirectingFileSystem implementation
944 //===-----------------------------------------------------------------------===/
948 enum EntryKind
{ EK_Directory
, EK_File
};
950 /// A single file or directory in the VFS.
956 Entry(EntryKind K
, StringRef Name
) : Kind(K
), Name(Name
) {}
957 virtual ~Entry() = default;
959 StringRef
getName() const { return Name
; }
960 EntryKind
getKind() const { return Kind
; }
963 class RedirectingDirectoryEntry
: public Entry
{
964 std::vector
<std::unique_ptr
<Entry
>> Contents
;
968 RedirectingDirectoryEntry(StringRef Name
,
969 std::vector
<std::unique_ptr
<Entry
>> Contents
,
971 : Entry(EK_Directory
, Name
), Contents(std::move(Contents
)),
973 RedirectingDirectoryEntry(StringRef Name
, Status S
)
974 : Entry(EK_Directory
, Name
), S(std::move(S
)) {}
976 Status
getStatus() { return S
; }
978 void addContent(std::unique_ptr
<Entry
> Content
) {
979 Contents
.push_back(std::move(Content
));
982 Entry
*getLastContent() const { return Contents
.back().get(); }
984 using iterator
= decltype(Contents
)::iterator
;
986 iterator
contents_begin() { return Contents
.begin(); }
987 iterator
contents_end() { return Contents
.end(); }
989 static bool classof(const Entry
*E
) { return E
->getKind() == EK_Directory
; }
992 class RedirectingFileEntry
: public Entry
{
994 enum NameKind
{ NK_NotSet
, NK_External
, NK_Virtual
};
997 std::string ExternalContentsPath
;
1001 RedirectingFileEntry(StringRef Name
, StringRef ExternalContentsPath
,
1003 : Entry(EK_File
, Name
), ExternalContentsPath(ExternalContentsPath
),
1006 StringRef
getExternalContentsPath() const { return ExternalContentsPath
; }
1008 /// whether to use the external path as the name for this file.
1009 bool useExternalName(bool GlobalUseExternalName
) const {
1010 return UseName
== NK_NotSet
? GlobalUseExternalName
1011 : (UseName
== NK_External
);
1014 NameKind
getUseName() const { return UseName
; }
1016 static bool classof(const Entry
*E
) { return E
->getKind() == EK_File
; }
1019 // FIXME: reuse implementation common with OverlayFSDirIterImpl as these
1020 // iterators are conceptually similar.
1021 class VFSFromYamlDirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
1023 RedirectingDirectoryEntry::iterator Current
, End
;
1025 // To handle 'fallthrough' mode we need to iterate at first through
1026 // RedirectingDirectoryEntry and then through ExternalFS. These operations are
1027 // done sequentially, we just need to keep a track of what kind of iteration
1028 // we are currently performing.
1030 /// Flag telling if we should iterate through ExternalFS or stop at the last
1031 /// RedirectingDirectoryEntry::iterator.
1032 bool IterateExternalFS
;
1033 /// Flag telling if we have switched to iterating through ExternalFS.
1034 bool IsExternalFSCurrent
= false;
1035 FileSystem
&ExternalFS
;
1036 directory_iterator ExternalDirIter
;
1037 llvm::StringSet
<> SeenNames
;
1039 /// To combine multiple iterations, different methods are responsible for
1040 /// different iteration steps.
1043 /// Responsible for dispatching between RedirectingDirectoryEntry iteration
1044 /// and ExternalFS iteration.
1045 std::error_code
incrementImpl(bool IsFirstTime
);
1046 /// Responsible for RedirectingDirectoryEntry iteration.
1047 std::error_code
incrementContent(bool IsFirstTime
);
1048 /// Responsible for ExternalFS iteration.
1049 std::error_code
incrementExternal();
1053 VFSFromYamlDirIterImpl(const Twine
&Path
,
1054 RedirectingDirectoryEntry::iterator Begin
,
1055 RedirectingDirectoryEntry::iterator End
,
1056 bool IterateExternalFS
, FileSystem
&ExternalFS
,
1057 std::error_code
&EC
);
1059 std::error_code
increment() override
;
1062 /// A virtual file system parsed from a YAML file.
1064 /// Currently, this class allows creating virtual directories and mapping
1065 /// virtual file paths to existing external files, available in \c ExternalFS.
1067 /// The basic structure of the parsed file is:
1070 /// 'version': <version number>,
1071 /// <optional configuration>
1073 /// <directory entries>
1078 /// All configuration options are optional.
1079 /// 'case-sensitive': <boolean, default=true>
1080 /// 'use-external-names': <boolean, default=true>
1081 /// 'overlay-relative': <boolean, default=false>
1082 /// 'fallthrough': <boolean, default=true>
1084 /// Virtual directories are represented as
1087 /// 'type': 'directory',
1088 /// 'name': <string>,
1089 /// 'contents': [ <file or directory entries> ]
1093 /// The default attributes for virtual directories are:
1095 /// MTime = now() when created
1097 /// User = Group = 0
1099 /// UniqueID = unspecified unique value
1102 /// Re-mapped files are represented as
1106 /// 'name': <string>,
1107 /// 'use-external-name': <boolean> # Optional
1108 /// 'external-contents': <path to external file>
1112 /// and inherit their attributes from the external contents.
1114 /// In both cases, the 'name' field may contain multiple path components (e.g.
1115 /// /path/to/file). However, any directory that contains more than one child
1116 /// must be uniquely represented by a directory entry.
1117 class RedirectingFileSystem
: public vfs::FileSystem
{
1118 friend class RedirectingFileSystemParser
;
1120 /// The root(s) of the virtual file system.
1121 std::vector
<std::unique_ptr
<Entry
>> Roots
;
1123 /// The file system to use for external references.
1124 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
;
1126 /// If IsRelativeOverlay is set, this represents the directory
1127 /// path that should be prefixed to each 'external-contents' entry
1128 /// when reading from YAML files.
1129 std::string ExternalContentsPrefixDir
;
1131 /// @name Configuration
1134 /// Whether to perform case-sensitive comparisons.
1136 /// Currently, case-insensitive matching only works correctly with ASCII.
1137 bool CaseSensitive
= true;
1139 /// IsRelativeOverlay marks whether a ExternalContentsPrefixDir path must
1140 /// be prefixed in every 'external-contents' when reading from YAML files.
1141 bool IsRelativeOverlay
= false;
1143 /// Whether to use to use the value of 'external-contents' for the
1144 /// names of files. This global value is overridable on a per-file basis.
1145 bool UseExternalNames
= true;
1147 /// Whether to attempt a file lookup in external file system after it wasn't
1149 bool IsFallthrough
= true;
1152 /// Virtual file paths and external files could be canonicalized without "..",
1153 /// "." and "./" in their paths. FIXME: some unittests currently fail on
1154 /// win32 when using remove_dots and remove_leading_dotslash on paths.
1155 bool UseCanonicalizedPaths
=
1163 RedirectingFileSystem(IntrusiveRefCntPtr
<FileSystem
> ExternalFS
)
1164 : ExternalFS(std::move(ExternalFS
)) {}
1166 /// Looks up the path <tt>[Start, End)</tt> in \p From, possibly
1167 /// recursing into the contents of \p From if it is a directory.
1168 ErrorOr
<Entry
*> lookupPath(sys::path::const_iterator Start
,
1169 sys::path::const_iterator End
, Entry
*From
) const;
1171 /// Get the status of a given an \c Entry.
1172 ErrorOr
<Status
> status(const Twine
&Path
, Entry
*E
);
1175 /// Looks up \p Path in \c Roots.
1176 ErrorOr
<Entry
*> lookupPath(const Twine
&Path
) const;
1178 /// Parses \p Buffer, which is expected to be in YAML format and
1179 /// returns a virtual file system representing its contents.
1180 static RedirectingFileSystem
*
1181 create(std::unique_ptr
<MemoryBuffer
> Buffer
,
1182 SourceMgr::DiagHandlerTy DiagHandler
, StringRef YAMLFilePath
,
1183 void *DiagContext
, IntrusiveRefCntPtr
<FileSystem
> ExternalFS
);
1185 ErrorOr
<Status
> status(const Twine
&Path
) override
;
1186 ErrorOr
<std::unique_ptr
<File
>> openFileForRead(const Twine
&Path
) override
;
1188 std::error_code
getRealPath(const Twine
&Path
,
1189 SmallVectorImpl
<char> &Output
) const override
;
1191 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
{
1192 return ExternalFS
->getCurrentWorkingDirectory();
1195 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
{
1196 return ExternalFS
->setCurrentWorkingDirectory(Path
);
1199 std::error_code
isLocal(const Twine
&Path
, bool &Result
) override
{
1200 return ExternalFS
->isLocal(Path
, Result
);
1203 directory_iterator
dir_begin(const Twine
&Dir
, std::error_code
&EC
) override
{
1204 ErrorOr
<Entry
*> E
= lookupPath(Dir
);
1207 if (IsFallthrough
&& EC
== errc::no_such_file_or_directory
)
1208 return ExternalFS
->dir_begin(Dir
, EC
);
1211 ErrorOr
<Status
> S
= status(Dir
, *E
);
1216 if (!S
->isDirectory()) {
1217 EC
= std::error_code(static_cast<int>(errc::not_a_directory
),
1218 std::system_category());
1222 auto *D
= cast
<RedirectingDirectoryEntry
>(*E
);
1223 return directory_iterator(std::make_shared
<VFSFromYamlDirIterImpl
>(
1224 Dir
, D
->contents_begin(), D
->contents_end(),
1225 /*IterateExternalFS=*/IsFallthrough
, *ExternalFS
, EC
));
1228 void setExternalContentsPrefixDir(StringRef PrefixDir
) {
1229 ExternalContentsPrefixDir
= PrefixDir
.str();
1232 StringRef
getExternalContentsPrefixDir() const {
1233 return ExternalContentsPrefixDir
;
1236 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
1237 LLVM_DUMP_METHOD
void dump() const {
1238 for (const auto &Root
: Roots
)
1239 dumpEntry(Root
.get());
1242 LLVM_DUMP_METHOD
void dumpEntry(Entry
*E
, int NumSpaces
= 0) const {
1243 StringRef Name
= E
->getName();
1244 for (int i
= 0, e
= NumSpaces
; i
< e
; ++i
)
1246 dbgs() << "'" << Name
.str().c_str() << "'"
1249 if (E
->getKind() == EK_Directory
) {
1250 auto *DE
= dyn_cast
<RedirectingDirectoryEntry
>(E
);
1251 assert(DE
&& "Should be a directory");
1253 for (std::unique_ptr
<Entry
> &SubEntry
:
1254 llvm::make_range(DE
->contents_begin(), DE
->contents_end()))
1255 dumpEntry(SubEntry
.get(), NumSpaces
+ 2);
1261 /// A helper class to hold the common YAML parsing state.
1262 class RedirectingFileSystemParser
{
1263 yaml::Stream
&Stream
;
1265 void error(yaml::Node
*N
, const Twine
&Msg
) { Stream
.printError(N
, Msg
); }
1268 bool parseScalarString(yaml::Node
*N
, StringRef
&Result
,
1269 SmallVectorImpl
<char> &Storage
) {
1270 const auto *S
= dyn_cast
<yaml::ScalarNode
>(N
);
1273 error(N
, "expected string");
1276 Result
= S
->getValue(Storage
);
1281 bool parseScalarBool(yaml::Node
*N
, bool &Result
) {
1282 SmallString
<5> Storage
;
1284 if (!parseScalarString(N
, Value
, Storage
))
1287 if (Value
.equals_lower("true") || Value
.equals_lower("on") ||
1288 Value
.equals_lower("yes") || Value
== "1") {
1291 } else if (Value
.equals_lower("false") || Value
.equals_lower("off") ||
1292 Value
.equals_lower("no") || Value
== "0") {
1297 error(N
, "expected boolean value");
1305 KeyStatus(bool Required
= false) : Required(Required
) {}
1308 using KeyStatusPair
= std::pair
<StringRef
, KeyStatus
>;
1311 bool checkDuplicateOrUnknownKey(yaml::Node
*KeyNode
, StringRef Key
,
1312 DenseMap
<StringRef
, KeyStatus
> &Keys
) {
1313 if (!Keys
.count(Key
)) {
1314 error(KeyNode
, "unknown key");
1317 KeyStatus
&S
= Keys
[Key
];
1319 error(KeyNode
, Twine("duplicate key '") + Key
+ "'");
1327 bool checkMissingKeys(yaml::Node
*Obj
, DenseMap
<StringRef
, KeyStatus
> &Keys
) {
1328 for (const auto &I
: Keys
) {
1329 if (I
.second
.Required
&& !I
.second
.Seen
) {
1330 error(Obj
, Twine("missing key '") + I
.first
+ "'");
1337 Entry
*lookupOrCreateEntry(RedirectingFileSystem
*FS
, StringRef Name
,
1338 Entry
*ParentEntry
= nullptr) {
1339 if (!ParentEntry
) { // Look for a existent root
1340 for (const auto &Root
: FS
->Roots
) {
1341 if (Name
.equals(Root
->getName())) {
1342 ParentEntry
= Root
.get();
1346 } else { // Advance to the next component
1347 auto *DE
= dyn_cast
<RedirectingDirectoryEntry
>(ParentEntry
);
1348 for (std::unique_ptr
<Entry
> &Content
:
1349 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
1350 auto *DirContent
= dyn_cast
<RedirectingDirectoryEntry
>(Content
.get());
1351 if (DirContent
&& Name
.equals(Content
->getName()))
1356 // ... or create a new one
1357 std::unique_ptr
<Entry
> E
= llvm::make_unique
<RedirectingDirectoryEntry
>(
1359 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1360 0, 0, 0, file_type::directory_file
, sys::fs::all_all
));
1362 if (!ParentEntry
) { // Add a new root to the overlay
1363 FS
->Roots
.push_back(std::move(E
));
1364 ParentEntry
= FS
->Roots
.back().get();
1368 auto *DE
= dyn_cast
<RedirectingDirectoryEntry
>(ParentEntry
);
1369 DE
->addContent(std::move(E
));
1370 return DE
->getLastContent();
1373 void uniqueOverlayTree(RedirectingFileSystem
*FS
, Entry
*SrcE
,
1374 Entry
*NewParentE
= nullptr) {
1375 StringRef Name
= SrcE
->getName();
1376 switch (SrcE
->getKind()) {
1377 case EK_Directory
: {
1378 auto *DE
= dyn_cast
<RedirectingDirectoryEntry
>(SrcE
);
1379 assert(DE
&& "Must be a directory");
1380 // Empty directories could be present in the YAML as a way to
1381 // describe a file for a current directory after some of its subdir
1382 // is parsed. This only leads to redundant walks, ignore it.
1384 NewParentE
= lookupOrCreateEntry(FS
, Name
, NewParentE
);
1385 for (std::unique_ptr
<Entry
> &SubEntry
:
1386 llvm::make_range(DE
->contents_begin(), DE
->contents_end()))
1387 uniqueOverlayTree(FS
, SubEntry
.get(), NewParentE
);
1391 auto *FE
= dyn_cast
<RedirectingFileEntry
>(SrcE
);
1392 assert(FE
&& "Must be a file");
1393 assert(NewParentE
&& "Parent entry must exist");
1394 auto *DE
= dyn_cast
<RedirectingDirectoryEntry
>(NewParentE
);
1395 DE
->addContent(llvm::make_unique
<RedirectingFileEntry
>(
1396 Name
, FE
->getExternalContentsPath(), FE
->getUseName()));
1402 std::unique_ptr
<Entry
> parseEntry(yaml::Node
*N
, RedirectingFileSystem
*FS
,
1404 auto *M
= dyn_cast
<yaml::MappingNode
>(N
);
1406 error(N
, "expected mapping node for file or directory entry");
1410 KeyStatusPair Fields
[] = {
1411 KeyStatusPair("name", true),
1412 KeyStatusPair("type", true),
1413 KeyStatusPair("contents", false),
1414 KeyStatusPair("external-contents", false),
1415 KeyStatusPair("use-external-name", false),
1418 DenseMap
<StringRef
, KeyStatus
> Keys(std::begin(Fields
), std::end(Fields
));
1420 bool HasContents
= false; // external or otherwise
1421 std::vector
<std::unique_ptr
<Entry
>> EntryArrayContents
;
1422 std::string ExternalContentsPath
;
1424 yaml::Node
*NameValueNode
;
1425 auto UseExternalName
= RedirectingFileEntry::NK_NotSet
;
1428 for (auto &I
: *M
) {
1430 // Reuse the buffer for key and value, since we don't look at key after
1432 SmallString
<256> Buffer
;
1433 if (!parseScalarString(I
.getKey(), Key
, Buffer
))
1436 if (!checkDuplicateOrUnknownKey(I
.getKey(), Key
, Keys
))
1440 if (Key
== "name") {
1441 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1444 NameValueNode
= I
.getValue();
1445 if (FS
->UseCanonicalizedPaths
) {
1446 SmallString
<256> Path(Value
);
1447 // Guarantee that old YAML files containing paths with ".." and "."
1448 // are properly canonicalized before read into the VFS.
1449 Path
= sys::path::remove_leading_dotslash(Path
);
1450 sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
1455 } else if (Key
== "type") {
1456 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1458 if (Value
== "file")
1460 else if (Value
== "directory")
1461 Kind
= EK_Directory
;
1463 error(I
.getValue(), "unknown value for 'type'");
1466 } else if (Key
== "contents") {
1469 "entry already has 'contents' or 'external-contents'");
1473 auto *Contents
= dyn_cast
<yaml::SequenceNode
>(I
.getValue());
1475 // FIXME: this is only for directories, what about files?
1476 error(I
.getValue(), "expected array");
1480 for (auto &I
: *Contents
) {
1481 if (std::unique_ptr
<Entry
> E
=
1482 parseEntry(&I
, FS
, /*IsRootEntry*/ false))
1483 EntryArrayContents
.push_back(std::move(E
));
1487 } else if (Key
== "external-contents") {
1490 "entry already has 'contents' or 'external-contents'");
1494 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1497 SmallString
<256> FullPath
;
1498 if (FS
->IsRelativeOverlay
) {
1499 FullPath
= FS
->getExternalContentsPrefixDir();
1500 assert(!FullPath
.empty() &&
1501 "External contents prefix directory must exist");
1502 llvm::sys::path::append(FullPath
, Value
);
1507 if (FS
->UseCanonicalizedPaths
) {
1508 // Guarantee that old YAML files containing paths with ".." and "."
1509 // are properly canonicalized before read into the VFS.
1510 FullPath
= sys::path::remove_leading_dotslash(FullPath
);
1511 sys::path::remove_dots(FullPath
, /*remove_dot_dot=*/true);
1513 ExternalContentsPath
= FullPath
.str();
1514 } else if (Key
== "use-external-name") {
1516 if (!parseScalarBool(I
.getValue(), Val
))
1518 UseExternalName
= Val
? RedirectingFileEntry::NK_External
1519 : RedirectingFileEntry::NK_Virtual
;
1521 llvm_unreachable("key missing from Keys");
1525 if (Stream
.failed())
1528 // check for missing keys
1530 error(N
, "missing key 'contents' or 'external-contents'");
1533 if (!checkMissingKeys(N
, Keys
))
1536 // check invalid configuration
1537 if (Kind
== EK_Directory
&&
1538 UseExternalName
!= RedirectingFileEntry::NK_NotSet
) {
1539 error(N
, "'use-external-name' is not supported for directories");
1543 if (IsRootEntry
&& !sys::path::is_absolute(Name
)) {
1544 assert(NameValueNode
&& "Name presence should be checked earlier");
1545 error(NameValueNode
,
1546 "entry with relative path at the root level is not discoverable");
1550 // Remove trailing slash(es), being careful not to remove the root path
1551 StringRef
Trimmed(Name
);
1552 size_t RootPathLen
= sys::path::root_path(Trimmed
).size();
1553 while (Trimmed
.size() > RootPathLen
&&
1554 sys::path::is_separator(Trimmed
.back()))
1555 Trimmed
= Trimmed
.slice(0, Trimmed
.size() - 1);
1556 // Get the last component
1557 StringRef LastComponent
= sys::path::filename(Trimmed
);
1559 std::unique_ptr
<Entry
> Result
;
1562 Result
= llvm::make_unique
<RedirectingFileEntry
>(
1563 LastComponent
, std::move(ExternalContentsPath
), UseExternalName
);
1566 Result
= llvm::make_unique
<RedirectingDirectoryEntry
>(
1567 LastComponent
, std::move(EntryArrayContents
),
1568 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1569 0, 0, 0, file_type::directory_file
, sys::fs::all_all
));
1573 StringRef Parent
= sys::path::parent_path(Trimmed
);
1577 // if 'name' contains multiple components, create implicit directory entries
1578 for (sys::path::reverse_iterator I
= sys::path::rbegin(Parent
),
1579 E
= sys::path::rend(Parent
);
1581 std::vector
<std::unique_ptr
<Entry
>> Entries
;
1582 Entries
.push_back(std::move(Result
));
1583 Result
= llvm::make_unique
<RedirectingDirectoryEntry
>(
1584 *I
, std::move(Entries
),
1585 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1586 0, 0, 0, file_type::directory_file
, sys::fs::all_all
));
1592 RedirectingFileSystemParser(yaml::Stream
&S
) : Stream(S
) {}
1595 bool parse(yaml::Node
*Root
, RedirectingFileSystem
*FS
) {
1596 auto *Top
= dyn_cast
<yaml::MappingNode
>(Root
);
1598 error(Root
, "expected mapping node");
1602 KeyStatusPair Fields
[] = {
1603 KeyStatusPair("version", true),
1604 KeyStatusPair("case-sensitive", false),
1605 KeyStatusPair("use-external-names", false),
1606 KeyStatusPair("overlay-relative", false),
1607 KeyStatusPair("fallthrough", false),
1608 KeyStatusPair("roots", true),
1611 DenseMap
<StringRef
, KeyStatus
> Keys(std::begin(Fields
), std::end(Fields
));
1612 std::vector
<std::unique_ptr
<Entry
>> RootEntries
;
1614 // Parse configuration and 'roots'
1615 for (auto &I
: *Top
) {
1616 SmallString
<10> KeyBuffer
;
1618 if (!parseScalarString(I
.getKey(), Key
, KeyBuffer
))
1621 if (!checkDuplicateOrUnknownKey(I
.getKey(), Key
, Keys
))
1624 if (Key
== "roots") {
1625 auto *Roots
= dyn_cast
<yaml::SequenceNode
>(I
.getValue());
1627 error(I
.getValue(), "expected array");
1631 for (auto &I
: *Roots
) {
1632 if (std::unique_ptr
<Entry
> E
=
1633 parseEntry(&I
, FS
, /*IsRootEntry*/ true))
1634 RootEntries
.push_back(std::move(E
));
1638 } else if (Key
== "version") {
1639 StringRef VersionString
;
1640 SmallString
<4> Storage
;
1641 if (!parseScalarString(I
.getValue(), VersionString
, Storage
))
1644 if (VersionString
.getAsInteger
<int>(10, Version
)) {
1645 error(I
.getValue(), "expected integer");
1649 error(I
.getValue(), "invalid version number");
1653 error(I
.getValue(), "version mismatch, expected 0");
1656 } else if (Key
== "case-sensitive") {
1657 if (!parseScalarBool(I
.getValue(), FS
->CaseSensitive
))
1659 } else if (Key
== "overlay-relative") {
1660 if (!parseScalarBool(I
.getValue(), FS
->IsRelativeOverlay
))
1662 } else if (Key
== "use-external-names") {
1663 if (!parseScalarBool(I
.getValue(), FS
->UseExternalNames
))
1665 } else if (Key
== "fallthrough") {
1666 if (!parseScalarBool(I
.getValue(), FS
->IsFallthrough
))
1669 llvm_unreachable("key missing from Keys");
1673 if (Stream
.failed())
1676 if (!checkMissingKeys(Top
, Keys
))
1679 // Now that we sucessefully parsed the YAML file, canonicalize the internal
1680 // representation to a proper directory tree so that we can search faster
1682 for (auto &E
: RootEntries
)
1683 uniqueOverlayTree(FS
, E
.get());
1691 RedirectingFileSystem
*
1692 RedirectingFileSystem::create(std::unique_ptr
<MemoryBuffer
> Buffer
,
1693 SourceMgr::DiagHandlerTy DiagHandler
,
1694 StringRef YAMLFilePath
, void *DiagContext
,
1695 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
1697 yaml::Stream
Stream(Buffer
->getMemBufferRef(), SM
);
1699 SM
.setDiagHandler(DiagHandler
, DiagContext
);
1700 yaml::document_iterator DI
= Stream
.begin();
1701 yaml::Node
*Root
= DI
->getRoot();
1702 if (DI
== Stream
.end() || !Root
) {
1703 SM
.PrintMessage(SMLoc(), SourceMgr::DK_Error
, "expected root node");
1707 RedirectingFileSystemParser
P(Stream
);
1709 std::unique_ptr
<RedirectingFileSystem
> FS(
1710 new RedirectingFileSystem(std::move(ExternalFS
)));
1712 if (!YAMLFilePath
.empty()) {
1713 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
1714 // to each 'external-contents' path.
1717 // -ivfsoverlay dummy.cache/vfs/vfs.yaml
1719 // FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs
1721 SmallString
<256> OverlayAbsDir
= sys::path::parent_path(YAMLFilePath
);
1722 std::error_code EC
= llvm::sys::fs::make_absolute(OverlayAbsDir
);
1723 assert(!EC
&& "Overlay dir final path must be absolute");
1725 FS
->setExternalContentsPrefixDir(OverlayAbsDir
);
1728 if (!P
.parse(Root
, FS
.get()))
1731 return FS
.release();
1734 ErrorOr
<Entry
*> RedirectingFileSystem::lookupPath(const Twine
&Path_
) const {
1735 SmallString
<256> Path
;
1736 Path_
.toVector(Path
);
1738 // Handle relative paths
1739 if (std::error_code EC
= makeAbsolute(Path
))
1742 // Canonicalize path by removing ".", "..", "./", etc components. This is
1743 // a VFS request, do bot bother about symlinks in the path components
1744 // but canonicalize in order to perform the correct entry search.
1745 if (UseCanonicalizedPaths
) {
1746 Path
= sys::path::remove_leading_dotslash(Path
);
1747 sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
1751 return make_error_code(llvm::errc::invalid_argument
);
1753 sys::path::const_iterator Start
= sys::path::begin(Path
);
1754 sys::path::const_iterator End
= sys::path::end(Path
);
1755 for (const auto &Root
: Roots
) {
1756 ErrorOr
<Entry
*> Result
= lookupPath(Start
, End
, Root
.get());
1757 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
1760 return make_error_code(llvm::errc::no_such_file_or_directory
);
1764 RedirectingFileSystem::lookupPath(sys::path::const_iterator Start
,
1765 sys::path::const_iterator End
,
1766 Entry
*From
) const {
1768 assert(!isTraversalComponent(*Start
) &&
1769 !isTraversalComponent(From
->getName()) &&
1770 "Paths should not contain traversal components");
1772 // FIXME: this is here to support windows, remove it once canonicalized
1773 // paths become globally default.
1774 if (Start
->equals("."))
1778 StringRef FromName
= From
->getName();
1780 // Forward the search to the next component in case this is an empty one.
1781 if (!FromName
.empty()) {
1782 if (CaseSensitive
? !Start
->equals(FromName
)
1783 : !Start
->equals_lower(FromName
))
1785 return make_error_code(llvm::errc::no_such_file_or_directory
);
1795 auto *DE
= dyn_cast
<RedirectingDirectoryEntry
>(From
);
1797 return make_error_code(llvm::errc::not_a_directory
);
1799 for (const std::unique_ptr
<Entry
> &DirEntry
:
1800 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
1801 ErrorOr
<Entry
*> Result
= lookupPath(Start
, End
, DirEntry
.get());
1802 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
1805 return make_error_code(llvm::errc::no_such_file_or_directory
);
1808 static Status
getRedirectedFileStatus(const Twine
&Path
, bool UseExternalNames
,
1809 Status ExternalStatus
) {
1810 Status S
= ExternalStatus
;
1811 if (!UseExternalNames
)
1812 S
= Status::copyWithNewName(S
, Path
.str());
1813 S
.IsVFSMapped
= true;
1817 ErrorOr
<Status
> RedirectingFileSystem::status(const Twine
&Path
, Entry
*E
) {
1818 assert(E
!= nullptr);
1819 if (auto *F
= dyn_cast
<RedirectingFileEntry
>(E
)) {
1820 ErrorOr
<Status
> S
= ExternalFS
->status(F
->getExternalContentsPath());
1821 assert(!S
|| S
->getName() == F
->getExternalContentsPath());
1823 return getRedirectedFileStatus(Path
, F
->useExternalName(UseExternalNames
),
1826 } else { // directory
1827 auto *DE
= cast
<RedirectingDirectoryEntry
>(E
);
1828 return Status::copyWithNewName(DE
->getStatus(), Path
.str());
1832 ErrorOr
<Status
> RedirectingFileSystem::status(const Twine
&Path
) {
1833 ErrorOr
<Entry
*> Result
= lookupPath(Path
);
1835 if (IsFallthrough
&&
1836 Result
.getError() == llvm::errc::no_such_file_or_directory
) {
1837 return ExternalFS
->status(Path
);
1839 return Result
.getError();
1841 return status(Path
, *Result
);
1846 /// Provide a file wrapper with an overriden status.
1847 class FileWithFixedStatus
: public File
{
1848 std::unique_ptr
<File
> InnerFile
;
1852 FileWithFixedStatus(std::unique_ptr
<File
> InnerFile
, Status S
)
1853 : InnerFile(std::move(InnerFile
)), S(std::move(S
)) {}
1855 ErrorOr
<Status
> status() override
{ return S
; }
1856 ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
1858 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
1859 bool IsVolatile
) override
{
1860 return InnerFile
->getBuffer(Name
, FileSize
, RequiresNullTerminator
,
1864 std::error_code
close() override
{ return InnerFile
->close(); }
1869 ErrorOr
<std::unique_ptr
<File
>>
1870 RedirectingFileSystem::openFileForRead(const Twine
&Path
) {
1871 ErrorOr
<Entry
*> E
= lookupPath(Path
);
1873 if (IsFallthrough
&&
1874 E
.getError() == llvm::errc::no_such_file_or_directory
) {
1875 return ExternalFS
->openFileForRead(Path
);
1877 return E
.getError();
1880 auto *F
= dyn_cast
<RedirectingFileEntry
>(*E
);
1881 if (!F
) // FIXME: errc::not_a_file?
1882 return make_error_code(llvm::errc::invalid_argument
);
1884 auto Result
= ExternalFS
->openFileForRead(F
->getExternalContentsPath());
1888 auto ExternalStatus
= (*Result
)->status();
1889 if (!ExternalStatus
)
1890 return ExternalStatus
.getError();
1892 // FIXME: Update the status with the name and VFSMapped.
1893 Status S
= getRedirectedFileStatus(Path
, F
->useExternalName(UseExternalNames
),
1895 return std::unique_ptr
<File
>(
1896 llvm::make_unique
<FileWithFixedStatus
>(std::move(*Result
), S
));
1900 RedirectingFileSystem::getRealPath(const Twine
&Path
,
1901 SmallVectorImpl
<char> &Output
) const {
1902 ErrorOr
<Entry
*> Result
= lookupPath(Path
);
1904 if (IsFallthrough
&&
1905 Result
.getError() == llvm::errc::no_such_file_or_directory
) {
1906 return ExternalFS
->getRealPath(Path
, Output
);
1908 return Result
.getError();
1911 if (auto *F
= dyn_cast
<RedirectingFileEntry
>(*Result
)) {
1912 return ExternalFS
->getRealPath(F
->getExternalContentsPath(), Output
);
1914 // Even if there is a directory entry, fall back to ExternalFS if allowed,
1915 // because directories don't have a single external contents path.
1916 return IsFallthrough
? ExternalFS
->getRealPath(Path
, Output
)
1917 : llvm::errc::invalid_argument
;
1920 IntrusiveRefCntPtr
<FileSystem
>
1921 vfs::getVFSFromYAML(std::unique_ptr
<MemoryBuffer
> Buffer
,
1922 SourceMgr::DiagHandlerTy DiagHandler
,
1923 StringRef YAMLFilePath
, void *DiagContext
,
1924 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
1925 return RedirectingFileSystem::create(std::move(Buffer
), DiagHandler
,
1926 YAMLFilePath
, DiagContext
,
1927 std::move(ExternalFS
));
1930 static void getVFSEntries(Entry
*SrcE
, SmallVectorImpl
<StringRef
> &Path
,
1931 SmallVectorImpl
<YAMLVFSEntry
> &Entries
) {
1932 auto Kind
= SrcE
->getKind();
1933 if (Kind
== EK_Directory
) {
1934 auto *DE
= dyn_cast
<RedirectingDirectoryEntry
>(SrcE
);
1935 assert(DE
&& "Must be a directory");
1936 for (std::unique_ptr
<Entry
> &SubEntry
:
1937 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
1938 Path
.push_back(SubEntry
->getName());
1939 getVFSEntries(SubEntry
.get(), Path
, Entries
);
1945 assert(Kind
== EK_File
&& "Must be a EK_File");
1946 auto *FE
= dyn_cast
<RedirectingFileEntry
>(SrcE
);
1947 assert(FE
&& "Must be a file");
1948 SmallString
<128> VPath
;
1949 for (auto &Comp
: Path
)
1950 llvm::sys::path::append(VPath
, Comp
);
1951 Entries
.push_back(YAMLVFSEntry(VPath
.c_str(), FE
->getExternalContentsPath()));
1954 void vfs::collectVFSFromYAML(std::unique_ptr
<MemoryBuffer
> Buffer
,
1955 SourceMgr::DiagHandlerTy DiagHandler
,
1956 StringRef YAMLFilePath
,
1957 SmallVectorImpl
<YAMLVFSEntry
> &CollectedEntries
,
1959 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
1960 RedirectingFileSystem
*VFS
= RedirectingFileSystem::create(
1961 std::move(Buffer
), DiagHandler
, YAMLFilePath
, DiagContext
,
1962 std::move(ExternalFS
));
1963 ErrorOr
<Entry
*> RootE
= VFS
->lookupPath("/");
1966 SmallVector
<StringRef
, 8> Components
;
1967 Components
.push_back("/");
1968 getVFSEntries(*RootE
, Components
, CollectedEntries
);
1971 UniqueID
vfs::getNextVirtualUniqueID() {
1972 static std::atomic
<unsigned> UID
;
1973 unsigned ID
= ++UID
;
1974 // The following assumes that uint64_t max will never collide with a real
1975 // dev_t value from the OS.
1976 return UniqueID(std::numeric_limits
<uint64_t>::max(), ID
);
1979 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath
, StringRef RealPath
) {
1980 assert(sys::path::is_absolute(VirtualPath
) && "virtual path not absolute");
1981 assert(sys::path::is_absolute(RealPath
) && "real path not absolute");
1982 assert(!pathHasTraversal(VirtualPath
) && "path traversal is not supported");
1983 Mappings
.emplace_back(VirtualPath
, RealPath
);
1989 llvm::raw_ostream
&OS
;
1990 SmallVector
<StringRef
, 16> DirStack
;
1992 unsigned getDirIndent() { return 4 * DirStack
.size(); }
1993 unsigned getFileIndent() { return 4 * (DirStack
.size() + 1); }
1994 bool containedIn(StringRef Parent
, StringRef Path
);
1995 StringRef
containedPart(StringRef Parent
, StringRef Path
);
1996 void startDirectory(StringRef Path
);
1997 void endDirectory();
1998 void writeEntry(StringRef VPath
, StringRef RPath
);
2001 JSONWriter(llvm::raw_ostream
&OS
) : OS(OS
) {}
2003 void write(ArrayRef
<YAMLVFSEntry
> Entries
, Optional
<bool> UseExternalNames
,
2004 Optional
<bool> IsCaseSensitive
, Optional
<bool> IsOverlayRelative
,
2005 StringRef OverlayDir
);
2010 bool JSONWriter::containedIn(StringRef Parent
, StringRef Path
) {
2011 using namespace llvm::sys
;
2013 // Compare each path component.
2014 auto IParent
= path::begin(Parent
), EParent
= path::end(Parent
);
2015 for (auto IChild
= path::begin(Path
), EChild
= path::end(Path
);
2016 IParent
!= EParent
&& IChild
!= EChild
; ++IParent
, ++IChild
) {
2017 if (*IParent
!= *IChild
)
2020 // Have we exhausted the parent path?
2021 return IParent
== EParent
;
2024 StringRef
JSONWriter::containedPart(StringRef Parent
, StringRef Path
) {
2025 assert(!Parent
.empty());
2026 assert(containedIn(Parent
, Path
));
2027 return Path
.slice(Parent
.size() + 1, StringRef::npos
);
2030 void JSONWriter::startDirectory(StringRef Path
) {
2032 DirStack
.empty() ? Path
: containedPart(DirStack
.back(), Path
);
2033 DirStack
.push_back(Path
);
2034 unsigned Indent
= getDirIndent();
2035 OS
.indent(Indent
) << "{\n";
2036 OS
.indent(Indent
+ 2) << "'type': 'directory',\n";
2037 OS
.indent(Indent
+ 2) << "'name': \"" << llvm::yaml::escape(Name
) << "\",\n";
2038 OS
.indent(Indent
+ 2) << "'contents': [\n";
2041 void JSONWriter::endDirectory() {
2042 unsigned Indent
= getDirIndent();
2043 OS
.indent(Indent
+ 2) << "]\n";
2044 OS
.indent(Indent
) << "}";
2046 DirStack
.pop_back();
2049 void JSONWriter::writeEntry(StringRef VPath
, StringRef RPath
) {
2050 unsigned Indent
= getFileIndent();
2051 OS
.indent(Indent
) << "{\n";
2052 OS
.indent(Indent
+ 2) << "'type': 'file',\n";
2053 OS
.indent(Indent
+ 2) << "'name': \"" << llvm::yaml::escape(VPath
) << "\",\n";
2054 OS
.indent(Indent
+ 2) << "'external-contents': \""
2055 << llvm::yaml::escape(RPath
) << "\"\n";
2056 OS
.indent(Indent
) << "}";
2059 void JSONWriter::write(ArrayRef
<YAMLVFSEntry
> Entries
,
2060 Optional
<bool> UseExternalNames
,
2061 Optional
<bool> IsCaseSensitive
,
2062 Optional
<bool> IsOverlayRelative
,
2063 StringRef OverlayDir
) {
2064 using namespace llvm::sys
;
2068 if (IsCaseSensitive
.hasValue())
2069 OS
<< " 'case-sensitive': '"
2070 << (IsCaseSensitive
.getValue() ? "true" : "false") << "',\n";
2071 if (UseExternalNames
.hasValue())
2072 OS
<< " 'use-external-names': '"
2073 << (UseExternalNames
.getValue() ? "true" : "false") << "',\n";
2074 bool UseOverlayRelative
= false;
2075 if (IsOverlayRelative
.hasValue()) {
2076 UseOverlayRelative
= IsOverlayRelative
.getValue();
2077 OS
<< " 'overlay-relative': '" << (UseOverlayRelative
? "true" : "false")
2080 OS
<< " 'roots': [\n";
2082 if (!Entries
.empty()) {
2083 const YAMLVFSEntry
&Entry
= Entries
.front();
2084 startDirectory(path::parent_path(Entry
.VPath
));
2086 StringRef RPath
= Entry
.RPath
;
2087 if (UseOverlayRelative
) {
2088 unsigned OverlayDirLen
= OverlayDir
.size();
2089 assert(RPath
.substr(0, OverlayDirLen
) == OverlayDir
&&
2090 "Overlay dir must be contained in RPath");
2091 RPath
= RPath
.slice(OverlayDirLen
, RPath
.size());
2094 writeEntry(path::filename(Entry
.VPath
), RPath
);
2096 for (const auto &Entry
: Entries
.slice(1)) {
2097 StringRef Dir
= path::parent_path(Entry
.VPath
);
2098 if (Dir
== DirStack
.back())
2101 while (!DirStack
.empty() && !containedIn(DirStack
.back(), Dir
)) {
2106 startDirectory(Dir
);
2108 StringRef RPath
= Entry
.RPath
;
2109 if (UseOverlayRelative
) {
2110 unsigned OverlayDirLen
= OverlayDir
.size();
2111 assert(RPath
.substr(0, OverlayDirLen
) == OverlayDir
&&
2112 "Overlay dir must be contained in RPath");
2113 RPath
= RPath
.slice(OverlayDirLen
, RPath
.size());
2115 writeEntry(path::filename(Entry
.VPath
), RPath
);
2118 while (!DirStack
.empty()) {
2129 void YAMLVFSWriter::write(llvm::raw_ostream
&OS
) {
2130 llvm::sort(Mappings
, [](const YAMLVFSEntry
&LHS
, const YAMLVFSEntry
&RHS
) {
2131 return LHS
.VPath
< RHS
.VPath
;
2134 JSONWriter(OS
).write(Mappings
, UseExternalNames
, IsCaseSensitive
,
2135 IsOverlayRelative
, OverlayDir
);
2138 VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(
2139 const Twine
&_Path
, RedirectingDirectoryEntry::iterator Begin
,
2140 RedirectingDirectoryEntry::iterator End
, bool IterateExternalFS
,
2141 FileSystem
&ExternalFS
, std::error_code
&EC
)
2142 : Dir(_Path
.str()), Current(Begin
), End(End
),
2143 IterateExternalFS(IterateExternalFS
), ExternalFS(ExternalFS
) {
2144 EC
= incrementImpl(/*IsFirstTime=*/true);
2147 std::error_code
VFSFromYamlDirIterImpl::increment() {
2148 return incrementImpl(/*IsFirstTime=*/false);
2151 std::error_code
VFSFromYamlDirIterImpl::incrementExternal() {
2152 assert(!(IsExternalFSCurrent
&& ExternalDirIter
== directory_iterator()) &&
2153 "incrementing past end");
2155 if (IsExternalFSCurrent
) {
2156 ExternalDirIter
.increment(EC
);
2157 } else if (IterateExternalFS
) {
2158 ExternalDirIter
= ExternalFS
.dir_begin(Dir
, EC
);
2159 IsExternalFSCurrent
= true;
2160 if (EC
&& EC
!= errc::no_such_file_or_directory
)
2164 if (EC
|| ExternalDirIter
== directory_iterator()) {
2165 CurrentEntry
= directory_entry();
2167 CurrentEntry
= *ExternalDirIter
;
2172 std::error_code
VFSFromYamlDirIterImpl::incrementContent(bool IsFirstTime
) {
2173 assert((IsFirstTime
|| Current
!= End
) && "cannot iterate past end");
2176 while (Current
!= End
) {
2177 SmallString
<128> PathStr(Dir
);
2178 llvm::sys::path::append(PathStr
, (*Current
)->getName());
2179 sys::fs::file_type Type
;
2180 switch ((*Current
)->getKind()) {
2182 Type
= sys::fs::file_type::directory_file
;
2185 Type
= sys::fs::file_type::regular_file
;
2188 CurrentEntry
= directory_entry(PathStr
.str(), Type
);
2191 return incrementExternal();
2194 std::error_code
VFSFromYamlDirIterImpl::incrementImpl(bool IsFirstTime
) {
2196 std::error_code EC
= IsExternalFSCurrent
? incrementExternal()
2197 : incrementContent(IsFirstTime
);
2198 if (EC
|| CurrentEntry
.path().empty())
2200 StringRef Name
= llvm::sys::path::filename(CurrentEntry
.path());
2201 if (SeenNames
.insert(Name
).second
)
2202 return EC
; // name not seen before
2204 llvm_unreachable("returned above");
2207 vfs::recursive_directory_iterator::recursive_directory_iterator(
2208 FileSystem
&FS_
, const Twine
&Path
, std::error_code
&EC
)
2210 directory_iterator I
= FS
->dir_begin(Path
, EC
);
2211 if (I
!= directory_iterator()) {
2212 State
= std::make_shared
<detail::RecDirIterState
>();
2213 State
->Stack
.push(I
);
2217 vfs::recursive_directory_iterator
&
2218 recursive_directory_iterator::increment(std::error_code
&EC
) {
2219 assert(FS
&& State
&& !State
->Stack
.empty() && "incrementing past end");
2220 assert(!State
->Stack
.top()->path().empty() && "non-canonical end iterator");
2221 vfs::directory_iterator End
;
2223 if (State
->HasNoPushRequest
)
2224 State
->HasNoPushRequest
= false;
2226 if (State
->Stack
.top()->type() == sys::fs::file_type::directory_file
) {
2227 vfs::directory_iterator I
= FS
->dir_begin(State
->Stack
.top()->path(), EC
);
2229 State
->Stack
.push(I
);
2235 while (!State
->Stack
.empty() && State
->Stack
.top().increment(EC
) == End
)
2238 if (State
->Stack
.empty())
2239 State
.reset(); // end iterator