1 //===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements the VirtualFileSystem interface.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/Support/VirtualFileSystem.h"
14 #include "llvm/ADT/ArrayRef.h"
15 #include "llvm/ADT/DenseMap.h"
16 #include "llvm/ADT/IntrusiveRefCntPtr.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/SmallString.h"
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/ADT/StringSet.h"
22 #include "llvm/ADT/Twine.h"
23 #include "llvm/ADT/iterator_range.h"
24 #include "llvm/Config/llvm-config.h"
25 #include "llvm/Support/Casting.h"
26 #include "llvm/Support/Chrono.h"
27 #include "llvm/Support/Compiler.h"
28 #include "llvm/Support/Debug.h"
29 #include "llvm/Support/Errc.h"
30 #include "llvm/Support/ErrorHandling.h"
31 #include "llvm/Support/ErrorOr.h"
32 #include "llvm/Support/FileSystem.h"
33 #include "llvm/Support/FileSystem/UniqueID.h"
34 #include "llvm/Support/MemoryBuffer.h"
35 #include "llvm/Support/Path.h"
36 #include "llvm/Support/SMLoc.h"
37 #include "llvm/Support/SourceMgr.h"
38 #include "llvm/Support/YAMLParser.h"
39 #include "llvm/Support/raw_ostream.h"
49 #include <system_error>
54 using namespace llvm::vfs
;
56 using llvm::sys::fs::file_t
;
57 using llvm::sys::fs::file_status
;
58 using llvm::sys::fs::file_type
;
59 using llvm::sys::fs::kInvalidFile
;
60 using llvm::sys::fs::perms
;
61 using llvm::sys::fs::UniqueID
;
63 Status::Status(const file_status
&Status
)
64 : UID(Status
.getUniqueID()), MTime(Status
.getLastModificationTime()),
65 User(Status
.getUser()), Group(Status
.getGroup()), Size(Status
.getSize()),
66 Type(Status
.type()), Perms(Status
.permissions()) {}
68 Status::Status(const Twine
&Name
, UniqueID UID
, sys::TimePoint
<> MTime
,
69 uint32_t User
, uint32_t Group
, uint64_t Size
, file_type Type
,
71 : Name(Name
.str()), UID(UID
), MTime(MTime
), User(User
), Group(Group
),
72 Size(Size
), Type(Type
), Perms(Perms
) {}
74 Status
Status::copyWithNewSize(const Status
&In
, uint64_t NewSize
) {
75 return Status(In
.getName(), In
.getUniqueID(), In
.getLastModificationTime(),
76 In
.getUser(), In
.getGroup(), NewSize
, In
.getType(),
80 Status
Status::copyWithNewName(const Status
&In
, const Twine
&NewName
) {
81 return Status(NewName
, In
.getUniqueID(), In
.getLastModificationTime(),
82 In
.getUser(), In
.getGroup(), In
.getSize(), In
.getType(),
86 Status
Status::copyWithNewName(const file_status
&In
, const Twine
&NewName
) {
87 return Status(NewName
, In
.getUniqueID(), In
.getLastModificationTime(),
88 In
.getUser(), In
.getGroup(), In
.getSize(), In
.type(),
92 bool Status::equivalent(const Status
&Other
) const {
93 assert(isStatusKnown() && Other
.isStatusKnown());
94 return getUniqueID() == Other
.getUniqueID();
97 bool Status::isDirectory() const { return Type
== file_type::directory_file
; }
99 bool Status::isRegularFile() const { return Type
== file_type::regular_file
; }
101 bool Status::isOther() const {
102 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
105 bool Status::isSymlink() const { return Type
== file_type::symlink_file
; }
107 bool Status::isStatusKnown() const { return Type
!= file_type::status_error
; }
109 bool Status::exists() const {
110 return isStatusKnown() && Type
!= file_type::file_not_found
;
113 File::~File() = default;
115 FileSystem::~FileSystem() = default;
117 ErrorOr
<std::unique_ptr
<MemoryBuffer
>>
118 FileSystem::getBufferForFile(const llvm::Twine
&Name
, int64_t FileSize
,
119 bool RequiresNullTerminator
, bool IsVolatile
,
121 auto F
= IsText
? openFileForRead(Name
) : openFileForReadBinary(Name
);
125 return (*F
)->getBuffer(Name
, FileSize
, RequiresNullTerminator
, IsVolatile
);
128 std::error_code
FileSystem::makeAbsolute(SmallVectorImpl
<char> &Path
) const {
129 if (llvm::sys::path::is_absolute(Path
))
132 auto WorkingDir
= getCurrentWorkingDirectory();
134 return WorkingDir
.getError();
136 llvm::sys::fs::make_absolute(WorkingDir
.get(), Path
);
140 std::error_code
FileSystem::getRealPath(const Twine
&Path
,
141 SmallVectorImpl
<char> &Output
) {
142 return errc::operation_not_permitted
;
145 std::error_code
FileSystem::isLocal(const Twine
&Path
, bool &Result
) {
146 return errc::operation_not_permitted
;
149 bool FileSystem::exists(const Twine
&Path
) {
150 auto Status
= status(Path
);
151 return Status
&& Status
->exists();
154 llvm::ErrorOr
<bool> FileSystem::equivalent(const Twine
&A
, const Twine
&B
) {
155 auto StatusA
= status(A
);
157 return StatusA
.getError();
158 auto StatusB
= status(B
);
160 return StatusB
.getError();
161 return StatusA
->equivalent(*StatusB
);
164 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
165 void FileSystem::dump() const { print(dbgs(), PrintType::RecursiveContents
); }
169 static bool isTraversalComponent(StringRef Component
) {
170 return Component
== ".." || Component
== ".";
173 static bool pathHasTraversal(StringRef Path
) {
174 using namespace llvm::sys
;
176 for (StringRef Comp
: llvm::make_range(path::begin(Path
), path::end(Path
)))
177 if (isTraversalComponent(Comp
))
183 //===-----------------------------------------------------------------------===/
184 // RealFileSystem implementation
185 //===-----------------------------------------------------------------------===/
189 /// Wrapper around a raw file descriptor.
190 class RealFile
: public File
{
191 friend class RealFileSystem
;
195 std::string RealName
;
197 RealFile(file_t RawFD
, StringRef NewName
, StringRef NewRealPathName
)
198 : FD(RawFD
), S(NewName
, {}, {}, {}, {}, {},
199 llvm::sys::fs::file_type::status_error
, {}),
200 RealName(NewRealPathName
.str()) {
201 assert(FD
!= kInvalidFile
&& "Invalid or inactive file descriptor");
205 ~RealFile() override
;
207 ErrorOr
<Status
> status() override
;
208 ErrorOr
<std::string
> getName() override
;
209 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> getBuffer(const Twine
&Name
,
211 bool RequiresNullTerminator
,
212 bool IsVolatile
) override
;
213 std::error_code
close() override
;
214 void setPath(const Twine
&Path
) override
;
219 RealFile::~RealFile() { close(); }
221 ErrorOr
<Status
> RealFile::status() {
222 assert(FD
!= kInvalidFile
&& "cannot stat closed file");
223 if (!S
.isStatusKnown()) {
224 file_status RealStatus
;
225 if (std::error_code EC
= sys::fs::status(FD
, RealStatus
))
227 S
= Status::copyWithNewName(RealStatus
, S
.getName());
232 ErrorOr
<std::string
> RealFile::getName() {
233 return RealName
.empty() ? S
.getName().str() : RealName
;
236 ErrorOr
<std::unique_ptr
<MemoryBuffer
>>
237 RealFile::getBuffer(const Twine
&Name
, int64_t FileSize
,
238 bool RequiresNullTerminator
, bool IsVolatile
) {
239 assert(FD
!= kInvalidFile
&& "cannot get buffer for closed file");
240 return MemoryBuffer::getOpenFile(FD
, Name
, FileSize
, RequiresNullTerminator
,
244 std::error_code
RealFile::close() {
245 std::error_code EC
= sys::fs::closeFile(FD
);
250 void RealFile::setPath(const Twine
&Path
) {
251 RealName
= Path
.str();
252 if (auto Status
= status())
253 S
= Status
.get().copyWithNewName(Status
.get(), Path
);
258 /// A file system according to your operating system.
259 /// This may be linked to the process's working directory, or maintain its own.
261 /// Currently, its own working directory is emulated by storing the path and
262 /// sending absolute paths to llvm::sys::fs:: functions.
263 /// A more principled approach would be to push this down a level, modelling
264 /// the working dir as an llvm::sys::fs::WorkingDir or similar.
265 /// This would enable the use of openat()-style functions on some platforms.
266 class RealFileSystem
: public FileSystem
{
268 explicit RealFileSystem(bool LinkCWDToProcess
) {
269 if (!LinkCWDToProcess
) {
270 SmallString
<128> PWD
, RealPWD
;
271 if (std::error_code EC
= llvm::sys::fs::current_path(PWD
))
273 else if (llvm::sys::fs::real_path(PWD
, RealPWD
))
274 WD
= WorkingDirectory
{PWD
, PWD
};
276 WD
= WorkingDirectory
{PWD
, RealPWD
};
280 ErrorOr
<Status
> status(const Twine
&Path
) override
;
281 ErrorOr
<std::unique_ptr
<File
>> openFileForRead(const Twine
&Path
) override
;
282 ErrorOr
<std::unique_ptr
<File
>>
283 openFileForReadBinary(const Twine
&Path
) override
;
284 directory_iterator
dir_begin(const Twine
&Dir
, std::error_code
&EC
) override
;
286 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
;
287 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
;
288 std::error_code
isLocal(const Twine
&Path
, bool &Result
) override
;
289 std::error_code
getRealPath(const Twine
&Path
,
290 SmallVectorImpl
<char> &Output
) override
;
293 void printImpl(raw_ostream
&OS
, PrintType Type
,
294 unsigned IndentLevel
) const override
;
297 // If this FS has its own working dir, use it to make Path absolute.
298 // The returned twine is safe to use as long as both Storage and Path live.
299 Twine
adjustPath(const Twine
&Path
, SmallVectorImpl
<char> &Storage
) const {
302 Path
.toVector(Storage
);
303 sys::fs::make_absolute(WD
->get().Resolved
, Storage
);
307 ErrorOr
<std::unique_ptr
<File
>>
308 openFileForReadWithFlags(const Twine
&Name
, sys::fs::OpenFlags Flags
) {
309 SmallString
<256> RealName
, Storage
;
310 Expected
<file_t
> FDOrErr
= sys::fs::openNativeFileForRead(
311 adjustPath(Name
, Storage
), Flags
, &RealName
);
313 return errorToErrorCode(FDOrErr
.takeError());
314 return std::unique_ptr
<File
>(
315 new RealFile(*FDOrErr
, Name
.str(), RealName
.str()));
318 struct WorkingDirectory
{
319 // The current working directory, without symlinks resolved. (echo $PWD).
320 SmallString
<128> Specified
;
321 // The current working directory, with links resolved. (readlink .).
322 SmallString
<128> Resolved
;
324 std::optional
<llvm::ErrorOr
<WorkingDirectory
>> WD
;
329 ErrorOr
<Status
> RealFileSystem::status(const Twine
&Path
) {
330 SmallString
<256> Storage
;
331 sys::fs::file_status RealStatus
;
332 if (std::error_code EC
=
333 sys::fs::status(adjustPath(Path
, Storage
), RealStatus
))
335 return Status::copyWithNewName(RealStatus
, Path
);
338 ErrorOr
<std::unique_ptr
<File
>>
339 RealFileSystem::openFileForRead(const Twine
&Name
) {
340 return openFileForReadWithFlags(Name
, sys::fs::OF_Text
);
343 ErrorOr
<std::unique_ptr
<File
>>
344 RealFileSystem::openFileForReadBinary(const Twine
&Name
) {
345 return openFileForReadWithFlags(Name
, sys::fs::OF_None
);
348 llvm::ErrorOr
<std::string
> RealFileSystem::getCurrentWorkingDirectory() const {
350 return std::string(WD
->get().Specified
);
352 return WD
->getError();
354 SmallString
<128> Dir
;
355 if (std::error_code EC
= llvm::sys::fs::current_path(Dir
))
357 return std::string(Dir
);
360 std::error_code
RealFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
362 return llvm::sys::fs::set_current_path(Path
);
364 SmallString
<128> Absolute
, Resolved
, Storage
;
365 adjustPath(Path
, Storage
).toVector(Absolute
);
367 if (auto Err
= llvm::sys::fs::is_directory(Absolute
, IsDir
))
370 return std::make_error_code(std::errc::not_a_directory
);
371 if (auto Err
= llvm::sys::fs::real_path(Absolute
, Resolved
))
373 WD
= WorkingDirectory
{Absolute
, Resolved
};
374 return std::error_code();
377 std::error_code
RealFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
378 SmallString
<256> Storage
;
379 return llvm::sys::fs::is_local(adjustPath(Path
, Storage
), Result
);
382 std::error_code
RealFileSystem::getRealPath(const Twine
&Path
,
383 SmallVectorImpl
<char> &Output
) {
384 SmallString
<256> Storage
;
385 return llvm::sys::fs::real_path(adjustPath(Path
, Storage
), Output
);
388 void RealFileSystem::printImpl(raw_ostream
&OS
, PrintType Type
,
389 unsigned IndentLevel
) const {
390 printIndent(OS
, IndentLevel
);
391 OS
<< "RealFileSystem using ";
399 IntrusiveRefCntPtr
<FileSystem
> vfs::getRealFileSystem() {
400 static IntrusiveRefCntPtr
<FileSystem
> FS(new RealFileSystem(true));
404 std::unique_ptr
<FileSystem
> vfs::createPhysicalFileSystem() {
405 return std::make_unique
<RealFileSystem
>(false);
410 class RealFSDirIter
: public llvm::vfs::detail::DirIterImpl
{
411 llvm::sys::fs::directory_iterator Iter
;
414 RealFSDirIter(const Twine
&Path
, std::error_code
&EC
) : Iter(Path
, EC
) {
415 if (Iter
!= llvm::sys::fs::directory_iterator())
416 CurrentEntry
= directory_entry(Iter
->path(), Iter
->type());
419 std::error_code
increment() override
{
422 CurrentEntry
= (Iter
== llvm::sys::fs::directory_iterator())
424 : directory_entry(Iter
->path(), Iter
->type());
431 directory_iterator
RealFileSystem::dir_begin(const Twine
&Dir
,
432 std::error_code
&EC
) {
433 SmallString
<128> Storage
;
434 return directory_iterator(
435 std::make_shared
<RealFSDirIter
>(adjustPath(Dir
, Storage
), EC
));
438 //===-----------------------------------------------------------------------===/
439 // OverlayFileSystem implementation
440 //===-----------------------------------------------------------------------===/
442 OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr
<FileSystem
> BaseFS
) {
443 FSList
.push_back(std::move(BaseFS
));
446 void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr
<FileSystem
> FS
) {
447 FSList
.push_back(FS
);
448 // Synchronize added file systems by duplicating the working directory from
449 // the first one in the list.
450 FS
->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
453 ErrorOr
<Status
> OverlayFileSystem::status(const Twine
&Path
) {
454 // FIXME: handle symlinks that cross file systems
455 for (iterator I
= overlays_begin(), E
= overlays_end(); I
!= E
; ++I
) {
456 ErrorOr
<Status
> Status
= (*I
)->status(Path
);
457 if (Status
|| Status
.getError() != llvm::errc::no_such_file_or_directory
)
460 return make_error_code(llvm::errc::no_such_file_or_directory
);
463 bool OverlayFileSystem::exists(const Twine
&Path
) {
464 // FIXME: handle symlinks that cross file systems
465 for (iterator I
= overlays_begin(), E
= overlays_end(); I
!= E
; ++I
) {
466 if ((*I
)->exists(Path
))
472 ErrorOr
<std::unique_ptr
<File
>>
473 OverlayFileSystem::openFileForRead(const llvm::Twine
&Path
) {
474 // FIXME: handle symlinks that cross file systems
475 for (iterator I
= overlays_begin(), E
= overlays_end(); I
!= E
; ++I
) {
476 auto Result
= (*I
)->openFileForRead(Path
);
477 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
480 return make_error_code(llvm::errc::no_such_file_or_directory
);
483 llvm::ErrorOr
<std::string
>
484 OverlayFileSystem::getCurrentWorkingDirectory() const {
485 // All file systems are synchronized, just take the first working directory.
486 return FSList
.front()->getCurrentWorkingDirectory();
490 OverlayFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
491 for (auto &FS
: FSList
)
492 if (std::error_code EC
= FS
->setCurrentWorkingDirectory(Path
))
497 std::error_code
OverlayFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
498 for (auto &FS
: FSList
)
499 if (FS
->exists(Path
))
500 return FS
->isLocal(Path
, Result
);
501 return errc::no_such_file_or_directory
;
504 std::error_code
OverlayFileSystem::getRealPath(const Twine
&Path
,
505 SmallVectorImpl
<char> &Output
) {
506 for (const auto &FS
: FSList
)
507 if (FS
->exists(Path
))
508 return FS
->getRealPath(Path
, Output
);
509 return errc::no_such_file_or_directory
;
512 void OverlayFileSystem::visitChildFileSystems(VisitCallbackTy Callback
) {
513 for (IntrusiveRefCntPtr
<FileSystem
> FS
: overlays_range()) {
515 FS
->visitChildFileSystems(Callback
);
519 void OverlayFileSystem::printImpl(raw_ostream
&OS
, PrintType Type
,
520 unsigned IndentLevel
) const {
521 printIndent(OS
, IndentLevel
);
522 OS
<< "OverlayFileSystem\n";
523 if (Type
== PrintType::Summary
)
526 if (Type
== PrintType::Contents
)
527 Type
= PrintType::Summary
;
528 for (const auto &FS
: overlays_range())
529 FS
->print(OS
, Type
, IndentLevel
+ 1);
532 llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;
536 /// Combines and deduplicates directory entries across multiple file systems.
537 class CombiningDirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
538 using FileSystemPtr
= llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
>;
540 /// Iterators to combine, processed in reverse order.
541 SmallVector
<directory_iterator
, 8> IterList
;
542 /// The iterator currently being traversed.
543 directory_iterator CurrentDirIter
;
544 /// The set of names already returned as entries.
545 llvm::StringSet
<> SeenNames
;
547 /// Sets \c CurrentDirIter to the next iterator in the list, or leaves it as
548 /// is (at its end position) if we've already gone through them all.
549 std::error_code
incrementIter(bool IsFirstTime
) {
550 while (!IterList
.empty()) {
551 CurrentDirIter
= IterList
.back();
553 if (CurrentDirIter
!= directory_iterator())
557 if (IsFirstTime
&& CurrentDirIter
== directory_iterator())
558 return errc::no_such_file_or_directory
;
562 std::error_code
incrementDirIter(bool IsFirstTime
) {
563 assert((IsFirstTime
|| CurrentDirIter
!= directory_iterator()) &&
564 "incrementing past end");
567 CurrentDirIter
.increment(EC
);
568 if (!EC
&& CurrentDirIter
== directory_iterator())
569 EC
= incrementIter(IsFirstTime
);
573 std::error_code
incrementImpl(bool IsFirstTime
) {
575 std::error_code EC
= incrementDirIter(IsFirstTime
);
576 if (EC
|| CurrentDirIter
== directory_iterator()) {
577 CurrentEntry
= directory_entry();
580 CurrentEntry
= *CurrentDirIter
;
581 StringRef Name
= llvm::sys::path::filename(CurrentEntry
.path());
582 if (SeenNames
.insert(Name
).second
)
583 return EC
; // name not seen before
585 llvm_unreachable("returned above");
589 CombiningDirIterImpl(ArrayRef
<FileSystemPtr
> FileSystems
, std::string Dir
,
590 std::error_code
&EC
) {
591 for (const auto &FS
: FileSystems
) {
593 directory_iterator Iter
= FS
->dir_begin(Dir
, FEC
);
594 if (FEC
&& FEC
!= errc::no_such_file_or_directory
) {
599 IterList
.push_back(Iter
);
601 EC
= incrementImpl(true);
604 CombiningDirIterImpl(ArrayRef
<directory_iterator
> DirIters
,
606 : IterList(DirIters
) {
607 EC
= incrementImpl(true);
610 std::error_code
increment() override
{ return incrementImpl(false); }
615 directory_iterator
OverlayFileSystem::dir_begin(const Twine
&Dir
,
616 std::error_code
&EC
) {
617 directory_iterator Combined
= directory_iterator(
618 std::make_shared
<CombiningDirIterImpl
>(FSList
, Dir
.str(), EC
));
624 void ProxyFileSystem::anchor() {}
631 enum InMemoryNodeKind
{
638 /// The in memory file system is a tree of Nodes. Every node can either be a
639 /// file, symlink, hardlink or a directory.
641 InMemoryNodeKind Kind
;
642 std::string FileName
;
645 InMemoryNode(llvm::StringRef FileName
, InMemoryNodeKind Kind
)
646 : Kind(Kind
), FileName(std::string(llvm::sys::path::filename(FileName
))) {
648 virtual ~InMemoryNode() = default;
650 /// Return the \p Status for this node. \p RequestedName should be the name
651 /// through which the caller referred to this node. It will override
652 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
653 virtual Status
getStatus(const Twine
&RequestedName
) const = 0;
655 /// Get the filename of this node (the name without the directory part).
656 StringRef
getFileName() const { return FileName
; }
657 InMemoryNodeKind
getKind() const { return Kind
; }
658 virtual std::string
toString(unsigned Indent
) const = 0;
661 class InMemoryFile
: public InMemoryNode
{
663 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
;
666 InMemoryFile(Status Stat
, std::unique_ptr
<llvm::MemoryBuffer
> Buffer
)
667 : InMemoryNode(Stat
.getName(), IME_File
), Stat(std::move(Stat
)),
668 Buffer(std::move(Buffer
)) {}
670 Status
getStatus(const Twine
&RequestedName
) const override
{
671 return Status::copyWithNewName(Stat
, RequestedName
);
673 llvm::MemoryBuffer
*getBuffer() const { return Buffer
.get(); }
675 std::string
toString(unsigned Indent
) const override
{
676 return (std::string(Indent
, ' ') + Stat
.getName() + "\n").str();
679 static bool classof(const InMemoryNode
*N
) {
680 return N
->getKind() == IME_File
;
686 class InMemoryHardLink
: public InMemoryNode
{
687 const InMemoryFile
&ResolvedFile
;
690 InMemoryHardLink(StringRef Path
, const InMemoryFile
&ResolvedFile
)
691 : InMemoryNode(Path
, IME_HardLink
), ResolvedFile(ResolvedFile
) {}
692 const InMemoryFile
&getResolvedFile() const { return ResolvedFile
; }
694 Status
getStatus(const Twine
&RequestedName
) const override
{
695 return ResolvedFile
.getStatus(RequestedName
);
698 std::string
toString(unsigned Indent
) const override
{
699 return std::string(Indent
, ' ') + "HardLink to -> " +
700 ResolvedFile
.toString(0);
703 static bool classof(const InMemoryNode
*N
) {
704 return N
->getKind() == IME_HardLink
;
708 class InMemorySymbolicLink
: public InMemoryNode
{
709 std::string TargetPath
;
713 InMemorySymbolicLink(StringRef Path
, StringRef TargetPath
, Status Stat
)
714 : InMemoryNode(Path
, IME_SymbolicLink
), TargetPath(std::move(TargetPath
)),
717 std::string
toString(unsigned Indent
) const override
{
718 return std::string(Indent
, ' ') + "SymbolicLink to -> " + TargetPath
;
721 Status
getStatus(const Twine
&RequestedName
) const override
{
722 return Status::copyWithNewName(Stat
, RequestedName
);
725 StringRef
getTargetPath() const { return TargetPath
; }
727 static bool classof(const InMemoryNode
*N
) {
728 return N
->getKind() == IME_SymbolicLink
;
732 /// Adapt a InMemoryFile for VFS' File interface. The goal is to make
733 /// \p InMemoryFileAdaptor mimic as much as possible the behavior of
735 class InMemoryFileAdaptor
: public File
{
736 const InMemoryFile
&Node
;
737 /// The name to use when returning a Status for this file.
738 std::string RequestedName
;
741 explicit InMemoryFileAdaptor(const InMemoryFile
&Node
,
742 std::string RequestedName
)
743 : Node(Node
), RequestedName(std::move(RequestedName
)) {}
745 llvm::ErrorOr
<Status
> status() override
{
746 return Node
.getStatus(RequestedName
);
749 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
750 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
751 bool IsVolatile
) override
{
752 llvm::MemoryBuffer
*Buf
= Node
.getBuffer();
753 return llvm::MemoryBuffer::getMemBuffer(
754 Buf
->getBuffer(), Buf
->getBufferIdentifier(), RequiresNullTerminator
);
757 std::error_code
close() override
{ return {}; }
759 void setPath(const Twine
&Path
) override
{ RequestedName
= Path
.str(); }
763 class InMemoryDirectory
: public InMemoryNode
{
765 std::map
<std::string
, std::unique_ptr
<InMemoryNode
>, std::less
<>> Entries
;
768 InMemoryDirectory(Status Stat
)
769 : InMemoryNode(Stat
.getName(), IME_Directory
), Stat(std::move(Stat
)) {}
771 /// Return the \p Status for this node. \p RequestedName should be the name
772 /// through which the caller referred to this node. It will override
773 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
774 Status
getStatus(const Twine
&RequestedName
) const override
{
775 return Status::copyWithNewName(Stat
, RequestedName
);
778 UniqueID
getUniqueID() const { return Stat
.getUniqueID(); }
780 InMemoryNode
*getChild(StringRef Name
) const {
781 auto I
= Entries
.find(Name
);
782 if (I
!= Entries
.end())
783 return I
->second
.get();
787 InMemoryNode
*addChild(StringRef Name
, std::unique_ptr
<InMemoryNode
> Child
) {
788 return Entries
.emplace(Name
, std::move(Child
)).first
->second
.get();
791 using const_iterator
= decltype(Entries
)::const_iterator
;
793 const_iterator
begin() const { return Entries
.begin(); }
794 const_iterator
end() const { return Entries
.end(); }
796 std::string
toString(unsigned Indent
) const override
{
798 (std::string(Indent
, ' ') + Stat
.getName() + "\n").str();
799 for (const auto &Entry
: Entries
)
800 Result
+= Entry
.second
->toString(Indent
+ 2);
804 static bool classof(const InMemoryNode
*N
) {
805 return N
->getKind() == IME_Directory
;
809 } // namespace detail
811 // The UniqueID of in-memory files is derived from path and content.
812 // This avoids difficulties in creating exactly equivalent in-memory FSes,
813 // as often needed in multithreaded programs.
814 static sys::fs::UniqueID
getUniqueID(hash_code Hash
) {
815 return sys::fs::UniqueID(std::numeric_limits
<uint64_t>::max(),
816 uint64_t(size_t(Hash
)));
818 static sys::fs::UniqueID
getFileID(sys::fs::UniqueID Parent
,
819 llvm::StringRef Name
,
820 llvm::StringRef Contents
) {
821 return getUniqueID(llvm::hash_combine(Parent
.getFile(), Name
, Contents
));
823 static sys::fs::UniqueID
getDirectoryID(sys::fs::UniqueID Parent
,
824 llvm::StringRef Name
) {
825 return getUniqueID(llvm::hash_combine(Parent
.getFile(), Name
));
828 Status
detail::NewInMemoryNodeInfo::makeStatus() const {
830 (Type
== sys::fs::file_type::directory_file
)
831 ? getDirectoryID(DirUID
, Name
)
832 : getFileID(DirUID
, Name
, Buffer
? Buffer
->getBuffer() : "");
834 return Status(Path
, UID
, llvm::sys::toTimePoint(ModificationTime
), User
,
835 Group
, Buffer
? Buffer
->getBufferSize() : 0, Type
, Perms
);
838 InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths
)
839 : Root(new detail::InMemoryDirectory(
840 Status("", getDirectoryID(llvm::sys::fs::UniqueID(), ""),
841 llvm::sys::TimePoint
<>(), 0, 0, 0,
842 llvm::sys::fs::file_type::directory_file
,
843 llvm::sys::fs::perms::all_all
))),
844 UseNormalizedPaths(UseNormalizedPaths
) {}
846 InMemoryFileSystem::~InMemoryFileSystem() = default;
848 std::string
InMemoryFileSystem::toString() const {
849 return Root
->toString(/*Indent=*/0);
852 bool InMemoryFileSystem::addFile(const Twine
&P
, time_t ModificationTime
,
853 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
,
854 std::optional
<uint32_t> User
,
855 std::optional
<uint32_t> Group
,
856 std::optional
<llvm::sys::fs::file_type
> Type
,
857 std::optional
<llvm::sys::fs::perms
> Perms
,
858 MakeNodeFn MakeNode
) {
859 SmallString
<128> Path
;
862 // Fix up relative paths. This just prepends the current working directory.
863 std::error_code EC
= makeAbsolute(Path
);
867 if (useNormalizedPaths())
868 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
873 detail::InMemoryDirectory
*Dir
= Root
.get();
874 auto I
= llvm::sys::path::begin(Path
), E
= sys::path::end(Path
);
875 const auto ResolvedUser
= User
.value_or(0);
876 const auto ResolvedGroup
= Group
.value_or(0);
877 const auto ResolvedType
= Type
.value_or(sys::fs::file_type::regular_file
);
878 const auto ResolvedPerms
= Perms
.value_or(sys::fs::all_all
);
879 // Any intermediate directories we create should be accessible by
880 // the owner, even if Perms says otherwise for the final path.
881 const auto NewDirectoryPerms
= ResolvedPerms
| sys::fs::owner_all
;
889 detail::InMemoryNode
*Node
= Dir
->getChild(Name
);
891 // This isn't the last element, so we create a new directory.
893 StringRef(Path
.str().begin(), Name
.end() - Path
.str().begin()),
894 getDirectoryID(Dir
->getUniqueID(), Name
),
895 llvm::sys::toTimePoint(ModificationTime
), ResolvedUser
, ResolvedGroup
,
896 0, sys::fs::file_type::directory_file
, NewDirectoryPerms
);
897 Dir
= cast
<detail::InMemoryDirectory
>(Dir
->addChild(
898 Name
, std::make_unique
<detail::InMemoryDirectory
>(std::move(Stat
))));
901 // Creating file under another file.
902 if (!isa
<detail::InMemoryDirectory
>(Node
))
904 Dir
= cast
<detail::InMemoryDirectory
>(Node
);
906 detail::InMemoryNode
*Node
= Dir
->getChild(Name
);
909 MakeNode({Dir
->getUniqueID(), Path
, Name
, ModificationTime
,
910 std::move(Buffer
), ResolvedUser
, ResolvedGroup
,
911 ResolvedType
, ResolvedPerms
}));
914 if (isa
<detail::InMemoryDirectory
>(Node
))
915 return ResolvedType
== sys::fs::file_type::directory_file
;
917 assert((isa
<detail::InMemoryFile
>(Node
) ||
918 isa
<detail::InMemoryHardLink
>(Node
)) &&
919 "Must be either file, hardlink or directory!");
921 // Return false only if the new file is different from the existing one.
922 if (auto *Link
= dyn_cast
<detail::InMemoryHardLink
>(Node
)) {
923 return Link
->getResolvedFile().getBuffer()->getBuffer() ==
926 return cast
<detail::InMemoryFile
>(Node
)->getBuffer()->getBuffer() ==
930 bool InMemoryFileSystem::addFile(const Twine
&P
, time_t ModificationTime
,
931 std::unique_ptr
<llvm::MemoryBuffer
> Buffer
,
932 std::optional
<uint32_t> User
,
933 std::optional
<uint32_t> Group
,
934 std::optional
<llvm::sys::fs::file_type
> Type
,
935 std::optional
<llvm::sys::fs::perms
> Perms
) {
936 return addFile(P
, ModificationTime
, std::move(Buffer
), User
, Group
, Type
,
938 [](detail::NewInMemoryNodeInfo NNI
)
939 -> std::unique_ptr
<detail::InMemoryNode
> {
940 Status Stat
= NNI
.makeStatus();
941 if (Stat
.getType() == sys::fs::file_type::directory_file
)
942 return std::make_unique
<detail::InMemoryDirectory
>(Stat
);
943 return std::make_unique
<detail::InMemoryFile
>(
944 Stat
, std::move(NNI
.Buffer
));
948 bool InMemoryFileSystem::addFileNoOwn(
949 const Twine
&P
, time_t ModificationTime
,
950 const llvm::MemoryBufferRef
&Buffer
, std::optional
<uint32_t> User
,
951 std::optional
<uint32_t> Group
, std::optional
<llvm::sys::fs::file_type
> Type
,
952 std::optional
<llvm::sys::fs::perms
> Perms
) {
953 return addFile(P
, ModificationTime
, llvm::MemoryBuffer::getMemBuffer(Buffer
),
954 std::move(User
), std::move(Group
), std::move(Type
),
956 [](detail::NewInMemoryNodeInfo NNI
)
957 -> std::unique_ptr
<detail::InMemoryNode
> {
958 Status Stat
= NNI
.makeStatus();
959 if (Stat
.getType() == sys::fs::file_type::directory_file
)
960 return std::make_unique
<detail::InMemoryDirectory
>(Stat
);
961 return std::make_unique
<detail::InMemoryFile
>(
962 Stat
, std::move(NNI
.Buffer
));
966 detail::NamedNodeOrError
967 InMemoryFileSystem::lookupNode(const Twine
&P
, bool FollowFinalSymlink
,
968 size_t SymlinkDepth
) const {
969 SmallString
<128> Path
;
972 // Fix up relative paths. This just prepends the current working directory.
973 std::error_code EC
= makeAbsolute(Path
);
977 if (useNormalizedPaths())
978 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
980 const detail::InMemoryDirectory
*Dir
= Root
.get();
982 return detail::NamedNodeOrError(Path
, Dir
);
984 auto I
= llvm::sys::path::begin(Path
), E
= llvm::sys::path::end(Path
);
986 detail::InMemoryNode
*Node
= Dir
->getChild(*I
);
989 return errc::no_such_file_or_directory
;
991 if (auto Symlink
= dyn_cast
<detail::InMemorySymbolicLink
>(Node
)) {
992 // If we're at the end of the path, and we're not following through
993 // terminal symlinks, then we're done.
994 if (I
== E
&& !FollowFinalSymlink
)
995 return detail::NamedNodeOrError(Path
, Symlink
);
997 if (SymlinkDepth
> InMemoryFileSystem::MaxSymlinkDepth
)
998 return errc::no_such_file_or_directory
;
1000 SmallString
<128> TargetPath
= Symlink
->getTargetPath();
1001 if (std::error_code EC
= makeAbsolute(TargetPath
))
1004 // Keep going with the target. We always want to follow symlinks here
1005 // because we're either at the end of a path that we want to follow, or
1006 // not at the end of a path, in which case we need to follow the symlink
1009 lookupNode(TargetPath
, /*FollowFinalSymlink=*/true, SymlinkDepth
+ 1);
1010 if (!Target
|| I
== E
)
1013 if (!isa
<detail::InMemoryDirectory
>(*Target
))
1014 return errc::no_such_file_or_directory
;
1016 // Otherwise, continue on the search in the symlinked directory.
1017 Dir
= cast
<detail::InMemoryDirectory
>(*Target
);
1021 // Return the file if it's at the end of the path.
1022 if (auto File
= dyn_cast
<detail::InMemoryFile
>(Node
)) {
1024 return detail::NamedNodeOrError(Path
, File
);
1025 return errc::no_such_file_or_directory
;
1028 // If Node is HardLink then return the resolved file.
1029 if (auto File
= dyn_cast
<detail::InMemoryHardLink
>(Node
)) {
1031 return detail::NamedNodeOrError(Path
, &File
->getResolvedFile());
1032 return errc::no_such_file_or_directory
;
1034 // Traverse directories.
1035 Dir
= cast
<detail::InMemoryDirectory
>(Node
);
1037 return detail::NamedNodeOrError(Path
, Dir
);
1041 bool InMemoryFileSystem::addHardLink(const Twine
&NewLink
,
1042 const Twine
&Target
) {
1043 auto NewLinkNode
= lookupNode(NewLink
, /*FollowFinalSymlink=*/false);
1044 // Whether symlinks in the hardlink target are followed is
1045 // implementation-defined in POSIX.
1046 // We're following symlinks here to be consistent with macOS.
1047 auto TargetNode
= lookupNode(Target
, /*FollowFinalSymlink=*/true);
1048 // FromPath must not have been added before. ToPath must have been added
1049 // before. Resolved ToPath must be a File.
1050 if (!TargetNode
|| NewLinkNode
|| !isa
<detail::InMemoryFile
>(*TargetNode
))
1052 return addFile(NewLink
, 0, nullptr, std::nullopt
, std::nullopt
, std::nullopt
,
1053 std::nullopt
, [&](detail::NewInMemoryNodeInfo NNI
) {
1054 return std::make_unique
<detail::InMemoryHardLink
>(
1056 *cast
<detail::InMemoryFile
>(*TargetNode
));
1060 bool InMemoryFileSystem::addSymbolicLink(
1061 const Twine
&NewLink
, const Twine
&Target
, time_t ModificationTime
,
1062 std::optional
<uint32_t> User
, std::optional
<uint32_t> Group
,
1063 std::optional
<llvm::sys::fs::perms
> Perms
) {
1064 auto NewLinkNode
= lookupNode(NewLink
, /*FollowFinalSymlink=*/false);
1068 SmallString
<128> NewLinkStr
, TargetStr
;
1069 NewLink
.toVector(NewLinkStr
);
1070 Target
.toVector(TargetStr
);
1072 return addFile(NewLinkStr
, ModificationTime
, nullptr, User
, Group
,
1073 sys::fs::file_type::symlink_file
, Perms
,
1074 [&](detail::NewInMemoryNodeInfo NNI
) {
1075 return std::make_unique
<detail::InMemorySymbolicLink
>(
1076 NewLinkStr
, TargetStr
, NNI
.makeStatus());
1080 llvm::ErrorOr
<Status
> InMemoryFileSystem::status(const Twine
&Path
) {
1081 auto Node
= lookupNode(Path
, /*FollowFinalSymlink=*/true);
1083 return (*Node
)->getStatus(Path
);
1084 return Node
.getError();
1087 llvm::ErrorOr
<std::unique_ptr
<File
>>
1088 InMemoryFileSystem::openFileForRead(const Twine
&Path
) {
1089 auto Node
= lookupNode(Path
,/*FollowFinalSymlink=*/true);
1091 return Node
.getError();
1093 // When we have a file provide a heap-allocated wrapper for the memory buffer
1094 // to match the ownership semantics for File.
1095 if (auto *F
= dyn_cast
<detail::InMemoryFile
>(*Node
))
1096 return std::unique_ptr
<File
>(
1097 new detail::InMemoryFileAdaptor(*F
, Path
.str()));
1099 // FIXME: errc::not_a_file?
1100 return make_error_code(llvm::errc::invalid_argument
);
1103 /// Adaptor from InMemoryDir::iterator to directory_iterator.
1104 class InMemoryFileSystem::DirIterator
: public llvm::vfs::detail::DirIterImpl
{
1105 const InMemoryFileSystem
*FS
;
1106 detail::InMemoryDirectory::const_iterator I
;
1107 detail::InMemoryDirectory::const_iterator E
;
1108 std::string RequestedDirName
;
1110 void setCurrentEntry() {
1112 SmallString
<256> Path(RequestedDirName
);
1113 llvm::sys::path::append(Path
, I
->second
->getFileName());
1114 sys::fs::file_type Type
= sys::fs::file_type::type_unknown
;
1115 switch (I
->second
->getKind()) {
1116 case detail::IME_File
:
1117 case detail::IME_HardLink
:
1118 Type
= sys::fs::file_type::regular_file
;
1120 case detail::IME_Directory
:
1121 Type
= sys::fs::file_type::directory_file
;
1123 case detail::IME_SymbolicLink
:
1124 if (auto SymlinkTarget
=
1125 FS
->lookupNode(Path
, /*FollowFinalSymlink=*/true)) {
1126 Path
= SymlinkTarget
.getName();
1127 Type
= (*SymlinkTarget
)->getStatus(Path
).getType();
1131 CurrentEntry
= directory_entry(std::string(Path
), Type
);
1133 // When we're at the end, make CurrentEntry invalid and DirIterImpl will
1135 CurrentEntry
= directory_entry();
1140 DirIterator() = default;
1142 DirIterator(const InMemoryFileSystem
*FS
,
1143 const detail::InMemoryDirectory
&Dir
,
1144 std::string RequestedDirName
)
1145 : FS(FS
), I(Dir
.begin()), E(Dir
.end()),
1146 RequestedDirName(std::move(RequestedDirName
)) {
1150 std::error_code
increment() override
{
1157 directory_iterator
InMemoryFileSystem::dir_begin(const Twine
&Dir
,
1158 std::error_code
&EC
) {
1159 auto Node
= lookupNode(Dir
, /*FollowFinalSymlink=*/true);
1161 EC
= Node
.getError();
1162 return directory_iterator(std::make_shared
<DirIterator
>());
1165 if (auto *DirNode
= dyn_cast
<detail::InMemoryDirectory
>(*Node
))
1166 return directory_iterator(
1167 std::make_shared
<DirIterator
>(this, *DirNode
, Dir
.str()));
1169 EC
= make_error_code(llvm::errc::not_a_directory
);
1170 return directory_iterator(std::make_shared
<DirIterator
>());
1173 std::error_code
InMemoryFileSystem::setCurrentWorkingDirectory(const Twine
&P
) {
1174 SmallString
<128> Path
;
1177 // Fix up relative paths. This just prepends the current working directory.
1178 std::error_code EC
= makeAbsolute(Path
);
1182 if (useNormalizedPaths())
1183 llvm::sys::path::remove_dots(Path
, /*remove_dot_dot=*/true);
1186 WorkingDirectory
= std::string(Path
);
1190 std::error_code
InMemoryFileSystem::getRealPath(const Twine
&Path
,
1191 SmallVectorImpl
<char> &Output
) {
1192 auto CWD
= getCurrentWorkingDirectory();
1193 if (!CWD
|| CWD
->empty())
1194 return errc::operation_not_permitted
;
1195 Path
.toVector(Output
);
1196 if (auto EC
= makeAbsolute(Output
))
1198 llvm::sys::path::remove_dots(Output
, /*remove_dot_dot=*/true);
1202 std::error_code
InMemoryFileSystem::isLocal(const Twine
&Path
, bool &Result
) {
1207 void InMemoryFileSystem::printImpl(raw_ostream
&OS
, PrintType PrintContents
,
1208 unsigned IndentLevel
) const {
1209 printIndent(OS
, IndentLevel
);
1210 OS
<< "InMemoryFileSystem\n";
1216 //===-----------------------------------------------------------------------===/
1217 // RedirectingFileSystem implementation
1218 //===-----------------------------------------------------------------------===/
1222 static llvm::sys::path::Style
getExistingStyle(llvm::StringRef Path
) {
1223 // Detect the path style in use by checking the first separator.
1224 llvm::sys::path::Style style
= llvm::sys::path::Style::native
;
1225 const size_t n
= Path
.find_first_of("/\\");
1226 // Can't distinguish between posix and windows_slash here.
1227 if (n
!= static_cast<size_t>(-1))
1228 style
= (Path
[n
] == '/') ? llvm::sys::path::Style::posix
1229 : llvm::sys::path::Style::windows_backslash
;
1233 /// Removes leading "./" as well as path components like ".." and ".".
1234 static llvm::SmallString
<256> canonicalize(llvm::StringRef Path
) {
1235 // First detect the path style in use by checking the first separator.
1236 llvm::sys::path::Style style
= getExistingStyle(Path
);
1238 // Now remove the dots. Explicitly specifying the path style prevents the
1239 // direction of the slashes from changing.
1240 llvm::SmallString
<256> result
=
1241 llvm::sys::path::remove_leading_dotslash(Path
, style
);
1242 llvm::sys::path::remove_dots(result
, /*remove_dot_dot=*/true, style
);
1246 /// Whether the error and entry specify a file/directory that was not found.
1247 static bool isFileNotFound(std::error_code EC
,
1248 RedirectingFileSystem::Entry
*E
= nullptr) {
1249 if (E
&& !isa
<RedirectingFileSystem::DirectoryRemapEntry
>(E
))
1251 return EC
== llvm::errc::no_such_file_or_directory
;
1254 } // anonymous namespace
1257 RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr
<FileSystem
> FS
)
1258 : ExternalFS(std::move(FS
)) {
1260 if (auto ExternalWorkingDirectory
=
1261 ExternalFS
->getCurrentWorkingDirectory()) {
1262 WorkingDirectory
= *ExternalWorkingDirectory
;
1266 /// Directory iterator implementation for \c RedirectingFileSystem's
1267 /// directory entries.
1268 class llvm::vfs::RedirectingFSDirIterImpl
1269 : public llvm::vfs::detail::DirIterImpl
{
1271 RedirectingFileSystem::DirectoryEntry::iterator Current
, End
;
1273 std::error_code
incrementImpl(bool IsFirstTime
) {
1274 assert((IsFirstTime
|| Current
!= End
) && "cannot iterate past end");
1277 if (Current
!= End
) {
1278 SmallString
<128> PathStr(Dir
);
1279 llvm::sys::path::append(PathStr
, (*Current
)->getName());
1280 sys::fs::file_type Type
= sys::fs::file_type::type_unknown
;
1281 switch ((*Current
)->getKind()) {
1282 case RedirectingFileSystem::EK_Directory
:
1284 case RedirectingFileSystem::EK_DirectoryRemap
:
1285 Type
= sys::fs::file_type::directory_file
;
1287 case RedirectingFileSystem::EK_File
:
1288 Type
= sys::fs::file_type::regular_file
;
1291 CurrentEntry
= directory_entry(std::string(PathStr
), Type
);
1293 CurrentEntry
= directory_entry();
1299 RedirectingFSDirIterImpl(
1300 const Twine
&Path
, RedirectingFileSystem::DirectoryEntry::iterator Begin
,
1301 RedirectingFileSystem::DirectoryEntry::iterator End
, std::error_code
&EC
)
1302 : Dir(Path
.str()), Current(Begin
), End(End
) {
1303 EC
= incrementImpl(/*IsFirstTime=*/true);
1306 std::error_code
increment() override
{
1307 return incrementImpl(/*IsFirstTime=*/false);
1312 /// Directory iterator implementation for \c RedirectingFileSystem's
1313 /// directory remap entries that maps the paths reported by the external
1314 /// file system's directory iterator back to the virtual directory's path.
1315 class RedirectingFSDirRemapIterImpl
: public llvm::vfs::detail::DirIterImpl
{
1317 llvm::sys::path::Style DirStyle
;
1318 llvm::vfs::directory_iterator ExternalIter
;
1321 RedirectingFSDirRemapIterImpl(std::string DirPath
,
1322 llvm::vfs::directory_iterator ExtIter
)
1323 : Dir(std::move(DirPath
)), DirStyle(getExistingStyle(Dir
)),
1324 ExternalIter(ExtIter
) {
1325 if (ExternalIter
!= llvm::vfs::directory_iterator())
1329 void setCurrentEntry() {
1330 StringRef ExternalPath
= ExternalIter
->path();
1331 llvm::sys::path::Style ExternalStyle
= getExistingStyle(ExternalPath
);
1332 StringRef File
= llvm::sys::path::filename(ExternalPath
, ExternalStyle
);
1334 SmallString
<128> NewPath(Dir
);
1335 llvm::sys::path::append(NewPath
, DirStyle
, File
);
1337 CurrentEntry
= directory_entry(std::string(NewPath
), ExternalIter
->type());
1340 std::error_code
increment() override
{
1342 ExternalIter
.increment(EC
);
1343 if (!EC
&& ExternalIter
!= llvm::vfs::directory_iterator())
1346 CurrentEntry
= directory_entry();
1352 llvm::ErrorOr
<std::string
>
1353 RedirectingFileSystem::getCurrentWorkingDirectory() const {
1354 return WorkingDirectory
;
1358 RedirectingFileSystem::setCurrentWorkingDirectory(const Twine
&Path
) {
1359 // Don't change the working directory if the path doesn't exist.
1361 return errc::no_such_file_or_directory
;
1363 SmallString
<128> AbsolutePath
;
1364 Path
.toVector(AbsolutePath
);
1365 if (std::error_code EC
= makeAbsolute(AbsolutePath
))
1367 WorkingDirectory
= std::string(AbsolutePath
);
1371 std::error_code
RedirectingFileSystem::isLocal(const Twine
&Path_
,
1373 SmallString
<256> Path
;
1374 Path_
.toVector(Path
);
1376 if (makeAbsolute(Path
))
1379 return ExternalFS
->isLocal(Path
, Result
);
1382 std::error_code
RedirectingFileSystem::makeAbsolute(SmallVectorImpl
<char> &Path
) const {
1383 // is_absolute(..., Style::windows_*) accepts paths with both slash types.
1384 if (llvm::sys::path::is_absolute(Path
, llvm::sys::path::Style::posix
) ||
1385 llvm::sys::path::is_absolute(Path
,
1386 llvm::sys::path::Style::windows_backslash
))
1387 // This covers windows absolute path with forward slash as well, as the
1388 // forward slashes are treated as path separation in llvm::path
1389 // regardless of what path::Style is used.
1392 auto WorkingDir
= getCurrentWorkingDirectory();
1394 return WorkingDir
.getError();
1396 return makeAbsolute(WorkingDir
.get(), Path
);
1400 RedirectingFileSystem::makeAbsolute(StringRef WorkingDir
,
1401 SmallVectorImpl
<char> &Path
) const {
1402 // We can't use sys::fs::make_absolute because that assumes the path style
1403 // is native and there is no way to override that. Since we know WorkingDir
1404 // is absolute, we can use it to determine which style we actually have and
1405 // append Path ourselves.
1406 if (!WorkingDir
.empty() &&
1407 !sys::path::is_absolute(WorkingDir
, sys::path::Style::posix
) &&
1408 !sys::path::is_absolute(WorkingDir
,
1409 sys::path::Style::windows_backslash
)) {
1410 return std::error_code();
1412 sys::path::Style style
= sys::path::Style::windows_backslash
;
1413 if (sys::path::is_absolute(WorkingDir
, sys::path::Style::posix
)) {
1414 style
= sys::path::Style::posix
;
1416 // Distinguish between windows_backslash and windows_slash; getExistingStyle
1417 // returns posix for a path with windows_slash.
1418 if (getExistingStyle(WorkingDir
) != sys::path::Style::windows_backslash
)
1419 style
= sys::path::Style::windows_slash
;
1422 std::string Result
= std::string(WorkingDir
);
1423 StringRef
Dir(Result
);
1424 if (!Dir
.ends_with(sys::path::get_separator(style
))) {
1425 Result
+= sys::path::get_separator(style
);
1427 // backslashes '\' are legit path charactors under POSIX. Windows APIs
1428 // like CreateFile accepts forward slashes '/' as path
1429 // separator (even when mixed with backslashes). Therefore,
1430 // `Path` should be directly appended to `WorkingDir` without converting
1432 Result
.append(Path
.data(), Path
.size());
1433 Path
.assign(Result
.begin(), Result
.end());
1438 directory_iterator
RedirectingFileSystem::dir_begin(const Twine
&Dir
,
1439 std::error_code
&EC
) {
1440 SmallString
<256> Path
;
1443 EC
= makeAbsolute(Path
);
1447 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
= lookupPath(Path
);
1449 if (Redirection
!= RedirectKind::RedirectOnly
&&
1450 isFileNotFound(Result
.getError()))
1451 return ExternalFS
->dir_begin(Path
, EC
);
1453 EC
= Result
.getError();
1457 // Use status to make sure the path exists and refers to a directory.
1458 ErrorOr
<Status
> S
= status(Path
, Dir
, *Result
);
1460 if (Redirection
!= RedirectKind::RedirectOnly
&&
1461 isFileNotFound(S
.getError(), Result
->E
))
1462 return ExternalFS
->dir_begin(Dir
, EC
);
1468 if (!S
->isDirectory()) {
1469 EC
= errc::not_a_directory
;
1473 // Create the appropriate directory iterator based on whether we found a
1474 // DirectoryRemapEntry or DirectoryEntry.
1475 directory_iterator RedirectIter
;
1476 std::error_code RedirectEC
;
1477 if (auto ExtRedirect
= Result
->getExternalRedirect()) {
1478 auto RE
= cast
<RedirectingFileSystem::RemapEntry
>(Result
->E
);
1479 RedirectIter
= ExternalFS
->dir_begin(*ExtRedirect
, RedirectEC
);
1481 if (!RE
->useExternalName(UseExternalNames
)) {
1482 // Update the paths in the results to use the virtual directory's path.
1484 directory_iterator(std::make_shared
<RedirectingFSDirRemapIterImpl
>(
1485 std::string(Path
), RedirectIter
));
1488 auto DE
= cast
<DirectoryEntry
>(Result
->E
);
1490 directory_iterator(std::make_shared
<RedirectingFSDirIterImpl
>(
1491 Path
, DE
->contents_begin(), DE
->contents_end(), RedirectEC
));
1495 if (RedirectEC
!= errc::no_such_file_or_directory
) {
1502 if (Redirection
== RedirectKind::RedirectOnly
) {
1504 return RedirectIter
;
1507 std::error_code ExternalEC
;
1508 directory_iterator ExternalIter
= ExternalFS
->dir_begin(Path
, ExternalEC
);
1510 if (ExternalEC
!= errc::no_such_file_or_directory
) {
1517 SmallVector
<directory_iterator
, 2> Iters
;
1518 switch (Redirection
) {
1519 case RedirectKind::Fallthrough
:
1520 Iters
.push_back(ExternalIter
);
1521 Iters
.push_back(RedirectIter
);
1523 case RedirectKind::Fallback
:
1524 Iters
.push_back(RedirectIter
);
1525 Iters
.push_back(ExternalIter
);
1528 llvm_unreachable("unhandled RedirectKind");
1531 directory_iterator Combined
{
1532 std::make_shared
<CombiningDirIterImpl
>(Iters
, EC
)};
1538 void RedirectingFileSystem::setOverlayFileDir(StringRef Dir
) {
1539 OverlayFileDir
= Dir
.str();
1542 StringRef
RedirectingFileSystem::getOverlayFileDir() const {
1543 return OverlayFileDir
;
1546 void RedirectingFileSystem::setFallthrough(bool Fallthrough
) {
1548 Redirection
= RedirectingFileSystem::RedirectKind::Fallthrough
;
1550 Redirection
= RedirectingFileSystem::RedirectKind::RedirectOnly
;
1554 void RedirectingFileSystem::setRedirection(
1555 RedirectingFileSystem::RedirectKind Kind
) {
1559 std::vector
<StringRef
> RedirectingFileSystem::getRoots() const {
1560 std::vector
<StringRef
> R
;
1561 R
.reserve(Roots
.size());
1562 for (const auto &Root
: Roots
)
1563 R
.push_back(Root
->getName());
1567 void RedirectingFileSystem::printImpl(raw_ostream
&OS
, PrintType Type
,
1568 unsigned IndentLevel
) const {
1569 printIndent(OS
, IndentLevel
);
1570 OS
<< "RedirectingFileSystem (UseExternalNames: "
1571 << (UseExternalNames
? "true" : "false") << ")\n";
1572 if (Type
== PrintType::Summary
)
1575 for (const auto &Root
: Roots
)
1576 printEntry(OS
, Root
.get(), IndentLevel
);
1578 printIndent(OS
, IndentLevel
);
1579 OS
<< "ExternalFS:\n";
1580 ExternalFS
->print(OS
, Type
== PrintType::Contents
? PrintType::Summary
: Type
,
1584 void RedirectingFileSystem::printEntry(raw_ostream
&OS
,
1585 RedirectingFileSystem::Entry
*E
,
1586 unsigned IndentLevel
) const {
1587 printIndent(OS
, IndentLevel
);
1588 OS
<< "'" << E
->getName() << "'";
1590 switch (E
->getKind()) {
1591 case EK_Directory
: {
1592 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(E
);
1595 for (std::unique_ptr
<Entry
> &SubEntry
:
1596 llvm::make_range(DE
->contents_begin(), DE
->contents_end()))
1597 printEntry(OS
, SubEntry
.get(), IndentLevel
+ 1);
1600 case EK_DirectoryRemap
:
1602 auto *RE
= cast
<RedirectingFileSystem::RemapEntry
>(E
);
1603 OS
<< " -> '" << RE
->getExternalContentsPath() << "'";
1604 switch (RE
->getUseName()) {
1608 OS
<< " (UseExternalName: true)";
1611 OS
<< " (UseExternalName: false)";
1620 void RedirectingFileSystem::visitChildFileSystems(VisitCallbackTy Callback
) {
1622 Callback(*ExternalFS
);
1623 ExternalFS
->visitChildFileSystems(Callback
);
1627 /// A helper class to hold the common YAML parsing state.
1628 class llvm::vfs::RedirectingFileSystemParser
{
1629 yaml::Stream
&Stream
;
1631 void error(yaml::Node
*N
, const Twine
&Msg
) { Stream
.printError(N
, Msg
); }
1634 bool parseScalarString(yaml::Node
*N
, StringRef
&Result
,
1635 SmallVectorImpl
<char> &Storage
) {
1636 const auto *S
= dyn_cast
<yaml::ScalarNode
>(N
);
1639 error(N
, "expected string");
1642 Result
= S
->getValue(Storage
);
1647 bool parseScalarBool(yaml::Node
*N
, bool &Result
) {
1648 SmallString
<5> Storage
;
1650 if (!parseScalarString(N
, Value
, Storage
))
1653 if (Value
.equals_insensitive("true") || Value
.equals_insensitive("on") ||
1654 Value
.equals_insensitive("yes") || Value
== "1") {
1657 } else if (Value
.equals_insensitive("false") ||
1658 Value
.equals_insensitive("off") ||
1659 Value
.equals_insensitive("no") || Value
== "0") {
1664 error(N
, "expected boolean value");
1668 std::optional
<RedirectingFileSystem::RedirectKind
>
1669 parseRedirectKind(yaml::Node
*N
) {
1670 SmallString
<12> Storage
;
1672 if (!parseScalarString(N
, Value
, Storage
))
1673 return std::nullopt
;
1675 if (Value
.equals_insensitive("fallthrough")) {
1676 return RedirectingFileSystem::RedirectKind::Fallthrough
;
1677 } else if (Value
.equals_insensitive("fallback")) {
1678 return RedirectingFileSystem::RedirectKind::Fallback
;
1679 } else if (Value
.equals_insensitive("redirect-only")) {
1680 return RedirectingFileSystem::RedirectKind::RedirectOnly
;
1682 return std::nullopt
;
1685 std::optional
<RedirectingFileSystem::RootRelativeKind
>
1686 parseRootRelativeKind(yaml::Node
*N
) {
1687 SmallString
<12> Storage
;
1689 if (!parseScalarString(N
, Value
, Storage
))
1690 return std::nullopt
;
1691 if (Value
.equals_insensitive("cwd")) {
1692 return RedirectingFileSystem::RootRelativeKind::CWD
;
1693 } else if (Value
.equals_insensitive("overlay-dir")) {
1694 return RedirectingFileSystem::RootRelativeKind::OverlayDir
;
1696 return std::nullopt
;
1703 KeyStatus(bool Required
= false) : Required(Required
) {}
1706 using KeyStatusPair
= std::pair
<StringRef
, KeyStatus
>;
1709 bool checkDuplicateOrUnknownKey(yaml::Node
*KeyNode
, StringRef Key
,
1710 DenseMap
<StringRef
, KeyStatus
> &Keys
) {
1711 auto It
= Keys
.find(Key
);
1712 if (It
== Keys
.end()) {
1713 error(KeyNode
, "unknown key");
1716 KeyStatus
&S
= It
->second
;
1718 error(KeyNode
, Twine("duplicate key '") + Key
+ "'");
1726 bool checkMissingKeys(yaml::Node
*Obj
, DenseMap
<StringRef
, KeyStatus
> &Keys
) {
1727 for (const auto &I
: Keys
) {
1728 if (I
.second
.Required
&& !I
.second
.Seen
) {
1729 error(Obj
, Twine("missing key '") + I
.first
+ "'");
1737 static RedirectingFileSystem::Entry
*
1738 lookupOrCreateEntry(RedirectingFileSystem
*FS
, StringRef Name
,
1739 RedirectingFileSystem::Entry
*ParentEntry
= nullptr) {
1740 if (!ParentEntry
) { // Look for a existent root
1741 for (const auto &Root
: FS
->Roots
) {
1742 if (Name
== Root
->getName()) {
1743 ParentEntry
= Root
.get();
1747 } else { // Advance to the next component
1748 auto *DE
= dyn_cast
<RedirectingFileSystem::DirectoryEntry
>(ParentEntry
);
1749 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &Content
:
1750 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
1752 dyn_cast
<RedirectingFileSystem::DirectoryEntry
>(Content
.get());
1753 if (DirContent
&& Name
== Content
->getName())
1758 // ... or create a new one
1759 std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
1760 std::make_unique
<RedirectingFileSystem::DirectoryEntry
>(
1761 Name
, Status("", getNextVirtualUniqueID(),
1762 std::chrono::system_clock::now(), 0, 0, 0,
1763 file_type::directory_file
, sys::fs::all_all
));
1765 if (!ParentEntry
) { // Add a new root to the overlay
1766 FS
->Roots
.push_back(std::move(E
));
1767 ParentEntry
= FS
->Roots
.back().get();
1771 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(ParentEntry
);
1772 DE
->addContent(std::move(E
));
1773 return DE
->getLastContent();
1777 void uniqueOverlayTree(RedirectingFileSystem
*FS
,
1778 RedirectingFileSystem::Entry
*SrcE
,
1779 RedirectingFileSystem::Entry
*NewParentE
= nullptr) {
1780 StringRef Name
= SrcE
->getName();
1781 switch (SrcE
->getKind()) {
1782 case RedirectingFileSystem::EK_Directory
: {
1783 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(SrcE
);
1784 // Empty directories could be present in the YAML as a way to
1785 // describe a file for a current directory after some of its subdir
1786 // is parsed. This only leads to redundant walks, ignore it.
1788 NewParentE
= lookupOrCreateEntry(FS
, Name
, NewParentE
);
1789 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &SubEntry
:
1790 llvm::make_range(DE
->contents_begin(), DE
->contents_end()))
1791 uniqueOverlayTree(FS
, SubEntry
.get(), NewParentE
);
1794 case RedirectingFileSystem::EK_DirectoryRemap
: {
1795 assert(NewParentE
&& "Parent entry must exist");
1796 auto *DR
= cast
<RedirectingFileSystem::DirectoryRemapEntry
>(SrcE
);
1797 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(NewParentE
);
1799 std::make_unique
<RedirectingFileSystem::DirectoryRemapEntry
>(
1800 Name
, DR
->getExternalContentsPath(), DR
->getUseName()));
1803 case RedirectingFileSystem::EK_File
: {
1804 assert(NewParentE
&& "Parent entry must exist");
1805 auto *FE
= cast
<RedirectingFileSystem::FileEntry
>(SrcE
);
1806 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(NewParentE
);
1807 DE
->addContent(std::make_unique
<RedirectingFileSystem::FileEntry
>(
1808 Name
, FE
->getExternalContentsPath(), FE
->getUseName()));
1814 std::unique_ptr
<RedirectingFileSystem::Entry
>
1815 parseEntry(yaml::Node
*N
, RedirectingFileSystem
*FS
, bool IsRootEntry
) {
1816 auto *M
= dyn_cast
<yaml::MappingNode
>(N
);
1818 error(N
, "expected mapping node for file or directory entry");
1822 KeyStatusPair Fields
[] = {
1823 KeyStatusPair("name", true),
1824 KeyStatusPair("type", true),
1825 KeyStatusPair("contents", false),
1826 KeyStatusPair("external-contents", false),
1827 KeyStatusPair("use-external-name", false),
1830 DenseMap
<StringRef
, KeyStatus
> Keys(std::begin(Fields
), std::end(Fields
));
1832 enum { CF_NotSet
, CF_List
, CF_External
} ContentsField
= CF_NotSet
;
1833 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>>
1835 SmallString
<256> ExternalContentsPath
;
1836 SmallString
<256> Name
;
1837 yaml::Node
*NameValueNode
= nullptr;
1838 auto UseExternalName
= RedirectingFileSystem::NK_NotSet
;
1839 RedirectingFileSystem::EntryKind Kind
;
1841 for (auto &I
: *M
) {
1843 // Reuse the buffer for key and value, since we don't look at key after
1845 SmallString
<256> Buffer
;
1846 if (!parseScalarString(I
.getKey(), Key
, Buffer
))
1849 if (!checkDuplicateOrUnknownKey(I
.getKey(), Key
, Keys
))
1853 if (Key
== "name") {
1854 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1857 NameValueNode
= I
.getValue();
1858 // Guarantee that old YAML files containing paths with ".." and "."
1859 // are properly canonicalized before read into the VFS.
1860 Name
= canonicalize(Value
).str();
1861 } else if (Key
== "type") {
1862 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1864 if (Value
== "file")
1865 Kind
= RedirectingFileSystem::EK_File
;
1866 else if (Value
== "directory")
1867 Kind
= RedirectingFileSystem::EK_Directory
;
1868 else if (Value
== "directory-remap")
1869 Kind
= RedirectingFileSystem::EK_DirectoryRemap
;
1871 error(I
.getValue(), "unknown value for 'type'");
1874 } else if (Key
== "contents") {
1875 if (ContentsField
!= CF_NotSet
) {
1877 "entry already has 'contents' or 'external-contents'");
1880 ContentsField
= CF_List
;
1881 auto *Contents
= dyn_cast
<yaml::SequenceNode
>(I
.getValue());
1883 // FIXME: this is only for directories, what about files?
1884 error(I
.getValue(), "expected array");
1888 for (auto &I
: *Contents
) {
1889 if (std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
1890 parseEntry(&I
, FS
, /*IsRootEntry*/ false))
1891 EntryArrayContents
.push_back(std::move(E
));
1895 } else if (Key
== "external-contents") {
1896 if (ContentsField
!= CF_NotSet
) {
1898 "entry already has 'contents' or 'external-contents'");
1901 ContentsField
= CF_External
;
1902 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1905 SmallString
<256> FullPath
;
1906 if (FS
->IsRelativeOverlay
) {
1907 FullPath
= FS
->getOverlayFileDir();
1908 assert(!FullPath
.empty() &&
1909 "External contents prefix directory must exist");
1910 llvm::sys::path::append(FullPath
, Value
);
1915 // Guarantee that old YAML files containing paths with ".." and "."
1916 // are properly canonicalized before read into the VFS.
1917 FullPath
= canonicalize(FullPath
);
1918 ExternalContentsPath
= FullPath
.str();
1919 } else if (Key
== "use-external-name") {
1921 if (!parseScalarBool(I
.getValue(), Val
))
1923 UseExternalName
= Val
? RedirectingFileSystem::NK_External
1924 : RedirectingFileSystem::NK_Virtual
;
1926 llvm_unreachable("key missing from Keys");
1930 if (Stream
.failed())
1933 // check for missing keys
1934 if (ContentsField
== CF_NotSet
) {
1935 error(N
, "missing key 'contents' or 'external-contents'");
1938 if (!checkMissingKeys(N
, Keys
))
1941 // check invalid configuration
1942 if (Kind
== RedirectingFileSystem::EK_Directory
&&
1943 UseExternalName
!= RedirectingFileSystem::NK_NotSet
) {
1944 error(N
, "'use-external-name' is not supported for 'directory' entries");
1948 if (Kind
== RedirectingFileSystem::EK_DirectoryRemap
&&
1949 ContentsField
== CF_List
) {
1950 error(N
, "'contents' is not supported for 'directory-remap' entries");
1954 sys::path::Style path_style
= sys::path::Style::native
;
1956 // VFS root entries may be in either Posix or Windows style. Figure out
1957 // which style we have, and use it consistently.
1958 if (sys::path::is_absolute(Name
, sys::path::Style::posix
)) {
1959 path_style
= sys::path::Style::posix
;
1960 } else if (sys::path::is_absolute(Name
,
1961 sys::path::Style::windows_backslash
)) {
1962 path_style
= sys::path::Style::windows_backslash
;
1964 // Relative VFS root entries are made absolute to either the overlay
1965 // directory, or the current working directory, then we can determine
1966 // the path style from that.
1968 if (FS
->RootRelative
==
1969 RedirectingFileSystem::RootRelativeKind::OverlayDir
) {
1970 StringRef FullPath
= FS
->getOverlayFileDir();
1971 assert(!FullPath
.empty() && "Overlay file directory must exist");
1972 EC
= FS
->makeAbsolute(FullPath
, Name
);
1973 Name
= canonicalize(Name
);
1975 EC
= sys::fs::make_absolute(Name
);
1978 assert(NameValueNode
&& "Name presence should be checked earlier");
1981 "entry with relative path at the root level is not discoverable");
1984 path_style
= sys::path::is_absolute(Name
, sys::path::Style::posix
)
1985 ? sys::path::Style::posix
1986 : sys::path::Style::windows_backslash
;
1988 // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will
1989 // return true even if `Name` is using forward slashes. Distinguish
1990 // between windows_backslash and windows_slash.
1991 if (path_style
== sys::path::Style::windows_backslash
&&
1992 getExistingStyle(Name
) != sys::path::Style::windows_backslash
)
1993 path_style
= sys::path::Style::windows_slash
;
1996 // Remove trailing slash(es), being careful not to remove the root path
1997 StringRef Trimmed
= Name
;
1998 size_t RootPathLen
= sys::path::root_path(Trimmed
, path_style
).size();
1999 while (Trimmed
.size() > RootPathLen
&&
2000 sys::path::is_separator(Trimmed
.back(), path_style
))
2001 Trimmed
= Trimmed
.slice(0, Trimmed
.size() - 1);
2003 // Get the last component
2004 StringRef LastComponent
= sys::path::filename(Trimmed
, path_style
);
2006 std::unique_ptr
<RedirectingFileSystem::Entry
> Result
;
2008 case RedirectingFileSystem::EK_File
:
2009 Result
= std::make_unique
<RedirectingFileSystem::FileEntry
>(
2010 LastComponent
, std::move(ExternalContentsPath
), UseExternalName
);
2012 case RedirectingFileSystem::EK_DirectoryRemap
:
2013 Result
= std::make_unique
<RedirectingFileSystem::DirectoryRemapEntry
>(
2014 LastComponent
, std::move(ExternalContentsPath
), UseExternalName
);
2016 case RedirectingFileSystem::EK_Directory
:
2017 Result
= std::make_unique
<RedirectingFileSystem::DirectoryEntry
>(
2018 LastComponent
, std::move(EntryArrayContents
),
2019 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2020 0, 0, 0, file_type::directory_file
, sys::fs::all_all
));
2024 StringRef Parent
= sys::path::parent_path(Trimmed
, path_style
);
2028 // if 'name' contains multiple components, create implicit directory entries
2029 for (sys::path::reverse_iterator I
= sys::path::rbegin(Parent
, path_style
),
2030 E
= sys::path::rend(Parent
);
2032 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>> Entries
;
2033 Entries
.push_back(std::move(Result
));
2034 Result
= std::make_unique
<RedirectingFileSystem::DirectoryEntry
>(
2035 *I
, std::move(Entries
),
2036 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2037 0, 0, 0, file_type::directory_file
, sys::fs::all_all
));
2043 RedirectingFileSystemParser(yaml::Stream
&S
) : Stream(S
) {}
2046 bool parse(yaml::Node
*Root
, RedirectingFileSystem
*FS
) {
2047 auto *Top
= dyn_cast
<yaml::MappingNode
>(Root
);
2049 error(Root
, "expected mapping node");
2053 KeyStatusPair Fields
[] = {
2054 KeyStatusPair("version", true),
2055 KeyStatusPair("case-sensitive", false),
2056 KeyStatusPair("use-external-names", false),
2057 KeyStatusPair("root-relative", false),
2058 KeyStatusPair("overlay-relative", false),
2059 KeyStatusPair("fallthrough", false),
2060 KeyStatusPair("redirecting-with", false),
2061 KeyStatusPair("roots", true),
2064 DenseMap
<StringRef
, KeyStatus
> Keys(std::begin(Fields
), std::end(Fields
));
2065 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>> RootEntries
;
2067 // Parse configuration and 'roots'
2068 for (auto &I
: *Top
) {
2069 SmallString
<10> KeyBuffer
;
2071 if (!parseScalarString(I
.getKey(), Key
, KeyBuffer
))
2074 if (!checkDuplicateOrUnknownKey(I
.getKey(), Key
, Keys
))
2077 if (Key
== "roots") {
2078 auto *Roots
= dyn_cast
<yaml::SequenceNode
>(I
.getValue());
2080 error(I
.getValue(), "expected array");
2084 for (auto &I
: *Roots
) {
2085 if (std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
2086 parseEntry(&I
, FS
, /*IsRootEntry*/ true))
2087 RootEntries
.push_back(std::move(E
));
2091 } else if (Key
== "version") {
2092 StringRef VersionString
;
2093 SmallString
<4> Storage
;
2094 if (!parseScalarString(I
.getValue(), VersionString
, Storage
))
2097 if (VersionString
.getAsInteger
<int>(10, Version
)) {
2098 error(I
.getValue(), "expected integer");
2102 error(I
.getValue(), "invalid version number");
2106 error(I
.getValue(), "version mismatch, expected 0");
2109 } else if (Key
== "case-sensitive") {
2110 if (!parseScalarBool(I
.getValue(), FS
->CaseSensitive
))
2112 } else if (Key
== "overlay-relative") {
2113 if (!parseScalarBool(I
.getValue(), FS
->IsRelativeOverlay
))
2115 } else if (Key
== "use-external-names") {
2116 if (!parseScalarBool(I
.getValue(), FS
->UseExternalNames
))
2118 } else if (Key
== "fallthrough") {
2119 if (Keys
["redirecting-with"].Seen
) {
2121 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2125 bool ShouldFallthrough
= false;
2126 if (!parseScalarBool(I
.getValue(), ShouldFallthrough
))
2129 if (ShouldFallthrough
) {
2130 FS
->Redirection
= RedirectingFileSystem::RedirectKind::Fallthrough
;
2132 FS
->Redirection
= RedirectingFileSystem::RedirectKind::RedirectOnly
;
2134 } else if (Key
== "redirecting-with") {
2135 if (Keys
["fallthrough"].Seen
) {
2137 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2141 if (auto Kind
= parseRedirectKind(I
.getValue())) {
2142 FS
->Redirection
= *Kind
;
2144 error(I
.getValue(), "expected valid redirect kind");
2147 } else if (Key
== "root-relative") {
2148 if (auto Kind
= parseRootRelativeKind(I
.getValue())) {
2149 FS
->RootRelative
= *Kind
;
2151 error(I
.getValue(), "expected valid root-relative kind");
2155 llvm_unreachable("key missing from Keys");
2159 if (Stream
.failed())
2162 if (!checkMissingKeys(Top
, Keys
))
2165 // Now that we sucessefully parsed the YAML file, canonicalize the internal
2166 // representation to a proper directory tree so that we can search faster
2168 for (auto &E
: RootEntries
)
2169 uniqueOverlayTree(FS
, E
.get());
2175 std::unique_ptr
<RedirectingFileSystem
>
2176 RedirectingFileSystem::create(std::unique_ptr
<MemoryBuffer
> Buffer
,
2177 SourceMgr::DiagHandlerTy DiagHandler
,
2178 StringRef YAMLFilePath
, void *DiagContext
,
2179 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
2181 yaml::Stream
Stream(Buffer
->getMemBufferRef(), SM
);
2183 SM
.setDiagHandler(DiagHandler
, DiagContext
);
2184 yaml::document_iterator DI
= Stream
.begin();
2185 yaml::Node
*Root
= DI
->getRoot();
2186 if (DI
== Stream
.end() || !Root
) {
2187 SM
.PrintMessage(SMLoc(), SourceMgr::DK_Error
, "expected root node");
2191 RedirectingFileSystemParser
P(Stream
);
2193 std::unique_ptr
<RedirectingFileSystem
> FS(
2194 new RedirectingFileSystem(ExternalFS
));
2196 if (!YAMLFilePath
.empty()) {
2197 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
2198 // to each 'external-contents' path.
2201 // -ivfsoverlay dummy.cache/vfs/vfs.yaml
2203 // FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs
2205 SmallString
<256> OverlayAbsDir
= sys::path::parent_path(YAMLFilePath
);
2206 std::error_code EC
= llvm::sys::fs::make_absolute(OverlayAbsDir
);
2207 assert(!EC
&& "Overlay dir final path must be absolute");
2209 FS
->setOverlayFileDir(OverlayAbsDir
);
2212 if (!P
.parse(Root
, FS
.get()))
2218 std::unique_ptr
<RedirectingFileSystem
> RedirectingFileSystem::create(
2219 ArrayRef
<std::pair
<std::string
, std::string
>> RemappedFiles
,
2220 bool UseExternalNames
, FileSystem
&ExternalFS
) {
2221 std::unique_ptr
<RedirectingFileSystem
> FS(
2222 new RedirectingFileSystem(&ExternalFS
));
2223 FS
->UseExternalNames
= UseExternalNames
;
2225 StringMap
<RedirectingFileSystem::Entry
*> Entries
;
2227 for (auto &Mapping
: llvm::reverse(RemappedFiles
)) {
2228 SmallString
<128> From
= StringRef(Mapping
.first
);
2229 SmallString
<128> To
= StringRef(Mapping
.second
);
2231 auto EC
= ExternalFS
.makeAbsolute(From
);
2233 assert(!EC
&& "Could not make absolute path");
2236 // Check if we've already mapped this file. The first one we see (in the
2237 // reverse iteration) wins.
2238 RedirectingFileSystem::Entry
*&ToEntry
= Entries
[From
];
2242 // Add parent directories.
2243 RedirectingFileSystem::Entry
*Parent
= nullptr;
2244 StringRef FromDirectory
= llvm::sys::path::parent_path(From
);
2245 for (auto I
= llvm::sys::path::begin(FromDirectory
),
2246 E
= llvm::sys::path::end(FromDirectory
);
2248 Parent
= RedirectingFileSystemParser::lookupOrCreateEntry(FS
.get(), *I
,
2251 assert(Parent
&& "File without a directory?");
2253 auto EC
= ExternalFS
.makeAbsolute(To
);
2255 assert(!EC
&& "Could not make absolute path");
2259 auto NewFile
= std::make_unique
<RedirectingFileSystem::FileEntry
>(
2260 llvm::sys::path::filename(From
), To
,
2261 UseExternalNames
? RedirectingFileSystem::NK_External
2262 : RedirectingFileSystem::NK_Virtual
);
2263 ToEntry
= NewFile
.get();
2264 cast
<RedirectingFileSystem::DirectoryEntry
>(Parent
)->addContent(
2265 std::move(NewFile
));
2271 RedirectingFileSystem::LookupResult::LookupResult(
2272 Entry
*E
, sys::path::const_iterator Start
, sys::path::const_iterator End
)
2274 assert(E
!= nullptr);
2275 // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
2276 // path of the directory it maps to in the external file system plus any
2277 // remaining path components in the provided iterator.
2278 if (auto *DRE
= dyn_cast
<RedirectingFileSystem::DirectoryRemapEntry
>(E
)) {
2279 SmallString
<256> Redirect(DRE
->getExternalContentsPath());
2280 sys::path::append(Redirect
, Start
, End
,
2281 getExistingStyle(DRE
->getExternalContentsPath()));
2282 ExternalRedirect
= std::string(Redirect
);
2286 void RedirectingFileSystem::LookupResult::getPath(
2287 llvm::SmallVectorImpl
<char> &Result
) const {
2289 for (Entry
*Parent
: Parents
)
2290 llvm::sys::path::append(Result
, Parent
->getName());
2291 llvm::sys::path::append(Result
, E
->getName());
2294 std::error_code
RedirectingFileSystem::makeCanonicalForLookup(
2295 SmallVectorImpl
<char> &Path
) const {
2296 if (std::error_code EC
= makeAbsolute(Path
))
2299 llvm::SmallString
<256> CanonicalPath
=
2300 canonicalize(StringRef(Path
.data(), Path
.size()));
2301 if (CanonicalPath
.empty())
2302 return make_error_code(llvm::errc::invalid_argument
);
2304 Path
.assign(CanonicalPath
.begin(), CanonicalPath
.end());
2308 ErrorOr
<RedirectingFileSystem::LookupResult
>
2309 RedirectingFileSystem::lookupPath(StringRef Path
) const {
2310 llvm::SmallString
<128> CanonicalPath(Path
);
2311 if (std::error_code EC
= makeCanonicalForLookup(CanonicalPath
))
2314 // RedirectOnly means the VFS is always used.
2315 if (UsageTrackingActive
&& Redirection
== RedirectKind::RedirectOnly
)
2318 sys::path::const_iterator Start
= sys::path::begin(CanonicalPath
);
2319 sys::path::const_iterator End
= sys::path::end(CanonicalPath
);
2320 llvm::SmallVector
<Entry
*, 32> Entries
;
2321 for (const auto &Root
: Roots
) {
2322 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
=
2323 lookupPathImpl(Start
, End
, Root
.get(), Entries
);
2324 if (UsageTrackingActive
&& Result
&& isa
<RemapEntry
>(Result
->E
))
2326 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
) {
2327 Result
->Parents
= std::move(Entries
);
2331 return make_error_code(llvm::errc::no_such_file_or_directory
);
2334 ErrorOr
<RedirectingFileSystem::LookupResult
>
2335 RedirectingFileSystem::lookupPathImpl(
2336 sys::path::const_iterator Start
, sys::path::const_iterator End
,
2337 RedirectingFileSystem::Entry
*From
,
2338 llvm::SmallVectorImpl
<Entry
*> &Entries
) const {
2339 assert(!isTraversalComponent(*Start
) &&
2340 !isTraversalComponent(From
->getName()) &&
2341 "Paths should not contain traversal components");
2343 StringRef FromName
= From
->getName();
2345 // Forward the search to the next component in case this is an empty one.
2346 if (!FromName
.empty()) {
2347 if (!pathComponentMatches(*Start
, FromName
))
2348 return make_error_code(llvm::errc::no_such_file_or_directory
);
2354 return LookupResult(From
, Start
, End
);
2358 if (isa
<RedirectingFileSystem::FileEntry
>(From
))
2359 return make_error_code(llvm::errc::not_a_directory
);
2361 if (isa
<RedirectingFileSystem::DirectoryRemapEntry
>(From
))
2362 return LookupResult(From
, Start
, End
);
2364 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(From
);
2365 for (const std::unique_ptr
<RedirectingFileSystem::Entry
> &DirEntry
:
2366 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
2367 Entries
.push_back(From
);
2368 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
=
2369 lookupPathImpl(Start
, End
, DirEntry
.get(), Entries
);
2370 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
2375 return make_error_code(llvm::errc::no_such_file_or_directory
);
2378 static Status
getRedirectedFileStatus(const Twine
&OriginalPath
,
2379 bool UseExternalNames
,
2380 Status ExternalStatus
) {
2381 // The path has been mapped by some nested VFS and exposes an external path,
2382 // don't override it with the original path.
2383 if (ExternalStatus
.ExposesExternalVFSPath
)
2384 return ExternalStatus
;
2386 Status S
= ExternalStatus
;
2387 if (!UseExternalNames
)
2388 S
= Status::copyWithNewName(S
, OriginalPath
);
2390 S
.ExposesExternalVFSPath
= true;
2394 ErrorOr
<Status
> RedirectingFileSystem::status(
2395 const Twine
&LookupPath
, const Twine
&OriginalPath
,
2396 const RedirectingFileSystem::LookupResult
&Result
) {
2397 if (std::optional
<StringRef
> ExtRedirect
= Result
.getExternalRedirect()) {
2398 SmallString
<256> RemappedPath((*ExtRedirect
).str());
2399 if (std::error_code EC
= makeAbsolute(RemappedPath
))
2402 ErrorOr
<Status
> S
= ExternalFS
->status(RemappedPath
);
2405 S
= Status::copyWithNewName(*S
, *ExtRedirect
);
2406 auto *RE
= cast
<RedirectingFileSystem::RemapEntry
>(Result
.E
);
2407 return getRedirectedFileStatus(OriginalPath
,
2408 RE
->useExternalName(UseExternalNames
), *S
);
2411 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(Result
.E
);
2412 return Status::copyWithNewName(DE
->getStatus(), LookupPath
);
2416 RedirectingFileSystem::getExternalStatus(const Twine
&LookupPath
,
2417 const Twine
&OriginalPath
) const {
2418 auto Result
= ExternalFS
->status(LookupPath
);
2420 // The path has been mapped by some nested VFS, don't override it with the
2422 if (!Result
|| Result
->ExposesExternalVFSPath
)
2424 return Status::copyWithNewName(Result
.get(), OriginalPath
);
2427 ErrorOr
<Status
> RedirectingFileSystem::status(const Twine
&OriginalPath
) {
2428 SmallString
<256> Path
;
2429 OriginalPath
.toVector(Path
);
2431 if (std::error_code EC
= makeAbsolute(Path
))
2434 if (Redirection
== RedirectKind::Fallback
) {
2435 // Attempt to find the original file first, only falling back to the
2436 // mapped file if that fails.
2437 ErrorOr
<Status
> S
= getExternalStatus(Path
, OriginalPath
);
2442 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
= lookupPath(Path
);
2444 // Was not able to map file, fallthrough to using the original path if
2445 // that was the specified redirection type.
2446 if (Redirection
== RedirectKind::Fallthrough
&&
2447 isFileNotFound(Result
.getError()))
2448 return getExternalStatus(Path
, OriginalPath
);
2449 return Result
.getError();
2452 ErrorOr
<Status
> S
= status(Path
, OriginalPath
, *Result
);
2453 if (!S
&& Redirection
== RedirectKind::Fallthrough
&&
2454 isFileNotFound(S
.getError(), Result
->E
)) {
2455 // Mapped the file but it wasn't found in the underlying filesystem,
2456 // fallthrough to using the original path if that was the specified
2457 // redirection type.
2458 return getExternalStatus(Path
, OriginalPath
);
2464 bool RedirectingFileSystem::exists(const Twine
&OriginalPath
) {
2465 SmallString
<256> Path
;
2466 OriginalPath
.toVector(Path
);
2468 if (makeAbsolute(Path
))
2471 if (Redirection
== RedirectKind::Fallback
) {
2472 // Attempt to find the original file first, only falling back to the
2473 // mapped file if that fails.
2474 if (ExternalFS
->exists(Path
))
2478 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
= lookupPath(Path
);
2480 // Was not able to map file, fallthrough to using the original path if
2481 // that was the specified redirection type.
2482 if (Redirection
== RedirectKind::Fallthrough
&&
2483 isFileNotFound(Result
.getError()))
2484 return ExternalFS
->exists(Path
);
2488 std::optional
<StringRef
> ExtRedirect
= Result
->getExternalRedirect();
2490 assert(isa
<RedirectingFileSystem::DirectoryEntry
>(Result
->E
));
2494 SmallString
<256> RemappedPath((*ExtRedirect
).str());
2495 if (makeAbsolute(RemappedPath
))
2498 if (ExternalFS
->exists(RemappedPath
))
2501 if (Redirection
== RedirectKind::Fallthrough
) {
2502 // Mapped the file but it wasn't found in the underlying filesystem,
2503 // fallthrough to using the original path if that was the specified
2504 // redirection type.
2505 return ExternalFS
->exists(Path
);
2513 /// Provide a file wrapper with an overriden status.
2514 class FileWithFixedStatus
: public File
{
2515 std::unique_ptr
<File
> InnerFile
;
2519 FileWithFixedStatus(std::unique_ptr
<File
> InnerFile
, Status S
)
2520 : InnerFile(std::move(InnerFile
)), S(std::move(S
)) {}
2522 ErrorOr
<Status
> status() override
{ return S
; }
2523 ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
2525 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
2526 bool IsVolatile
) override
{
2527 return InnerFile
->getBuffer(Name
, FileSize
, RequiresNullTerminator
,
2531 std::error_code
close() override
{ return InnerFile
->close(); }
2533 void setPath(const Twine
&Path
) override
{ S
= S
.copyWithNewName(S
, Path
); }
2538 ErrorOr
<std::unique_ptr
<File
>>
2539 File::getWithPath(ErrorOr
<std::unique_ptr
<File
>> Result
, const Twine
&P
) {
2540 // See \c getRedirectedFileStatus - don't update path if it's exposing an
2542 if (!Result
|| (*Result
)->status()->ExposesExternalVFSPath
)
2545 ErrorOr
<std::unique_ptr
<File
>> F
= std::move(*Result
);
2546 auto Name
= F
->get()->getName();
2547 if (Name
&& Name
.get() != P
.str())
2548 F
->get()->setPath(P
);
2552 ErrorOr
<std::unique_ptr
<File
>>
2553 RedirectingFileSystem::openFileForRead(const Twine
&OriginalPath
) {
2554 SmallString
<256> Path
;
2555 OriginalPath
.toVector(Path
);
2557 if (std::error_code EC
= makeAbsolute(Path
))
2560 if (Redirection
== RedirectKind::Fallback
) {
2561 // Attempt to find the original file first, only falling back to the
2562 // mapped file if that fails.
2563 auto F
= File::getWithPath(ExternalFS
->openFileForRead(Path
), OriginalPath
);
2568 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
= lookupPath(Path
);
2570 // Was not able to map file, fallthrough to using the original path if
2571 // that was the specified redirection type.
2572 if (Redirection
== RedirectKind::Fallthrough
&&
2573 isFileNotFound(Result
.getError()))
2574 return File::getWithPath(ExternalFS
->openFileForRead(Path
), OriginalPath
);
2575 return Result
.getError();
2578 if (!Result
->getExternalRedirect()) // FIXME: errc::not_a_file?
2579 return make_error_code(llvm::errc::invalid_argument
);
2581 StringRef ExtRedirect
= *Result
->getExternalRedirect();
2582 SmallString
<256> RemappedPath(ExtRedirect
.str());
2583 if (std::error_code EC
= makeAbsolute(RemappedPath
))
2586 auto *RE
= cast
<RedirectingFileSystem::RemapEntry
>(Result
->E
);
2589 File::getWithPath(ExternalFS
->openFileForRead(RemappedPath
), ExtRedirect
);
2590 if (!ExternalFile
) {
2591 if (Redirection
== RedirectKind::Fallthrough
&&
2592 isFileNotFound(ExternalFile
.getError(), Result
->E
)) {
2593 // Mapped the file but it wasn't found in the underlying filesystem,
2594 // fallthrough to using the original path if that was the specified
2595 // redirection type.
2596 return File::getWithPath(ExternalFS
->openFileForRead(Path
), OriginalPath
);
2598 return ExternalFile
;
2601 auto ExternalStatus
= (*ExternalFile
)->status();
2602 if (!ExternalStatus
)
2603 return ExternalStatus
.getError();
2605 // Otherwise, the file was successfully remapped. Mark it as such. Also
2606 // replace the underlying path if the external name is being used.
2607 Status S
= getRedirectedFileStatus(
2608 OriginalPath
, RE
->useExternalName(UseExternalNames
), *ExternalStatus
);
2609 return std::unique_ptr
<File
>(
2610 std::make_unique
<FileWithFixedStatus
>(std::move(*ExternalFile
), S
));
2614 RedirectingFileSystem::getRealPath(const Twine
&OriginalPath
,
2615 SmallVectorImpl
<char> &Output
) {
2616 SmallString
<256> Path
;
2617 OriginalPath
.toVector(Path
);
2619 if (std::error_code EC
= makeAbsolute(Path
))
2622 if (Redirection
== RedirectKind::Fallback
) {
2623 // Attempt to find the original file first, only falling back to the
2624 // mapped file if that fails.
2625 std::error_code EC
= ExternalFS
->getRealPath(Path
, Output
);
2630 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
= lookupPath(Path
);
2632 // Was not able to map file, fallthrough to using the original path if
2633 // that was the specified redirection type.
2634 if (Redirection
== RedirectKind::Fallthrough
&&
2635 isFileNotFound(Result
.getError()))
2636 return ExternalFS
->getRealPath(Path
, Output
);
2637 return Result
.getError();
2640 // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2641 // path in the external file system.
2642 if (auto ExtRedirect
= Result
->getExternalRedirect()) {
2643 auto P
= ExternalFS
->getRealPath(*ExtRedirect
, Output
);
2644 if (P
&& Redirection
== RedirectKind::Fallthrough
&&
2645 isFileNotFound(P
, Result
->E
)) {
2646 // Mapped the file but it wasn't found in the underlying filesystem,
2647 // fallthrough to using the original path if that was the specified
2648 // redirection type.
2649 return ExternalFS
->getRealPath(Path
, Output
);
2654 // We found a DirectoryEntry, which does not have a single external contents
2655 // path. Use the canonical virtual path.
2656 if (Redirection
== RedirectKind::Fallthrough
) {
2657 Result
->getPath(Output
);
2660 return llvm::errc::invalid_argument
;
2663 std::unique_ptr
<FileSystem
>
2664 vfs::getVFSFromYAML(std::unique_ptr
<MemoryBuffer
> Buffer
,
2665 SourceMgr::DiagHandlerTy DiagHandler
,
2666 StringRef YAMLFilePath
, void *DiagContext
,
2667 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
2668 return RedirectingFileSystem::create(std::move(Buffer
), DiagHandler
,
2669 YAMLFilePath
, DiagContext
,
2670 std::move(ExternalFS
));
2673 static void getVFSEntries(RedirectingFileSystem::Entry
*SrcE
,
2674 SmallVectorImpl
<StringRef
> &Path
,
2675 SmallVectorImpl
<YAMLVFSEntry
> &Entries
) {
2676 auto Kind
= SrcE
->getKind();
2677 if (Kind
== RedirectingFileSystem::EK_Directory
) {
2678 auto *DE
= dyn_cast
<RedirectingFileSystem::DirectoryEntry
>(SrcE
);
2679 assert(DE
&& "Must be a directory");
2680 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &SubEntry
:
2681 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
2682 Path
.push_back(SubEntry
->getName());
2683 getVFSEntries(SubEntry
.get(), Path
, Entries
);
2689 if (Kind
== RedirectingFileSystem::EK_DirectoryRemap
) {
2690 auto *DR
= dyn_cast
<RedirectingFileSystem::DirectoryRemapEntry
>(SrcE
);
2691 assert(DR
&& "Must be a directory remap");
2692 SmallString
<128> VPath
;
2693 for (auto &Comp
: Path
)
2694 llvm::sys::path::append(VPath
, Comp
);
2696 YAMLVFSEntry(VPath
.c_str(), DR
->getExternalContentsPath()));
2700 assert(Kind
== RedirectingFileSystem::EK_File
&& "Must be a EK_File");
2701 auto *FE
= dyn_cast
<RedirectingFileSystem::FileEntry
>(SrcE
);
2702 assert(FE
&& "Must be a file");
2703 SmallString
<128> VPath
;
2704 for (auto &Comp
: Path
)
2705 llvm::sys::path::append(VPath
, Comp
);
2706 Entries
.push_back(YAMLVFSEntry(VPath
.c_str(), FE
->getExternalContentsPath()));
2709 void vfs::collectVFSFromYAML(std::unique_ptr
<MemoryBuffer
> Buffer
,
2710 SourceMgr::DiagHandlerTy DiagHandler
,
2711 StringRef YAMLFilePath
,
2712 SmallVectorImpl
<YAMLVFSEntry
> &CollectedEntries
,
2714 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
2715 std::unique_ptr
<RedirectingFileSystem
> VFS
= RedirectingFileSystem::create(
2716 std::move(Buffer
), DiagHandler
, YAMLFilePath
, DiagContext
,
2717 std::move(ExternalFS
));
2720 ErrorOr
<RedirectingFileSystem::LookupResult
> RootResult
=
2721 VFS
->lookupPath("/");
2724 SmallVector
<StringRef
, 8> Components
;
2725 Components
.push_back("/");
2726 getVFSEntries(RootResult
->E
, Components
, CollectedEntries
);
2729 UniqueID
vfs::getNextVirtualUniqueID() {
2730 static std::atomic
<unsigned> UID
;
2731 unsigned ID
= ++UID
;
2732 // The following assumes that uint64_t max will never collide with a real
2733 // dev_t value from the OS.
2734 return UniqueID(std::numeric_limits
<uint64_t>::max(), ID
);
2737 void YAMLVFSWriter::addEntry(StringRef VirtualPath
, StringRef RealPath
,
2739 assert(sys::path::is_absolute(VirtualPath
) && "virtual path not absolute");
2740 assert(sys::path::is_absolute(RealPath
) && "real path not absolute");
2741 assert(!pathHasTraversal(VirtualPath
) && "path traversal is not supported");
2742 Mappings
.emplace_back(VirtualPath
, RealPath
, IsDirectory
);
2745 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath
, StringRef RealPath
) {
2746 addEntry(VirtualPath
, RealPath
, /*IsDirectory=*/false);
2749 void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath
,
2750 StringRef RealPath
) {
2751 addEntry(VirtualPath
, RealPath
, /*IsDirectory=*/true);
2757 llvm::raw_ostream
&OS
;
2758 SmallVector
<StringRef
, 16> DirStack
;
2760 unsigned getDirIndent() { return 4 * DirStack
.size(); }
2761 unsigned getFileIndent() { return 4 * (DirStack
.size() + 1); }
2762 bool containedIn(StringRef Parent
, StringRef Path
);
2763 StringRef
containedPart(StringRef Parent
, StringRef Path
);
2764 void startDirectory(StringRef Path
);
2765 void endDirectory();
2766 void writeEntry(StringRef VPath
, StringRef RPath
);
2769 JSONWriter(llvm::raw_ostream
&OS
) : OS(OS
) {}
2771 void write(ArrayRef
<YAMLVFSEntry
> Entries
,
2772 std::optional
<bool> UseExternalNames
,
2773 std::optional
<bool> IsCaseSensitive
,
2774 std::optional
<bool> IsOverlayRelative
, StringRef OverlayDir
);
2779 bool JSONWriter::containedIn(StringRef Parent
, StringRef Path
) {
2780 using namespace llvm::sys
;
2782 // Compare each path component.
2783 auto IParent
= path::begin(Parent
), EParent
= path::end(Parent
);
2784 for (auto IChild
= path::begin(Path
), EChild
= path::end(Path
);
2785 IParent
!= EParent
&& IChild
!= EChild
; ++IParent
, ++IChild
) {
2786 if (*IParent
!= *IChild
)
2789 // Have we exhausted the parent path?
2790 return IParent
== EParent
;
2793 StringRef
JSONWriter::containedPart(StringRef Parent
, StringRef Path
) {
2794 assert(!Parent
.empty());
2795 assert(containedIn(Parent
, Path
));
2796 return Path
.substr(Parent
.size() + 1);
2799 void JSONWriter::startDirectory(StringRef Path
) {
2801 DirStack
.empty() ? Path
: containedPart(DirStack
.back(), Path
);
2802 DirStack
.push_back(Path
);
2803 unsigned Indent
= getDirIndent();
2804 OS
.indent(Indent
) << "{\n";
2805 OS
.indent(Indent
+ 2) << "'type': 'directory',\n";
2806 OS
.indent(Indent
+ 2) << "'name': \"" << llvm::yaml::escape(Name
) << "\",\n";
2807 OS
.indent(Indent
+ 2) << "'contents': [\n";
2810 void JSONWriter::endDirectory() {
2811 unsigned Indent
= getDirIndent();
2812 OS
.indent(Indent
+ 2) << "]\n";
2813 OS
.indent(Indent
) << "}";
2815 DirStack
.pop_back();
2818 void JSONWriter::writeEntry(StringRef VPath
, StringRef RPath
) {
2819 unsigned Indent
= getFileIndent();
2820 OS
.indent(Indent
) << "{\n";
2821 OS
.indent(Indent
+ 2) << "'type': 'file',\n";
2822 OS
.indent(Indent
+ 2) << "'name': \"" << llvm::yaml::escape(VPath
) << "\",\n";
2823 OS
.indent(Indent
+ 2) << "'external-contents': \""
2824 << llvm::yaml::escape(RPath
) << "\"\n";
2825 OS
.indent(Indent
) << "}";
2828 void JSONWriter::write(ArrayRef
<YAMLVFSEntry
> Entries
,
2829 std::optional
<bool> UseExternalNames
,
2830 std::optional
<bool> IsCaseSensitive
,
2831 std::optional
<bool> IsOverlayRelative
,
2832 StringRef OverlayDir
) {
2833 using namespace llvm::sys
;
2837 if (IsCaseSensitive
)
2838 OS
<< " 'case-sensitive': '" << (*IsCaseSensitive
? "true" : "false")
2840 if (UseExternalNames
)
2841 OS
<< " 'use-external-names': '" << (*UseExternalNames
? "true" : "false")
2843 bool UseOverlayRelative
= false;
2844 if (IsOverlayRelative
) {
2845 UseOverlayRelative
= *IsOverlayRelative
;
2846 OS
<< " 'overlay-relative': '" << (UseOverlayRelative
? "true" : "false")
2849 OS
<< " 'roots': [\n";
2851 if (!Entries
.empty()) {
2852 const YAMLVFSEntry
&Entry
= Entries
.front();
2855 Entry
.IsDirectory
? Entry
.VPath
: path::parent_path(Entry
.VPath
)
2858 StringRef RPath
= Entry
.RPath
;
2859 if (UseOverlayRelative
) {
2860 assert(RPath
.starts_with(OverlayDir
) &&
2861 "Overlay dir must be contained in RPath");
2862 RPath
= RPath
.substr(OverlayDir
.size());
2865 bool IsCurrentDirEmpty
= true;
2866 if (!Entry
.IsDirectory
) {
2867 writeEntry(path::filename(Entry
.VPath
), RPath
);
2868 IsCurrentDirEmpty
= false;
2871 for (const auto &Entry
: Entries
.slice(1)) {
2873 Entry
.IsDirectory
? Entry
.VPath
: path::parent_path(Entry
.VPath
);
2874 if (Dir
== DirStack
.back()) {
2875 if (!IsCurrentDirEmpty
) {
2879 bool IsDirPoppedFromStack
= false;
2880 while (!DirStack
.empty() && !containedIn(DirStack
.back(), Dir
)) {
2883 IsDirPoppedFromStack
= true;
2885 if (IsDirPoppedFromStack
|| !IsCurrentDirEmpty
) {
2888 startDirectory(Dir
);
2889 IsCurrentDirEmpty
= true;
2891 StringRef RPath
= Entry
.RPath
;
2892 if (UseOverlayRelative
) {
2893 assert(RPath
.starts_with(OverlayDir
) &&
2894 "Overlay dir must be contained in RPath");
2895 RPath
= RPath
.substr(OverlayDir
.size());
2897 if (!Entry
.IsDirectory
) {
2898 writeEntry(path::filename(Entry
.VPath
), RPath
);
2899 IsCurrentDirEmpty
= false;
2903 while (!DirStack
.empty()) {
2914 void YAMLVFSWriter::write(llvm::raw_ostream
&OS
) {
2915 llvm::sort(Mappings
, [](const YAMLVFSEntry
&LHS
, const YAMLVFSEntry
&RHS
) {
2916 return LHS
.VPath
< RHS
.VPath
;
2919 JSONWriter(OS
).write(Mappings
, UseExternalNames
, IsCaseSensitive
,
2920 IsOverlayRelative
, OverlayDir
);
2923 vfs::recursive_directory_iterator::recursive_directory_iterator(
2924 FileSystem
&FS_
, const Twine
&Path
, std::error_code
&EC
)
2926 directory_iterator I
= FS
->dir_begin(Path
, EC
);
2927 if (I
!= directory_iterator()) {
2928 State
= std::make_shared
<detail::RecDirIterState
>();
2929 State
->Stack
.push_back(I
);
2933 vfs::recursive_directory_iterator
&
2934 recursive_directory_iterator::increment(std::error_code
&EC
) {
2935 assert(FS
&& State
&& !State
->Stack
.empty() && "incrementing past end");
2936 assert(!State
->Stack
.back()->path().empty() && "non-canonical end iterator");
2937 vfs::directory_iterator End
;
2939 if (State
->HasNoPushRequest
)
2940 State
->HasNoPushRequest
= false;
2942 if (State
->Stack
.back()->type() == sys::fs::file_type::directory_file
) {
2943 vfs::directory_iterator I
=
2944 FS
->dir_begin(State
->Stack
.back()->path(), EC
);
2946 State
->Stack
.push_back(I
);
2952 while (!State
->Stack
.empty() && State
->Stack
.back().increment(EC
) == End
)
2953 State
->Stack
.pop_back();
2955 if (State
->Stack
.empty())
2956 State
.reset(); // end iterator
2961 void TracingFileSystem::printImpl(raw_ostream
&OS
, PrintType Type
,
2962 unsigned IndentLevel
) const {
2963 printIndent(OS
, IndentLevel
);
2964 OS
<< "TracingFileSystem\n";
2965 if (Type
== PrintType::Summary
)
2968 printIndent(OS
, IndentLevel
);
2969 OS
<< "NumStatusCalls=" << NumStatusCalls
<< "\n";
2970 printIndent(OS
, IndentLevel
);
2971 OS
<< "NumOpenFileForReadCalls=" << NumOpenFileForReadCalls
<< "\n";
2972 printIndent(OS
, IndentLevel
);
2973 OS
<< "NumDirBeginCalls=" << NumDirBeginCalls
<< "\n";
2974 printIndent(OS
, IndentLevel
);
2975 OS
<< "NumGetRealPathCalls=" << NumGetRealPathCalls
<< "\n";
2976 printIndent(OS
, IndentLevel
);
2977 OS
<< "NumExistsCalls=" << NumExistsCalls
<< "\n";
2978 printIndent(OS
, IndentLevel
);
2979 OS
<< "NumIsLocalCalls=" << NumIsLocalCalls
<< "\n";
2981 if (Type
== PrintType::Contents
)
2982 Type
= PrintType::Summary
;
2983 getUnderlyingFS().print(OS
, Type
, IndentLevel
+ 1);
2986 const char FileSystem::ID
= 0;
2987 const char OverlayFileSystem::ID
= 0;
2988 const char ProxyFileSystem::ID
= 0;
2989 const char InMemoryFileSystem::ID
= 0;
2990 const char RedirectingFileSystem::ID
= 0;
2991 const char TracingFileSystem::ID
= 0;