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/None.h"
18 #include "llvm/ADT/Optional.h"
19 #include "llvm/ADT/STLExtras.h"
20 #include "llvm/ADT/SmallString.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/ADT/StringSet.h"
24 #include "llvm/ADT/Twine.h"
25 #include "llvm/ADT/iterator_range.h"
26 #include "llvm/Config/llvm-config.h"
27 #include "llvm/Support/Casting.h"
28 #include "llvm/Support/Chrono.h"
29 #include "llvm/Support/Compiler.h"
30 #include "llvm/Support/Debug.h"
31 #include "llvm/Support/Errc.h"
32 #include "llvm/Support/ErrorHandling.h"
33 #include "llvm/Support/ErrorOr.h"
34 #include "llvm/Support/FileSystem.h"
35 #include "llvm/Support/MemoryBuffer.h"
36 #include "llvm/Support/Path.h"
37 #include "llvm/Support/Process.h"
38 #include "llvm/Support/SMLoc.h"
39 #include "llvm/Support/SourceMgr.h"
40 #include "llvm/Support/YAMLParser.h"
41 #include "llvm/Support/raw_ostream.h"
52 #include <system_error>
57 using namespace llvm::vfs
;
59 using llvm::sys::fs::file_status
;
60 using llvm::sys::fs::file_type
;
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(StringRef Name
, UniqueID UID
, sys::TimePoint
<> MTime
,
70 uint32_t User
, uint32_t Group
, uint64_t Size
, file_type Type
,
72 : Name(Name
), UID(UID
), MTime(MTime
), User(User
), Group(Group
), Size(Size
),
73 Type(Type
), Perms(Perms
) {}
75 Status
Status::copyWithNewName(const Status
&In
, StringRef NewName
) {
76 return Status(NewName
, In
.getUniqueID(), In
.getLastModificationTime(),
77 In
.getUser(), In
.getGroup(), In
.getSize(), In
.getType(),
81 Status
Status::copyWithNewName(const file_status
&In
, StringRef NewName
) {
82 return Status(NewName
, In
.getUniqueID(), In
.getLastModificationTime(),
83 In
.getUser(), In
.getGroup(), In
.getSize(), In
.type(),
87 bool Status::equivalent(const Status
&Other
) const {
88 assert(isStatusKnown() && Other
.isStatusKnown());
89 return getUniqueID() == Other
.getUniqueID();
92 bool Status::isDirectory() const { return Type
== file_type::directory_file
; }
94 bool Status::isRegularFile() const { return Type
== file_type::regular_file
; }
96 bool Status::isOther() const {
97 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
100 bool Status::isSymlink() const { return Type
== file_type::symlink_file
; }
102 bool Status::isStatusKnown() const { return Type
!= file_type::status_error
; }
104 bool Status::exists() const {
105 return isStatusKnown() && Type
!= file_type::file_not_found
;
108 File::~File() = default;
110 FileSystem::~FileSystem() = default;
112 ErrorOr
<std::unique_ptr
<MemoryBuffer
>>
113 FileSystem::getBufferForFile(const llvm::Twine
&Name
, int64_t FileSize
,
114 bool RequiresNullTerminator
, bool IsVolatile
) {
115 auto F
= openFileForRead(Name
);
119 return (*F
)->getBuffer(Name
, FileSize
, RequiresNullTerminator
, IsVolatile
);
122 std::error_code
FileSystem::makeAbsolute(SmallVectorImpl
<char> &Path
) const {
123 if (llvm::sys::path::is_absolute(Path
))
126 auto WorkingDir
= getCurrentWorkingDirectory();
128 return WorkingDir
.getError();
130 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 /// A file system according to your operating system.
232 /// This may be linked to the process's working directory, or maintain its own.
234 /// Currently, its own working directory is emulated by storing the path and
235 /// sending absolute paths to llvm::sys::fs:: functions.
236 /// A more principled approach would be to push this down a level, modelling
237 /// the working dir as an llvm::sys::fs::WorkingDir or similar.
238 /// This would enable the use of openat()-style functions on some platforms.
239 class RealFileSystem
: public FileSystem
{
241 explicit RealFileSystem(bool LinkCWDToProcess
) {
242 if (!LinkCWDToProcess
) {
243 SmallString
<128> PWD
, RealPWD
;
244 if (llvm::sys::fs::current_path(PWD
))
245 return; // Awful, but nothing to do here.
246 if (llvm::sys::fs::real_path(PWD
, RealPWD
))
253 ErrorOr
<Status
> status(const Twine
&Path
) override
;
254 ErrorOr
<std::unique_ptr
<File
>> openFileForRead(const Twine
&Path
) override
;
255 directory_iterator
dir_begin(const Twine
&Dir
, std::error_code
&EC
) override
;
257 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
;
258 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
;
259 std::error_code
isLocal(const Twine
&Path
, bool &Result
) override
;
260 std::error_code
getRealPath(const Twine
&Path
,
261 SmallVectorImpl
<char> &Output
) const override
;
264 // If this FS has its own working dir, use it to make Path absolute.
265 // The returned twine is safe to use as long as both Storage and Path live.
266 Twine
adjustPath(const Twine
&Path
, SmallVectorImpl
<char> &Storage
) const {
269 Path
.toVector(Storage
);
270 sys::fs::make_absolute(WD
->Resolved
, Storage
);
274 struct WorkingDirectory
{
275 // The current working directory, without symlinks resolved. (echo $PWD).
276 SmallString
<128> Specified
;
277 // The current working directory, with links resolved. (readlink .).
278 SmallString
<128> Resolved
;
280 Optional
<WorkingDirectory
> WD
;
285 ErrorOr
<Status
> RealFileSystem::status(const Twine
&Path
) {
286 SmallString
<256> Storage
;
287 sys::fs::file_status RealStatus
;
288 if (std::error_code EC
=
289 sys::fs::status(adjustPath(Path
, Storage
), RealStatus
))
291 return Status::copyWithNewName(RealStatus
, Path
.str());
294 ErrorOr
<std::unique_ptr
<File
>>
295 RealFileSystem::openFileForRead(const Twine
&Name
) {
297 SmallString
<256> RealName
, Storage
;
298 if (std::error_code EC
= sys::fs::openFileForRead(
299 adjustPath(Name
, Storage
), FD
, sys::fs::OF_None
, &RealName
))
301 return std::unique_ptr
<File
>(new RealFile(FD
, Name
.str(), RealName
.str()));
304 llvm::ErrorOr
<std::string
> RealFileSystem::getCurrentWorkingDirectory() const {
306 return WD
->Specified
.str();
308 SmallString
<128> Dir
;
309 if (std::error_code EC
= llvm::sys::fs::current_path(Dir
))
314 std::error_code
RealFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
316 return llvm::sys::fs::set_current_path(Path
);
318 SmallString
<128> Absolute
, Resolved
, Storage
;
319 adjustPath(Path
, Storage
).toVector(Absolute
);
321 if (auto Err
= llvm::sys::fs::is_directory(Absolute
, IsDir
))
324 return std::make_error_code(std::errc::not_a_directory
);
325 if (auto Err
= llvm::sys::fs::real_path(Absolute
, Resolved
))
327 WD
= {Absolute
, Resolved
};
328 return std::error_code();
331 std::error_code
RealFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
332 SmallString
<256> Storage
;
333 return llvm::sys::fs::is_local(adjustPath(Path
, Storage
), Result
);
337 RealFileSystem::getRealPath(const Twine
&Path
,
338 SmallVectorImpl
<char> &Output
) const {
339 SmallString
<256> Storage
;
340 return llvm::sys::fs::real_path(adjustPath(Path
, Storage
), Output
);
343 IntrusiveRefCntPtr
<FileSystem
> vfs::getRealFileSystem() {
344 static IntrusiveRefCntPtr
<FileSystem
> FS(new RealFileSystem(true));
348 std::unique_ptr
<FileSystem
> vfs::createPhysicalFileSystem() {
349 return llvm::make_unique
<RealFileSystem
>(false);
354 class RealFSDirIter
: public llvm::vfs::detail::DirIterImpl
{
355 llvm::sys::fs::directory_iterator Iter
;
358 RealFSDirIter(const Twine
&Path
, std::error_code
&EC
) : Iter(Path
, EC
) {
359 if (Iter
!= llvm::sys::fs::directory_iterator())
360 CurrentEntry
= directory_entry(Iter
->path(), Iter
->type());
363 std::error_code
increment() override
{
366 CurrentEntry
= (Iter
== llvm::sys::fs::directory_iterator())
368 : directory_entry(Iter
->path(), Iter
->type());
375 directory_iterator
RealFileSystem::dir_begin(const Twine
&Dir
,
376 std::error_code
&EC
) {
377 SmallString
<128> Storage
;
378 return directory_iterator(
379 std::make_shared
<RealFSDirIter
>(adjustPath(Dir
, Storage
), EC
));
382 //===-----------------------------------------------------------------------===/
383 // OverlayFileSystem implementation
384 //===-----------------------------------------------------------------------===/
386 OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr
<FileSystem
> BaseFS
) {
387 FSList
.push_back(std::move(BaseFS
));
390 void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr
<FileSystem
> FS
) {
391 FSList
.push_back(FS
);
392 // Synchronize added file systems by duplicating the working directory from
393 // the first one in the list.
394 FS
->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
397 ErrorOr
<Status
> OverlayFileSystem::status(const Twine
&Path
) {
398 // FIXME: handle symlinks that cross file systems
399 for (iterator I
= overlays_begin(), E
= overlays_end(); I
!= E
; ++I
) {
400 ErrorOr
<Status
> Status
= (*I
)->status(Path
);
401 if (Status
|| Status
.getError() != llvm::errc::no_such_file_or_directory
)
404 return make_error_code(llvm::errc::no_such_file_or_directory
);
407 ErrorOr
<std::unique_ptr
<File
>>
408 OverlayFileSystem::openFileForRead(const llvm::Twine
&Path
) {
409 // FIXME: handle symlinks that cross file systems
410 for (iterator I
= overlays_begin(), E
= overlays_end(); I
!= E
; ++I
) {
411 auto Result
= (*I
)->openFileForRead(Path
);
412 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
415 return make_error_code(llvm::errc::no_such_file_or_directory
);
418 llvm::ErrorOr
<std::string
>
419 OverlayFileSystem::getCurrentWorkingDirectory() const {
420 // All file systems are synchronized, just take the first working directory.
421 return FSList
.front()->getCurrentWorkingDirectory();
425 OverlayFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
426 for (auto &FS
: FSList
)
427 if (std::error_code EC
= FS
->setCurrentWorkingDirectory(Path
))
432 std::error_code
OverlayFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
433 for (auto &FS
: FSList
)
434 if (FS
->exists(Path
))
435 return FS
->isLocal(Path
, Result
);
436 return errc::no_such_file_or_directory
;
440 OverlayFileSystem::getRealPath(const Twine
&Path
,
441 SmallVectorImpl
<char> &Output
) const {
442 for (auto &FS
: FSList
)
443 if (FS
->exists(Path
))
444 return FS
->getRealPath(Path
, Output
);
445 return errc::no_such_file_or_directory
;
448 llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;
452 class OverlayFSDirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
453 OverlayFileSystem
&Overlays
;
455 OverlayFileSystem::iterator CurrentFS
;
456 directory_iterator CurrentDirIter
;
457 llvm::StringSet
<> SeenNames
;
459 std::error_code
incrementFS() {
460 assert(CurrentFS
!= Overlays
.overlays_end() && "incrementing past end");
462 for (auto E
= Overlays
.overlays_end(); CurrentFS
!= E
; ++CurrentFS
) {
464 CurrentDirIter
= (*CurrentFS
)->dir_begin(Path
, EC
);
465 if (EC
&& EC
!= errc::no_such_file_or_directory
)
467 if (CurrentDirIter
!= directory_iterator())
473 std::error_code
incrementDirIter(bool IsFirstTime
) {
474 assert((IsFirstTime
|| CurrentDirIter
!= directory_iterator()) &&
475 "incrementing past end");
478 CurrentDirIter
.increment(EC
);
479 if (!EC
&& CurrentDirIter
== directory_iterator())
484 std::error_code
incrementImpl(bool IsFirstTime
) {
486 std::error_code EC
= incrementDirIter(IsFirstTime
);
487 if (EC
|| CurrentDirIter
== directory_iterator()) {
488 CurrentEntry
= directory_entry();
491 CurrentEntry
= *CurrentDirIter
;
492 StringRef Name
= llvm::sys::path::filename(CurrentEntry
.path());
493 if (SeenNames
.insert(Name
).second
)
494 return EC
; // name not seen before
496 llvm_unreachable("returned above");
500 OverlayFSDirIterImpl(const Twine
&Path
, OverlayFileSystem
&FS
,
502 : Overlays(FS
), Path(Path
.str()), CurrentFS(Overlays
.overlays_begin()) {
503 CurrentDirIter
= (*CurrentFS
)->dir_begin(Path
, EC
);
504 EC
= incrementImpl(true);
507 std::error_code
increment() override
{ return incrementImpl(false); }
512 directory_iterator
OverlayFileSystem::dir_begin(const Twine
&Dir
,
513 std::error_code
&EC
) {
514 return directory_iterator(
515 std::make_shared
<OverlayFSDirIterImpl
>(Dir
, *this, EC
));
518 void ProxyFileSystem::anchor() {}
525 enum InMemoryNodeKind
{ IME_File
, IME_Directory
, IME_HardLink
};
527 /// The in memory file system is a tree of Nodes. Every node can either be a
528 /// file , hardlink or a directory.
530 InMemoryNodeKind Kind
;
531 std::string FileName
;
534 InMemoryNode(llvm::StringRef FileName
, InMemoryNodeKind Kind
)
535 : Kind(Kind
), FileName(llvm::sys::path::filename(FileName
)) {}
536 virtual ~InMemoryNode() = default;
538 /// Get the filename of this node (the name without the directory part).
539 StringRef
getFileName() const { return FileName
; }
540 InMemoryNodeKind
getKind() const { return Kind
; }
541 virtual std::string
toString(unsigned Indent
) const = 0;
544 class InMemoryFile
: public InMemoryNode
{
546 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
;
549 InMemoryFile(Status Stat
, std::unique_ptr
<llvm::MemoryBuffer
> Buffer
)
550 : InMemoryNode(Stat
.getName(), IME_File
), Stat(std::move(Stat
)),
551 Buffer(std::move(Buffer
)) {}
553 /// Return the \p Status for this node. \p RequestedName should be the name
554 /// through which the caller referred to this node. It will override
555 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
556 Status
getStatus(StringRef RequestedName
) const {
557 return Status::copyWithNewName(Stat
, RequestedName
);
559 llvm::MemoryBuffer
*getBuffer() const { return Buffer
.get(); }
561 std::string
toString(unsigned Indent
) const override
{
562 return (std::string(Indent
, ' ') + Stat
.getName() + "\n").str();
565 static bool classof(const InMemoryNode
*N
) {
566 return N
->getKind() == IME_File
;
572 class InMemoryHardLink
: public InMemoryNode
{
573 const InMemoryFile
&ResolvedFile
;
576 InMemoryHardLink(StringRef Path
, const InMemoryFile
&ResolvedFile
)
577 : InMemoryNode(Path
, IME_HardLink
), ResolvedFile(ResolvedFile
) {}
578 const InMemoryFile
&getResolvedFile() const { return ResolvedFile
; }
580 std::string
toString(unsigned Indent
) const override
{
581 return std::string(Indent
, ' ') + "HardLink to -> " +
582 ResolvedFile
.toString(0);
585 static bool classof(const InMemoryNode
*N
) {
586 return N
->getKind() == IME_HardLink
;
590 /// Adapt a InMemoryFile for VFS' File interface. The goal is to make
591 /// \p InMemoryFileAdaptor mimic as much as possible the behavior of
593 class InMemoryFileAdaptor
: public File
{
594 const InMemoryFile
&Node
;
595 /// The name to use when returning a Status for this file.
596 std::string RequestedName
;
599 explicit InMemoryFileAdaptor(const InMemoryFile
&Node
,
600 std::string RequestedName
)
601 : Node(Node
), RequestedName(std::move(RequestedName
)) {}
603 llvm::ErrorOr
<Status
> status() override
{
604 return Node
.getStatus(RequestedName
);
607 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
608 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
609 bool IsVolatile
) override
{
610 llvm::MemoryBuffer
*Buf
= Node
.getBuffer();
611 return llvm::MemoryBuffer::getMemBuffer(
612 Buf
->getBuffer(), Buf
->getBufferIdentifier(), RequiresNullTerminator
);
615 std::error_code
close() override
{ return {}; }
619 class InMemoryDirectory
: public InMemoryNode
{
621 llvm::StringMap
<std::unique_ptr
<InMemoryNode
>> Entries
;
624 InMemoryDirectory(Status Stat
)
625 : InMemoryNode(Stat
.getName(), IME_Directory
), Stat(std::move(Stat
)) {}
627 /// Return the \p Status for this node. \p RequestedName should be the name
628 /// through which the caller referred to this node. It will override
629 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
630 Status
getStatus(StringRef RequestedName
) const {
631 return Status::copyWithNewName(Stat
, RequestedName
);
633 InMemoryNode
*getChild(StringRef Name
) {
634 auto I
= Entries
.find(Name
);
635 if (I
!= Entries
.end())
636 return I
->second
.get();
640 InMemoryNode
*addChild(StringRef Name
, std::unique_ptr
<InMemoryNode
> Child
) {
641 return Entries
.insert(make_pair(Name
, std::move(Child
)))
642 .first
->second
.get();
645 using const_iterator
= decltype(Entries
)::const_iterator
;
647 const_iterator
begin() const { return Entries
.begin(); }
648 const_iterator
end() const { return Entries
.end(); }
650 std::string
toString(unsigned Indent
) const override
{
652 (std::string(Indent
, ' ') + Stat
.getName() + "\n").str();
653 for (const auto &Entry
: Entries
)
654 Result
+= Entry
.second
->toString(Indent
+ 2);
658 static bool classof(const InMemoryNode
*N
) {
659 return N
->getKind() == IME_Directory
;
664 Status
getNodeStatus(const InMemoryNode
*Node
, StringRef RequestedName
) {
665 if (auto Dir
= dyn_cast
<detail::InMemoryDirectory
>(Node
))
666 return Dir
->getStatus(RequestedName
);
667 if (auto File
= dyn_cast
<detail::InMemoryFile
>(Node
))
668 return File
->getStatus(RequestedName
);
669 if (auto Link
= dyn_cast
<detail::InMemoryHardLink
>(Node
))
670 return Link
->getResolvedFile().getStatus(RequestedName
);
671 llvm_unreachable("Unknown node type");
674 } // namespace detail
676 InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths
)
677 : Root(new detail::InMemoryDirectory(
678 Status("", getNextVirtualUniqueID(), llvm::sys::TimePoint
<>(), 0, 0,
679 0, llvm::sys::fs::file_type::directory_file
,
680 llvm::sys::fs::perms::all_all
))),
681 UseNormalizedPaths(UseNormalizedPaths
) {}
683 InMemoryFileSystem::~InMemoryFileSystem() = default;
685 std::string
InMemoryFileSystem::toString() const {
686 return Root
->toString(/*Indent=*/0);
689 bool InMemoryFileSystem::addFile(const Twine
&P
, time_t ModificationTime
,
690 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
,
691 Optional
<uint32_t> User
,
692 Optional
<uint32_t> Group
,
693 Optional
<llvm::sys::fs::file_type
> Type
,
694 Optional
<llvm::sys::fs::perms
> Perms
,
695 const detail::InMemoryFile
*HardLinkTarget
) {
696 SmallString
<128> Path
;
699 // Fix up relative paths. This just prepends the current working directory.
700 std::error_code EC
= makeAbsolute(Path
);
704 if (useNormalizedPaths())
705 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
710 detail::InMemoryDirectory
*Dir
= Root
.get();
711 auto I
= llvm::sys::path::begin(Path
), E
= sys::path::end(Path
);
712 const auto ResolvedUser
= User
.getValueOr(0);
713 const auto ResolvedGroup
= Group
.getValueOr(0);
714 const auto ResolvedType
= Type
.getValueOr(sys::fs::file_type::regular_file
);
715 const auto ResolvedPerms
= Perms
.getValueOr(sys::fs::all_all
);
716 assert(!(HardLinkTarget
&& Buffer
) && "HardLink cannot have a buffer");
717 // Any intermediate directories we create should be accessible by
718 // the owner, even if Perms says otherwise for the final path.
719 const auto NewDirectoryPerms
= ResolvedPerms
| sys::fs::owner_all
;
722 detail::InMemoryNode
*Node
= Dir
->getChild(Name
);
727 std::unique_ptr
<detail::InMemoryNode
> Child
;
729 Child
.reset(new detail::InMemoryHardLink(P
.str(), *HardLinkTarget
));
731 // Create a new file or directory.
732 Status
Stat(P
.str(), getNextVirtualUniqueID(),
733 llvm::sys::toTimePoint(ModificationTime
), ResolvedUser
,
734 ResolvedGroup
, Buffer
->getBufferSize(), ResolvedType
,
736 if (ResolvedType
== sys::fs::file_type::directory_file
) {
737 Child
.reset(new detail::InMemoryDirectory(std::move(Stat
)));
740 new detail::InMemoryFile(std::move(Stat
), std::move(Buffer
)));
743 Dir
->addChild(Name
, std::move(Child
));
747 // Create a new directory. Use the path up to here.
749 StringRef(Path
.str().begin(), Name
.end() - Path
.str().begin()),
750 getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime
),
751 ResolvedUser
, ResolvedGroup
, 0, sys::fs::file_type::directory_file
,
753 Dir
= cast
<detail::InMemoryDirectory
>(Dir
->addChild(
754 Name
, llvm::make_unique
<detail::InMemoryDirectory
>(std::move(Stat
))));
758 if (auto *NewDir
= dyn_cast
<detail::InMemoryDirectory
>(Node
)) {
761 assert((isa
<detail::InMemoryFile
>(Node
) ||
762 isa
<detail::InMemoryHardLink
>(Node
)) &&
763 "Must be either file, hardlink or directory!");
765 // Trying to insert a directory in place of a file.
769 // Return false only if the new file is different from the existing one.
770 if (auto Link
= dyn_cast
<detail::InMemoryHardLink
>(Node
)) {
771 return Link
->getResolvedFile().getBuffer()->getBuffer() ==
774 return cast
<detail::InMemoryFile
>(Node
)->getBuffer()->getBuffer() ==
780 bool InMemoryFileSystem::addFile(const Twine
&P
, time_t ModificationTime
,
781 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
,
782 Optional
<uint32_t> User
,
783 Optional
<uint32_t> Group
,
784 Optional
<llvm::sys::fs::file_type
> Type
,
785 Optional
<llvm::sys::fs::perms
> Perms
) {
786 return addFile(P
, ModificationTime
, std::move(Buffer
), User
, Group
, Type
,
787 Perms
, /*HardLinkTarget=*/nullptr);
790 bool InMemoryFileSystem::addFileNoOwn(const Twine
&P
, time_t ModificationTime
,
791 llvm::MemoryBuffer
*Buffer
,
792 Optional
<uint32_t> User
,
793 Optional
<uint32_t> Group
,
794 Optional
<llvm::sys::fs::file_type
> Type
,
795 Optional
<llvm::sys::fs::perms
> Perms
) {
796 return addFile(P
, ModificationTime
,
797 llvm::MemoryBuffer::getMemBuffer(
798 Buffer
->getBuffer(), Buffer
->getBufferIdentifier()),
799 std::move(User
), std::move(Group
), std::move(Type
),
803 static ErrorOr
<const detail::InMemoryNode
*>
804 lookupInMemoryNode(const InMemoryFileSystem
&FS
, detail::InMemoryDirectory
*Dir
,
806 SmallString
<128> Path
;
809 // Fix up relative paths. This just prepends the current working directory.
810 std::error_code EC
= FS
.makeAbsolute(Path
);
814 if (FS
.useNormalizedPaths())
815 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
820 auto I
= llvm::sys::path::begin(Path
), E
= llvm::sys::path::end(Path
);
822 detail::InMemoryNode
*Node
= Dir
->getChild(*I
);
825 return errc::no_such_file_or_directory
;
827 // Return the file if it's at the end of the path.
828 if (auto File
= dyn_cast
<detail::InMemoryFile
>(Node
)) {
831 return errc::no_such_file_or_directory
;
834 // If Node is HardLink then return the resolved file.
835 if (auto File
= dyn_cast
<detail::InMemoryHardLink
>(Node
)) {
837 return &File
->getResolvedFile();
838 return errc::no_such_file_or_directory
;
840 // Traverse directories.
841 Dir
= cast
<detail::InMemoryDirectory
>(Node
);
847 bool InMemoryFileSystem::addHardLink(const Twine
&FromPath
,
848 const Twine
&ToPath
) {
849 auto FromNode
= lookupInMemoryNode(*this, Root
.get(), FromPath
);
850 auto ToNode
= lookupInMemoryNode(*this, Root
.get(), ToPath
);
851 // FromPath must not have been added before. ToPath must have been added
852 // before. Resolved ToPath must be a File.
853 if (!ToNode
|| FromNode
|| !isa
<detail::InMemoryFile
>(*ToNode
))
855 return this->addFile(FromPath
, 0, nullptr, None
, None
, None
, None
,
856 cast
<detail::InMemoryFile
>(*ToNode
));
859 llvm::ErrorOr
<Status
> InMemoryFileSystem::status(const Twine
&Path
) {
860 auto Node
= lookupInMemoryNode(*this, Root
.get(), Path
);
862 return detail::getNodeStatus(*Node
, Path
.str());
863 return Node
.getError();
866 llvm::ErrorOr
<std::unique_ptr
<File
>>
867 InMemoryFileSystem::openFileForRead(const Twine
&Path
) {
868 auto Node
= lookupInMemoryNode(*this, Root
.get(), Path
);
870 return Node
.getError();
872 // When we have a file provide a heap-allocated wrapper for the memory buffer
873 // to match the ownership semantics for File.
874 if (auto *F
= dyn_cast
<detail::InMemoryFile
>(*Node
))
875 return std::unique_ptr
<File
>(
876 new detail::InMemoryFileAdaptor(*F
, Path
.str()));
878 // FIXME: errc::not_a_file?
879 return make_error_code(llvm::errc::invalid_argument
);
884 /// Adaptor from InMemoryDir::iterator to directory_iterator.
885 class InMemoryDirIterator
: public llvm::vfs::detail::DirIterImpl
{
886 detail::InMemoryDirectory::const_iterator I
;
887 detail::InMemoryDirectory::const_iterator E
;
888 std::string RequestedDirName
;
890 void setCurrentEntry() {
892 SmallString
<256> Path(RequestedDirName
);
893 llvm::sys::path::append(Path
, I
->second
->getFileName());
894 sys::fs::file_type Type
;
895 switch (I
->second
->getKind()) {
896 case detail::IME_File
:
897 case detail::IME_HardLink
:
898 Type
= sys::fs::file_type::regular_file
;
900 case detail::IME_Directory
:
901 Type
= sys::fs::file_type::directory_file
;
904 CurrentEntry
= directory_entry(Path
.str(), Type
);
906 // When we're at the end, make CurrentEntry invalid and DirIterImpl will
908 CurrentEntry
= directory_entry();
913 InMemoryDirIterator() = default;
915 explicit InMemoryDirIterator(const detail::InMemoryDirectory
&Dir
,
916 std::string RequestedDirName
)
917 : I(Dir
.begin()), E(Dir
.end()),
918 RequestedDirName(std::move(RequestedDirName
)) {
922 std::error_code
increment() override
{
931 directory_iterator
InMemoryFileSystem::dir_begin(const Twine
&Dir
,
932 std::error_code
&EC
) {
933 auto Node
= lookupInMemoryNode(*this, Root
.get(), Dir
);
935 EC
= Node
.getError();
936 return directory_iterator(std::make_shared
<InMemoryDirIterator
>());
939 if (auto *DirNode
= dyn_cast
<detail::InMemoryDirectory
>(*Node
))
940 return directory_iterator(
941 std::make_shared
<InMemoryDirIterator
>(*DirNode
, Dir
.str()));
943 EC
= make_error_code(llvm::errc::not_a_directory
);
944 return directory_iterator(std::make_shared
<InMemoryDirIterator
>());
947 std::error_code
InMemoryFileSystem::setCurrentWorkingDirectory(const Twine
&P
) {
948 SmallString
<128> Path
;
951 // Fix up relative paths. This just prepends the current working directory.
952 std::error_code EC
= makeAbsolute(Path
);
956 if (useNormalizedPaths())
957 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
960 WorkingDirectory
= Path
.str();
965 InMemoryFileSystem::getRealPath(const Twine
&Path
,
966 SmallVectorImpl
<char> &Output
) const {
967 auto CWD
= getCurrentWorkingDirectory();
968 if (!CWD
|| CWD
->empty())
969 return errc::operation_not_permitted
;
970 Path
.toVector(Output
);
971 if (auto EC
= makeAbsolute(Output
))
973 llvm::sys::path::remove_dots(Output
, /*remove_dot_dot=*/true);
977 std::error_code
InMemoryFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
985 //===-----------------------------------------------------------------------===/
986 // RedirectingFileSystem implementation
987 //===-----------------------------------------------------------------------===/
989 // FIXME: reuse implementation common with OverlayFSDirIterImpl as these
990 // iterators are conceptually similar.
991 class llvm::vfs::VFSFromYamlDirIterImpl
992 : public llvm::vfs::detail::DirIterImpl
{
994 RedirectingFileSystem::RedirectingDirectoryEntry::iterator Current
, End
;
996 // To handle 'fallthrough' mode we need to iterate at first through
997 // RedirectingDirectoryEntry and then through ExternalFS. These operations are
998 // done sequentially, we just need to keep a track of what kind of iteration
999 // we are currently performing.
1001 /// Flag telling if we should iterate through ExternalFS or stop at the last
1002 /// RedirectingDirectoryEntry::iterator.
1003 bool IterateExternalFS
;
1004 /// Flag telling if we have switched to iterating through ExternalFS.
1005 bool IsExternalFSCurrent
= false;
1006 FileSystem
&ExternalFS
;
1007 directory_iterator ExternalDirIter
;
1008 llvm::StringSet
<> SeenNames
;
1010 /// To combine multiple iterations, different methods are responsible for
1011 /// different iteration steps.
1014 /// Responsible for dispatching between RedirectingDirectoryEntry iteration
1015 /// and ExternalFS iteration.
1016 std::error_code
incrementImpl(bool IsFirstTime
);
1017 /// Responsible for RedirectingDirectoryEntry iteration.
1018 std::error_code
incrementContent(bool IsFirstTime
);
1019 /// Responsible for ExternalFS iteration.
1020 std::error_code
incrementExternal();
1024 VFSFromYamlDirIterImpl(
1026 RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin
,
1027 RedirectingFileSystem::RedirectingDirectoryEntry::iterator End
,
1028 bool IterateExternalFS
, FileSystem
&ExternalFS
, std::error_code
&EC
);
1030 std::error_code
increment() override
;
1033 llvm::ErrorOr
<std::string
>
1034 RedirectingFileSystem::getCurrentWorkingDirectory() const {
1035 return ExternalFS
->getCurrentWorkingDirectory();
1039 RedirectingFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
1040 return ExternalFS
->setCurrentWorkingDirectory(Path
);
1043 std::error_code
RedirectingFileSystem::isLocal(const Twine
&Path
,
1045 return ExternalFS
->isLocal(Path
, Result
);
1048 directory_iterator
RedirectingFileSystem::dir_begin(const Twine
&Dir
,
1049 std::error_code
&EC
) {
1050 ErrorOr
<RedirectingFileSystem::Entry
*> E
= lookupPath(Dir
);
1053 if (IsFallthrough
&& EC
== errc::no_such_file_or_directory
)
1054 return ExternalFS
->dir_begin(Dir
, EC
);
1057 ErrorOr
<Status
> S
= status(Dir
, *E
);
1062 if (!S
->isDirectory()) {
1063 EC
= std::error_code(static_cast<int>(errc::not_a_directory
),
1064 std::system_category());
1068 auto *D
= cast
<RedirectingFileSystem::RedirectingDirectoryEntry
>(*E
);
1069 return directory_iterator(std::make_shared
<VFSFromYamlDirIterImpl
>(
1070 Dir
, D
->contents_begin(), D
->contents_end(),
1071 /*IterateExternalFS=*/IsFallthrough
, *ExternalFS
, EC
));
1074 void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir
) {
1075 ExternalContentsPrefixDir
= PrefixDir
.str();
1078 StringRef
RedirectingFileSystem::getExternalContentsPrefixDir() const {
1079 return ExternalContentsPrefixDir
;
1082 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
1083 LLVM_DUMP_METHOD
void RedirectingFileSystem::dump() const {
1084 for (const auto &Root
: Roots
)
1085 dumpEntry(Root
.get());
1088 LLVM_DUMP_METHOD
void
1089 RedirectingFileSystem::dumpEntry(RedirectingFileSystem::Entry
*E
,
1090 int NumSpaces
) const {
1091 StringRef Name
= E
->getName();
1092 for (int i
= 0, e
= NumSpaces
; i
< e
; ++i
)
1094 dbgs() << "'" << Name
.str().c_str() << "'"
1097 if (E
->getKind() == RedirectingFileSystem::EK_Directory
) {
1098 auto *DE
= dyn_cast
<RedirectingFileSystem::RedirectingDirectoryEntry
>(E
);
1099 assert(DE
&& "Should be a directory");
1101 for (std::unique_ptr
<Entry
> &SubEntry
:
1102 llvm::make_range(DE
->contents_begin(), DE
->contents_end()))
1103 dumpEntry(SubEntry
.get(), NumSpaces
+ 2);
1108 /// A helper class to hold the common YAML parsing state.
1109 class llvm::vfs::RedirectingFileSystemParser
{
1110 yaml::Stream
&Stream
;
1112 void error(yaml::Node
*N
, const Twine
&Msg
) { Stream
.printError(N
, Msg
); }
1115 bool parseScalarString(yaml::Node
*N
, StringRef
&Result
,
1116 SmallVectorImpl
<char> &Storage
) {
1117 const auto *S
= dyn_cast
<yaml::ScalarNode
>(N
);
1120 error(N
, "expected string");
1123 Result
= S
->getValue(Storage
);
1128 bool parseScalarBool(yaml::Node
*N
, bool &Result
) {
1129 SmallString
<5> Storage
;
1131 if (!parseScalarString(N
, Value
, Storage
))
1134 if (Value
.equals_lower("true") || Value
.equals_lower("on") ||
1135 Value
.equals_lower("yes") || Value
== "1") {
1138 } else if (Value
.equals_lower("false") || Value
.equals_lower("off") ||
1139 Value
.equals_lower("no") || Value
== "0") {
1144 error(N
, "expected boolean value");
1152 KeyStatus(bool Required
= false) : Required(Required
) {}
1155 using KeyStatusPair
= std::pair
<StringRef
, KeyStatus
>;
1158 bool checkDuplicateOrUnknownKey(yaml::Node
*KeyNode
, StringRef Key
,
1159 DenseMap
<StringRef
, KeyStatus
> &Keys
) {
1160 if (!Keys
.count(Key
)) {
1161 error(KeyNode
, "unknown key");
1164 KeyStatus
&S
= Keys
[Key
];
1166 error(KeyNode
, Twine("duplicate key '") + Key
+ "'");
1174 bool checkMissingKeys(yaml::Node
*Obj
, DenseMap
<StringRef
, KeyStatus
> &Keys
) {
1175 for (const auto &I
: Keys
) {
1176 if (I
.second
.Required
&& !I
.second
.Seen
) {
1177 error(Obj
, Twine("missing key '") + I
.first
+ "'");
1184 RedirectingFileSystem::Entry
*
1185 lookupOrCreateEntry(RedirectingFileSystem
*FS
, StringRef Name
,
1186 RedirectingFileSystem::Entry
*ParentEntry
= nullptr) {
1187 if (!ParentEntry
) { // Look for a existent root
1188 for (const auto &Root
: FS
->Roots
) {
1189 if (Name
.equals(Root
->getName())) {
1190 ParentEntry
= Root
.get();
1194 } else { // Advance to the next component
1195 auto *DE
= dyn_cast
<RedirectingFileSystem::RedirectingDirectoryEntry
>(
1197 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &Content
:
1198 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
1200 dyn_cast
<RedirectingFileSystem::RedirectingDirectoryEntry
>(
1202 if (DirContent
&& Name
.equals(Content
->getName()))
1207 // ... or create a new one
1208 std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
1209 llvm::make_unique
<RedirectingFileSystem::RedirectingDirectoryEntry
>(
1210 Name
, Status("", getNextVirtualUniqueID(),
1211 std::chrono::system_clock::now(), 0, 0, 0,
1212 file_type::directory_file
, sys::fs::all_all
));
1214 if (!ParentEntry
) { // Add a new root to the overlay
1215 FS
->Roots
.push_back(std::move(E
));
1216 ParentEntry
= FS
->Roots
.back().get();
1221 dyn_cast
<RedirectingFileSystem::RedirectingDirectoryEntry
>(ParentEntry
);
1222 DE
->addContent(std::move(E
));
1223 return DE
->getLastContent();
1226 void uniqueOverlayTree(RedirectingFileSystem
*FS
,
1227 RedirectingFileSystem::Entry
*SrcE
,
1228 RedirectingFileSystem::Entry
*NewParentE
= nullptr) {
1229 StringRef Name
= SrcE
->getName();
1230 switch (SrcE
->getKind()) {
1231 case RedirectingFileSystem::EK_Directory
: {
1233 dyn_cast
<RedirectingFileSystem::RedirectingDirectoryEntry
>(SrcE
);
1234 assert(DE
&& "Must be a directory");
1235 // Empty directories could be present in the YAML as a way to
1236 // describe a file for a current directory after some of its subdir
1237 // is parsed. This only leads to redundant walks, ignore it.
1239 NewParentE
= lookupOrCreateEntry(FS
, Name
, NewParentE
);
1240 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &SubEntry
:
1241 llvm::make_range(DE
->contents_begin(), DE
->contents_end()))
1242 uniqueOverlayTree(FS
, SubEntry
.get(), NewParentE
);
1245 case RedirectingFileSystem::EK_File
: {
1246 auto *FE
= dyn_cast
<RedirectingFileSystem::RedirectingFileEntry
>(SrcE
);
1247 assert(FE
&& "Must be a file");
1248 assert(NewParentE
&& "Parent entry must exist");
1249 auto *DE
= dyn_cast
<RedirectingFileSystem::RedirectingDirectoryEntry
>(
1252 llvm::make_unique
<RedirectingFileSystem::RedirectingFileEntry
>(
1253 Name
, FE
->getExternalContentsPath(), FE
->getUseName()));
1259 std::unique_ptr
<RedirectingFileSystem::Entry
>
1260 parseEntry(yaml::Node
*N
, RedirectingFileSystem
*FS
, bool IsRootEntry
) {
1261 auto *M
= dyn_cast
<yaml::MappingNode
>(N
);
1263 error(N
, "expected mapping node for file or directory entry");
1267 KeyStatusPair Fields
[] = {
1268 KeyStatusPair("name", true),
1269 KeyStatusPair("type", true),
1270 KeyStatusPair("contents", false),
1271 KeyStatusPair("external-contents", false),
1272 KeyStatusPair("use-external-name", false),
1275 DenseMap
<StringRef
, KeyStatus
> Keys(std::begin(Fields
), std::end(Fields
));
1277 bool HasContents
= false; // external or otherwise
1278 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>>
1280 std::string ExternalContentsPath
;
1282 yaml::Node
*NameValueNode
;
1283 auto UseExternalName
=
1284 RedirectingFileSystem::RedirectingFileEntry::NK_NotSet
;
1285 RedirectingFileSystem::EntryKind Kind
;
1287 for (auto &I
: *M
) {
1289 // Reuse the buffer for key and value, since we don't look at key after
1291 SmallString
<256> Buffer
;
1292 if (!parseScalarString(I
.getKey(), Key
, Buffer
))
1295 if (!checkDuplicateOrUnknownKey(I
.getKey(), Key
, Keys
))
1299 if (Key
== "name") {
1300 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1303 NameValueNode
= I
.getValue();
1304 if (FS
->UseCanonicalizedPaths
) {
1305 SmallString
<256> Path(Value
);
1306 // Guarantee that old YAML files containing paths with ".." and "."
1307 // are properly canonicalized before read into the VFS.
1308 Path
= sys::path::remove_leading_dotslash(Path
);
1309 sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
1314 } else if (Key
== "type") {
1315 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1317 if (Value
== "file")
1318 Kind
= RedirectingFileSystem::EK_File
;
1319 else if (Value
== "directory")
1320 Kind
= RedirectingFileSystem::EK_Directory
;
1322 error(I
.getValue(), "unknown value for 'type'");
1325 } else if (Key
== "contents") {
1328 "entry already has 'contents' or 'external-contents'");
1332 auto *Contents
= dyn_cast
<yaml::SequenceNode
>(I
.getValue());
1334 // FIXME: this is only for directories, what about files?
1335 error(I
.getValue(), "expected array");
1339 for (auto &I
: *Contents
) {
1340 if (std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
1341 parseEntry(&I
, FS
, /*IsRootEntry*/ false))
1342 EntryArrayContents
.push_back(std::move(E
));
1346 } else if (Key
== "external-contents") {
1349 "entry already has 'contents' or 'external-contents'");
1353 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1356 SmallString
<256> FullPath
;
1357 if (FS
->IsRelativeOverlay
) {
1358 FullPath
= FS
->getExternalContentsPrefixDir();
1359 assert(!FullPath
.empty() &&
1360 "External contents prefix directory must exist");
1361 llvm::sys::path::append(FullPath
, Value
);
1366 if (FS
->UseCanonicalizedPaths
) {
1367 // Guarantee that old YAML files containing paths with ".." and "."
1368 // are properly canonicalized before read into the VFS.
1369 FullPath
= sys::path::remove_leading_dotslash(FullPath
);
1370 sys::path::remove_dots(FullPath
, /*remove_dot_dot=*/true);
1372 ExternalContentsPath
= FullPath
.str();
1373 } else if (Key
== "use-external-name") {
1375 if (!parseScalarBool(I
.getValue(), Val
))
1378 Val
? RedirectingFileSystem::RedirectingFileEntry::NK_External
1379 : RedirectingFileSystem::RedirectingFileEntry::NK_Virtual
;
1381 llvm_unreachable("key missing from Keys");
1385 if (Stream
.failed())
1388 // check for missing keys
1390 error(N
, "missing key 'contents' or 'external-contents'");
1393 if (!checkMissingKeys(N
, Keys
))
1396 // check invalid configuration
1397 if (Kind
== RedirectingFileSystem::EK_Directory
&&
1399 RedirectingFileSystem::RedirectingFileEntry::NK_NotSet
) {
1400 error(N
, "'use-external-name' is not supported for directories");
1404 if (IsRootEntry
&& !sys::path::is_absolute(Name
)) {
1405 assert(NameValueNode
&& "Name presence should be checked earlier");
1406 error(NameValueNode
,
1407 "entry with relative path at the root level is not discoverable");
1411 // Remove trailing slash(es), being careful not to remove the root path
1412 StringRef
Trimmed(Name
);
1413 size_t RootPathLen
= sys::path::root_path(Trimmed
).size();
1414 while (Trimmed
.size() > RootPathLen
&&
1415 sys::path::is_separator(Trimmed
.back()))
1416 Trimmed
= Trimmed
.slice(0, Trimmed
.size() - 1);
1417 // Get the last component
1418 StringRef LastComponent
= sys::path::filename(Trimmed
);
1420 std::unique_ptr
<RedirectingFileSystem::Entry
> Result
;
1422 case RedirectingFileSystem::EK_File
:
1423 Result
= llvm::make_unique
<RedirectingFileSystem::RedirectingFileEntry
>(
1424 LastComponent
, std::move(ExternalContentsPath
), UseExternalName
);
1426 case RedirectingFileSystem::EK_Directory
:
1428 llvm::make_unique
<RedirectingFileSystem::RedirectingDirectoryEntry
>(
1429 LastComponent
, std::move(EntryArrayContents
),
1430 Status("", getNextVirtualUniqueID(),
1431 std::chrono::system_clock::now(), 0, 0, 0,
1432 file_type::directory_file
, sys::fs::all_all
));
1436 StringRef Parent
= sys::path::parent_path(Trimmed
);
1440 // if 'name' contains multiple components, create implicit directory entries
1441 for (sys::path::reverse_iterator I
= sys::path::rbegin(Parent
),
1442 E
= sys::path::rend(Parent
);
1444 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>> Entries
;
1445 Entries
.push_back(std::move(Result
));
1447 llvm::make_unique
<RedirectingFileSystem::RedirectingDirectoryEntry
>(
1448 *I
, std::move(Entries
),
1449 Status("", getNextVirtualUniqueID(),
1450 std::chrono::system_clock::now(), 0, 0, 0,
1451 file_type::directory_file
, sys::fs::all_all
));
1457 RedirectingFileSystemParser(yaml::Stream
&S
) : Stream(S
) {}
1460 bool parse(yaml::Node
*Root
, RedirectingFileSystem
*FS
) {
1461 auto *Top
= dyn_cast
<yaml::MappingNode
>(Root
);
1463 error(Root
, "expected mapping node");
1467 KeyStatusPair Fields
[] = {
1468 KeyStatusPair("version", true),
1469 KeyStatusPair("case-sensitive", false),
1470 KeyStatusPair("use-external-names", false),
1471 KeyStatusPair("overlay-relative", false),
1472 KeyStatusPair("fallthrough", false),
1473 KeyStatusPair("roots", true),
1476 DenseMap
<StringRef
, KeyStatus
> Keys(std::begin(Fields
), std::end(Fields
));
1477 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>> RootEntries
;
1479 // Parse configuration and 'roots'
1480 for (auto &I
: *Top
) {
1481 SmallString
<10> KeyBuffer
;
1483 if (!parseScalarString(I
.getKey(), Key
, KeyBuffer
))
1486 if (!checkDuplicateOrUnknownKey(I
.getKey(), Key
, Keys
))
1489 if (Key
== "roots") {
1490 auto *Roots
= dyn_cast
<yaml::SequenceNode
>(I
.getValue());
1492 error(I
.getValue(), "expected array");
1496 for (auto &I
: *Roots
) {
1497 if (std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
1498 parseEntry(&I
, FS
, /*IsRootEntry*/ true))
1499 RootEntries
.push_back(std::move(E
));
1503 } else if (Key
== "version") {
1504 StringRef VersionString
;
1505 SmallString
<4> Storage
;
1506 if (!parseScalarString(I
.getValue(), VersionString
, Storage
))
1509 if (VersionString
.getAsInteger
<int>(10, Version
)) {
1510 error(I
.getValue(), "expected integer");
1514 error(I
.getValue(), "invalid version number");
1518 error(I
.getValue(), "version mismatch, expected 0");
1521 } else if (Key
== "case-sensitive") {
1522 if (!parseScalarBool(I
.getValue(), FS
->CaseSensitive
))
1524 } else if (Key
== "overlay-relative") {
1525 if (!parseScalarBool(I
.getValue(), FS
->IsRelativeOverlay
))
1527 } else if (Key
== "use-external-names") {
1528 if (!parseScalarBool(I
.getValue(), FS
->UseExternalNames
))
1530 } else if (Key
== "fallthrough") {
1531 if (!parseScalarBool(I
.getValue(), FS
->IsFallthrough
))
1534 llvm_unreachable("key missing from Keys");
1538 if (Stream
.failed())
1541 if (!checkMissingKeys(Top
, Keys
))
1544 // Now that we sucessefully parsed the YAML file, canonicalize the internal
1545 // representation to a proper directory tree so that we can search faster
1547 for (auto &E
: RootEntries
)
1548 uniqueOverlayTree(FS
, E
.get());
1554 RedirectingFileSystem
*
1555 RedirectingFileSystem::create(std::unique_ptr
<MemoryBuffer
> Buffer
,
1556 SourceMgr::DiagHandlerTy DiagHandler
,
1557 StringRef YAMLFilePath
, void *DiagContext
,
1558 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
1560 yaml::Stream
Stream(Buffer
->getMemBufferRef(), SM
);
1562 SM
.setDiagHandler(DiagHandler
, DiagContext
);
1563 yaml::document_iterator DI
= Stream
.begin();
1564 yaml::Node
*Root
= DI
->getRoot();
1565 if (DI
== Stream
.end() || !Root
) {
1566 SM
.PrintMessage(SMLoc(), SourceMgr::DK_Error
, "expected root node");
1570 RedirectingFileSystemParser
P(Stream
);
1572 std::unique_ptr
<RedirectingFileSystem
> FS(
1573 new RedirectingFileSystem(std::move(ExternalFS
)));
1575 if (!YAMLFilePath
.empty()) {
1576 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
1577 // to each 'external-contents' path.
1580 // -ivfsoverlay dummy.cache/vfs/vfs.yaml
1582 // FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs
1584 SmallString
<256> OverlayAbsDir
= sys::path::parent_path(YAMLFilePath
);
1585 std::error_code EC
= llvm::sys::fs::make_absolute(OverlayAbsDir
);
1586 assert(!EC
&& "Overlay dir final path must be absolute");
1588 FS
->setExternalContentsPrefixDir(OverlayAbsDir
);
1591 if (!P
.parse(Root
, FS
.get()))
1594 return FS
.release();
1597 ErrorOr
<RedirectingFileSystem::Entry
*>
1598 RedirectingFileSystem::lookupPath(const Twine
&Path_
) const {
1599 SmallString
<256> Path
;
1600 Path_
.toVector(Path
);
1602 // Handle relative paths
1603 if (std::error_code EC
= makeAbsolute(Path
))
1606 // Canonicalize path by removing ".", "..", "./", etc components. This is
1607 // a VFS request, do bot bother about symlinks in the path components
1608 // but canonicalize in order to perform the correct entry search.
1609 if (UseCanonicalizedPaths
) {
1610 Path
= sys::path::remove_leading_dotslash(Path
);
1611 sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
1615 return make_error_code(llvm::errc::invalid_argument
);
1617 sys::path::const_iterator Start
= sys::path::begin(Path
);
1618 sys::path::const_iterator End
= sys::path::end(Path
);
1619 for (const auto &Root
: Roots
) {
1620 ErrorOr
<RedirectingFileSystem::Entry
*> Result
=
1621 lookupPath(Start
, End
, Root
.get());
1622 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
1625 return make_error_code(llvm::errc::no_such_file_or_directory
);
1628 ErrorOr
<RedirectingFileSystem::Entry
*>
1629 RedirectingFileSystem::lookupPath(sys::path::const_iterator Start
,
1630 sys::path::const_iterator End
,
1631 RedirectingFileSystem::Entry
*From
) const {
1633 assert(!isTraversalComponent(*Start
) &&
1634 !isTraversalComponent(From
->getName()) &&
1635 "Paths should not contain traversal components");
1637 // FIXME: this is here to support windows, remove it once canonicalized
1638 // paths become globally default.
1639 if (Start
->equals("."))
1643 StringRef FromName
= From
->getName();
1645 // Forward the search to the next component in case this is an empty one.
1646 if (!FromName
.empty()) {
1647 if (CaseSensitive
? !Start
->equals(FromName
)
1648 : !Start
->equals_lower(FromName
))
1650 return make_error_code(llvm::errc::no_such_file_or_directory
);
1660 auto *DE
= dyn_cast
<RedirectingFileSystem::RedirectingDirectoryEntry
>(From
);
1662 return make_error_code(llvm::errc::not_a_directory
);
1664 for (const std::unique_ptr
<RedirectingFileSystem::Entry
> &DirEntry
:
1665 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
1666 ErrorOr
<RedirectingFileSystem::Entry
*> Result
=
1667 lookupPath(Start
, End
, DirEntry
.get());
1668 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
1671 return make_error_code(llvm::errc::no_such_file_or_directory
);
1674 static Status
getRedirectedFileStatus(const Twine
&Path
, bool UseExternalNames
,
1675 Status ExternalStatus
) {
1676 Status S
= ExternalStatus
;
1677 if (!UseExternalNames
)
1678 S
= Status::copyWithNewName(S
, Path
.str());
1679 S
.IsVFSMapped
= true;
1683 ErrorOr
<Status
> RedirectingFileSystem::status(const Twine
&Path
,
1684 RedirectingFileSystem::Entry
*E
) {
1685 assert(E
!= nullptr);
1686 if (auto *F
= dyn_cast
<RedirectingFileSystem::RedirectingFileEntry
>(E
)) {
1687 ErrorOr
<Status
> S
= ExternalFS
->status(F
->getExternalContentsPath());
1688 assert(!S
|| S
->getName() == F
->getExternalContentsPath());
1690 return getRedirectedFileStatus(Path
, F
->useExternalName(UseExternalNames
),
1693 } else { // directory
1694 auto *DE
= cast
<RedirectingFileSystem::RedirectingDirectoryEntry
>(E
);
1695 return Status::copyWithNewName(DE
->getStatus(), Path
.str());
1699 ErrorOr
<Status
> RedirectingFileSystem::status(const Twine
&Path
) {
1700 ErrorOr
<RedirectingFileSystem::Entry
*> Result
= lookupPath(Path
);
1702 if (IsFallthrough
&&
1703 Result
.getError() == llvm::errc::no_such_file_or_directory
) {
1704 return ExternalFS
->status(Path
);
1706 return Result
.getError();
1708 return status(Path
, *Result
);
1713 /// Provide a file wrapper with an overriden status.
1714 class FileWithFixedStatus
: public File
{
1715 std::unique_ptr
<File
> InnerFile
;
1719 FileWithFixedStatus(std::unique_ptr
<File
> InnerFile
, Status S
)
1720 : InnerFile(std::move(InnerFile
)), S(std::move(S
)) {}
1722 ErrorOr
<Status
> status() override
{ return S
; }
1723 ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
1725 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
1726 bool IsVolatile
) override
{
1727 return InnerFile
->getBuffer(Name
, FileSize
, RequiresNullTerminator
,
1731 std::error_code
close() override
{ return InnerFile
->close(); }
1736 ErrorOr
<std::unique_ptr
<File
>>
1737 RedirectingFileSystem::openFileForRead(const Twine
&Path
) {
1738 ErrorOr
<RedirectingFileSystem::Entry
*> E
= lookupPath(Path
);
1740 if (IsFallthrough
&&
1741 E
.getError() == llvm::errc::no_such_file_or_directory
) {
1742 return ExternalFS
->openFileForRead(Path
);
1744 return E
.getError();
1747 auto *F
= dyn_cast
<RedirectingFileSystem::RedirectingFileEntry
>(*E
);
1748 if (!F
) // FIXME: errc::not_a_file?
1749 return make_error_code(llvm::errc::invalid_argument
);
1751 auto Result
= ExternalFS
->openFileForRead(F
->getExternalContentsPath());
1755 auto ExternalStatus
= (*Result
)->status();
1756 if (!ExternalStatus
)
1757 return ExternalStatus
.getError();
1759 // FIXME: Update the status with the name and VFSMapped.
1760 Status S
= getRedirectedFileStatus(Path
, F
->useExternalName(UseExternalNames
),
1762 return std::unique_ptr
<File
>(
1763 llvm::make_unique
<FileWithFixedStatus
>(std::move(*Result
), S
));
1767 RedirectingFileSystem::getRealPath(const Twine
&Path
,
1768 SmallVectorImpl
<char> &Output
) const {
1769 ErrorOr
<RedirectingFileSystem::Entry
*> Result
= lookupPath(Path
);
1771 if (IsFallthrough
&&
1772 Result
.getError() == llvm::errc::no_such_file_or_directory
) {
1773 return ExternalFS
->getRealPath(Path
, Output
);
1775 return Result
.getError();
1779 dyn_cast
<RedirectingFileSystem::RedirectingFileEntry
>(*Result
)) {
1780 return ExternalFS
->getRealPath(F
->getExternalContentsPath(), Output
);
1782 // Even if there is a directory entry, fall back to ExternalFS if allowed,
1783 // because directories don't have a single external contents path.
1784 return IsFallthrough
? ExternalFS
->getRealPath(Path
, Output
)
1785 : llvm::errc::invalid_argument
;
1788 IntrusiveRefCntPtr
<FileSystem
>
1789 vfs::getVFSFromYAML(std::unique_ptr
<MemoryBuffer
> Buffer
,
1790 SourceMgr::DiagHandlerTy DiagHandler
,
1791 StringRef YAMLFilePath
, void *DiagContext
,
1792 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
1793 return RedirectingFileSystem::create(std::move(Buffer
), DiagHandler
,
1794 YAMLFilePath
, DiagContext
,
1795 std::move(ExternalFS
));
1798 static void getVFSEntries(RedirectingFileSystem::Entry
*SrcE
,
1799 SmallVectorImpl
<StringRef
> &Path
,
1800 SmallVectorImpl
<YAMLVFSEntry
> &Entries
) {
1801 auto Kind
= SrcE
->getKind();
1802 if (Kind
== RedirectingFileSystem::EK_Directory
) {
1803 auto *DE
= dyn_cast
<RedirectingFileSystem::RedirectingDirectoryEntry
>(SrcE
);
1804 assert(DE
&& "Must be a directory");
1805 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &SubEntry
:
1806 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
1807 Path
.push_back(SubEntry
->getName());
1808 getVFSEntries(SubEntry
.get(), Path
, Entries
);
1814 assert(Kind
== RedirectingFileSystem::EK_File
&& "Must be a EK_File");
1815 auto *FE
= dyn_cast
<RedirectingFileSystem::RedirectingFileEntry
>(SrcE
);
1816 assert(FE
&& "Must be a file");
1817 SmallString
<128> VPath
;
1818 for (auto &Comp
: Path
)
1819 llvm::sys::path::append(VPath
, Comp
);
1820 Entries
.push_back(YAMLVFSEntry(VPath
.c_str(), FE
->getExternalContentsPath()));
1823 void vfs::collectVFSFromYAML(std::unique_ptr
<MemoryBuffer
> Buffer
,
1824 SourceMgr::DiagHandlerTy DiagHandler
,
1825 StringRef YAMLFilePath
,
1826 SmallVectorImpl
<YAMLVFSEntry
> &CollectedEntries
,
1828 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
1829 RedirectingFileSystem
*VFS
= RedirectingFileSystem::create(
1830 std::move(Buffer
), DiagHandler
, YAMLFilePath
, DiagContext
,
1831 std::move(ExternalFS
));
1832 ErrorOr
<RedirectingFileSystem::Entry
*> RootE
= VFS
->lookupPath("/");
1835 SmallVector
<StringRef
, 8> Components
;
1836 Components
.push_back("/");
1837 getVFSEntries(*RootE
, Components
, CollectedEntries
);
1840 UniqueID
vfs::getNextVirtualUniqueID() {
1841 static std::atomic
<unsigned> UID
;
1842 unsigned ID
= ++UID
;
1843 // The following assumes that uint64_t max will never collide with a real
1844 // dev_t value from the OS.
1845 return UniqueID(std::numeric_limits
<uint64_t>::max(), ID
);
1848 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath
, StringRef RealPath
) {
1849 assert(sys::path::is_absolute(VirtualPath
) && "virtual path not absolute");
1850 assert(sys::path::is_absolute(RealPath
) && "real path not absolute");
1851 assert(!pathHasTraversal(VirtualPath
) && "path traversal is not supported");
1852 Mappings
.emplace_back(VirtualPath
, RealPath
);
1858 llvm::raw_ostream
&OS
;
1859 SmallVector
<StringRef
, 16> DirStack
;
1861 unsigned getDirIndent() { return 4 * DirStack
.size(); }
1862 unsigned getFileIndent() { return 4 * (DirStack
.size() + 1); }
1863 bool containedIn(StringRef Parent
, StringRef Path
);
1864 StringRef
containedPart(StringRef Parent
, StringRef Path
);
1865 void startDirectory(StringRef Path
);
1866 void endDirectory();
1867 void writeEntry(StringRef VPath
, StringRef RPath
);
1870 JSONWriter(llvm::raw_ostream
&OS
) : OS(OS
) {}
1872 void write(ArrayRef
<YAMLVFSEntry
> Entries
, Optional
<bool> UseExternalNames
,
1873 Optional
<bool> IsCaseSensitive
, Optional
<bool> IsOverlayRelative
,
1874 StringRef OverlayDir
);
1879 bool JSONWriter::containedIn(StringRef Parent
, StringRef Path
) {
1880 using namespace llvm::sys
;
1882 // Compare each path component.
1883 auto IParent
= path::begin(Parent
), EParent
= path::end(Parent
);
1884 for (auto IChild
= path::begin(Path
), EChild
= path::end(Path
);
1885 IParent
!= EParent
&& IChild
!= EChild
; ++IParent
, ++IChild
) {
1886 if (*IParent
!= *IChild
)
1889 // Have we exhausted the parent path?
1890 return IParent
== EParent
;
1893 StringRef
JSONWriter::containedPart(StringRef Parent
, StringRef Path
) {
1894 assert(!Parent
.empty());
1895 assert(containedIn(Parent
, Path
));
1896 return Path
.slice(Parent
.size() + 1, StringRef::npos
);
1899 void JSONWriter::startDirectory(StringRef Path
) {
1901 DirStack
.empty() ? Path
: containedPart(DirStack
.back(), Path
);
1902 DirStack
.push_back(Path
);
1903 unsigned Indent
= getDirIndent();
1904 OS
.indent(Indent
) << "{\n";
1905 OS
.indent(Indent
+ 2) << "'type': 'directory',\n";
1906 OS
.indent(Indent
+ 2) << "'name': \"" << llvm::yaml::escape(Name
) << "\",\n";
1907 OS
.indent(Indent
+ 2) << "'contents': [\n";
1910 void JSONWriter::endDirectory() {
1911 unsigned Indent
= getDirIndent();
1912 OS
.indent(Indent
+ 2) << "]\n";
1913 OS
.indent(Indent
) << "}";
1915 DirStack
.pop_back();
1918 void JSONWriter::writeEntry(StringRef VPath
, StringRef RPath
) {
1919 unsigned Indent
= getFileIndent();
1920 OS
.indent(Indent
) << "{\n";
1921 OS
.indent(Indent
+ 2) << "'type': 'file',\n";
1922 OS
.indent(Indent
+ 2) << "'name': \"" << llvm::yaml::escape(VPath
) << "\",\n";
1923 OS
.indent(Indent
+ 2) << "'external-contents': \""
1924 << llvm::yaml::escape(RPath
) << "\"\n";
1925 OS
.indent(Indent
) << "}";
1928 void JSONWriter::write(ArrayRef
<YAMLVFSEntry
> Entries
,
1929 Optional
<bool> UseExternalNames
,
1930 Optional
<bool> IsCaseSensitive
,
1931 Optional
<bool> IsOverlayRelative
,
1932 StringRef OverlayDir
) {
1933 using namespace llvm::sys
;
1937 if (IsCaseSensitive
.hasValue())
1938 OS
<< " 'case-sensitive': '"
1939 << (IsCaseSensitive
.getValue() ? "true" : "false") << "',\n";
1940 if (UseExternalNames
.hasValue())
1941 OS
<< " 'use-external-names': '"
1942 << (UseExternalNames
.getValue() ? "true" : "false") << "',\n";
1943 bool UseOverlayRelative
= false;
1944 if (IsOverlayRelative
.hasValue()) {
1945 UseOverlayRelative
= IsOverlayRelative
.getValue();
1946 OS
<< " 'overlay-relative': '" << (UseOverlayRelative
? "true" : "false")
1949 OS
<< " 'roots': [\n";
1951 if (!Entries
.empty()) {
1952 const YAMLVFSEntry
&Entry
= Entries
.front();
1953 startDirectory(path::parent_path(Entry
.VPath
));
1955 StringRef RPath
= Entry
.RPath
;
1956 if (UseOverlayRelative
) {
1957 unsigned OverlayDirLen
= OverlayDir
.size();
1958 assert(RPath
.substr(0, OverlayDirLen
) == OverlayDir
&&
1959 "Overlay dir must be contained in RPath");
1960 RPath
= RPath
.slice(OverlayDirLen
, RPath
.size());
1963 writeEntry(path::filename(Entry
.VPath
), RPath
);
1965 for (const auto &Entry
: Entries
.slice(1)) {
1966 StringRef Dir
= path::parent_path(Entry
.VPath
);
1967 if (Dir
== DirStack
.back())
1970 while (!DirStack
.empty() && !containedIn(DirStack
.back(), Dir
)) {
1975 startDirectory(Dir
);
1977 StringRef RPath
= Entry
.RPath
;
1978 if (UseOverlayRelative
) {
1979 unsigned OverlayDirLen
= OverlayDir
.size();
1980 assert(RPath
.substr(0, OverlayDirLen
) == OverlayDir
&&
1981 "Overlay dir must be contained in RPath");
1982 RPath
= RPath
.slice(OverlayDirLen
, RPath
.size());
1984 writeEntry(path::filename(Entry
.VPath
), RPath
);
1987 while (!DirStack
.empty()) {
1998 void YAMLVFSWriter::write(llvm::raw_ostream
&OS
) {
1999 llvm::sort(Mappings
, [](const YAMLVFSEntry
&LHS
, const YAMLVFSEntry
&RHS
) {
2000 return LHS
.VPath
< RHS
.VPath
;
2003 JSONWriter(OS
).write(Mappings
, UseExternalNames
, IsCaseSensitive
,
2004 IsOverlayRelative
, OverlayDir
);
2007 VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(
2009 RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin
,
2010 RedirectingFileSystem::RedirectingDirectoryEntry::iterator End
,
2011 bool IterateExternalFS
, FileSystem
&ExternalFS
, std::error_code
&EC
)
2012 : Dir(_Path
.str()), Current(Begin
), End(End
),
2013 IterateExternalFS(IterateExternalFS
), ExternalFS(ExternalFS
) {
2014 EC
= incrementImpl(/*IsFirstTime=*/true);
2017 std::error_code
VFSFromYamlDirIterImpl::increment() {
2018 return incrementImpl(/*IsFirstTime=*/false);
2021 std::error_code
VFSFromYamlDirIterImpl::incrementExternal() {
2022 assert(!(IsExternalFSCurrent
&& ExternalDirIter
== directory_iterator()) &&
2023 "incrementing past end");
2025 if (IsExternalFSCurrent
) {
2026 ExternalDirIter
.increment(EC
);
2027 } else if (IterateExternalFS
) {
2028 ExternalDirIter
= ExternalFS
.dir_begin(Dir
, EC
);
2029 IsExternalFSCurrent
= true;
2030 if (EC
&& EC
!= errc::no_such_file_or_directory
)
2034 if (EC
|| ExternalDirIter
== directory_iterator()) {
2035 CurrentEntry
= directory_entry();
2037 CurrentEntry
= *ExternalDirIter
;
2042 std::error_code
VFSFromYamlDirIterImpl::incrementContent(bool IsFirstTime
) {
2043 assert((IsFirstTime
|| Current
!= End
) && "cannot iterate past end");
2046 while (Current
!= End
) {
2047 SmallString
<128> PathStr(Dir
);
2048 llvm::sys::path::append(PathStr
, (*Current
)->getName());
2049 sys::fs::file_type Type
;
2050 switch ((*Current
)->getKind()) {
2051 case RedirectingFileSystem::EK_Directory
:
2052 Type
= sys::fs::file_type::directory_file
;
2054 case RedirectingFileSystem::EK_File
:
2055 Type
= sys::fs::file_type::regular_file
;
2058 CurrentEntry
= directory_entry(PathStr
.str(), Type
);
2061 return incrementExternal();
2064 std::error_code
VFSFromYamlDirIterImpl::incrementImpl(bool IsFirstTime
) {
2066 std::error_code EC
= IsExternalFSCurrent
? incrementExternal()
2067 : incrementContent(IsFirstTime
);
2068 if (EC
|| CurrentEntry
.path().empty())
2070 StringRef Name
= llvm::sys::path::filename(CurrentEntry
.path());
2071 if (SeenNames
.insert(Name
).second
)
2072 return EC
; // name not seen before
2074 llvm_unreachable("returned above");
2077 vfs::recursive_directory_iterator::recursive_directory_iterator(
2078 FileSystem
&FS_
, const Twine
&Path
, std::error_code
&EC
)
2080 directory_iterator I
= FS
->dir_begin(Path
, EC
);
2081 if (I
!= directory_iterator()) {
2082 State
= std::make_shared
<detail::RecDirIterState
>();
2083 State
->Stack
.push(I
);
2087 vfs::recursive_directory_iterator
&
2088 recursive_directory_iterator::increment(std::error_code
&EC
) {
2089 assert(FS
&& State
&& !State
->Stack
.empty() && "incrementing past end");
2090 assert(!State
->Stack
.top()->path().empty() && "non-canonical end iterator");
2091 vfs::directory_iterator End
;
2093 if (State
->HasNoPushRequest
)
2094 State
->HasNoPushRequest
= false;
2096 if (State
->Stack
.top()->type() == sys::fs::file_type::directory_file
) {
2097 vfs::directory_iterator I
= FS
->dir_begin(State
->Stack
.top()->path(), EC
);
2099 State
->Stack
.push(I
);
2105 while (!State
->Stack
.empty() && State
->Stack
.top().increment(EC
) == End
)
2108 if (State
->Stack
.empty())
2109 State
.reset(); // end iterator