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 if (!Keys
.count(Key
)) {
1712 error(KeyNode
, "unknown key");
1715 KeyStatus
&S
= Keys
[Key
];
1717 error(KeyNode
, Twine("duplicate key '") + Key
+ "'");
1725 bool checkMissingKeys(yaml::Node
*Obj
, DenseMap
<StringRef
, KeyStatus
> &Keys
) {
1726 for (const auto &I
: Keys
) {
1727 if (I
.second
.Required
&& !I
.second
.Seen
) {
1728 error(Obj
, Twine("missing key '") + I
.first
+ "'");
1736 static RedirectingFileSystem::Entry
*
1737 lookupOrCreateEntry(RedirectingFileSystem
*FS
, StringRef Name
,
1738 RedirectingFileSystem::Entry
*ParentEntry
= nullptr) {
1739 if (!ParentEntry
) { // Look for a existent root
1740 for (const auto &Root
: FS
->Roots
) {
1741 if (Name
== Root
->getName()) {
1742 ParentEntry
= Root
.get();
1746 } else { // Advance to the next component
1747 auto *DE
= dyn_cast
<RedirectingFileSystem::DirectoryEntry
>(ParentEntry
);
1748 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &Content
:
1749 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
1751 dyn_cast
<RedirectingFileSystem::DirectoryEntry
>(Content
.get());
1752 if (DirContent
&& Name
== Content
->getName())
1757 // ... or create a new one
1758 std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
1759 std::make_unique
<RedirectingFileSystem::DirectoryEntry
>(
1760 Name
, Status("", getNextVirtualUniqueID(),
1761 std::chrono::system_clock::now(), 0, 0, 0,
1762 file_type::directory_file
, sys::fs::all_all
));
1764 if (!ParentEntry
) { // Add a new root to the overlay
1765 FS
->Roots
.push_back(std::move(E
));
1766 ParentEntry
= FS
->Roots
.back().get();
1770 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(ParentEntry
);
1771 DE
->addContent(std::move(E
));
1772 return DE
->getLastContent();
1776 void uniqueOverlayTree(RedirectingFileSystem
*FS
,
1777 RedirectingFileSystem::Entry
*SrcE
,
1778 RedirectingFileSystem::Entry
*NewParentE
= nullptr) {
1779 StringRef Name
= SrcE
->getName();
1780 switch (SrcE
->getKind()) {
1781 case RedirectingFileSystem::EK_Directory
: {
1782 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(SrcE
);
1783 // Empty directories could be present in the YAML as a way to
1784 // describe a file for a current directory after some of its subdir
1785 // is parsed. This only leads to redundant walks, ignore it.
1787 NewParentE
= lookupOrCreateEntry(FS
, Name
, NewParentE
);
1788 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &SubEntry
:
1789 llvm::make_range(DE
->contents_begin(), DE
->contents_end()))
1790 uniqueOverlayTree(FS
, SubEntry
.get(), NewParentE
);
1793 case RedirectingFileSystem::EK_DirectoryRemap
: {
1794 assert(NewParentE
&& "Parent entry must exist");
1795 auto *DR
= cast
<RedirectingFileSystem::DirectoryRemapEntry
>(SrcE
);
1796 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(NewParentE
);
1798 std::make_unique
<RedirectingFileSystem::DirectoryRemapEntry
>(
1799 Name
, DR
->getExternalContentsPath(), DR
->getUseName()));
1802 case RedirectingFileSystem::EK_File
: {
1803 assert(NewParentE
&& "Parent entry must exist");
1804 auto *FE
= cast
<RedirectingFileSystem::FileEntry
>(SrcE
);
1805 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(NewParentE
);
1806 DE
->addContent(std::make_unique
<RedirectingFileSystem::FileEntry
>(
1807 Name
, FE
->getExternalContentsPath(), FE
->getUseName()));
1813 std::unique_ptr
<RedirectingFileSystem::Entry
>
1814 parseEntry(yaml::Node
*N
, RedirectingFileSystem
*FS
, bool IsRootEntry
) {
1815 auto *M
= dyn_cast
<yaml::MappingNode
>(N
);
1817 error(N
, "expected mapping node for file or directory entry");
1821 KeyStatusPair Fields
[] = {
1822 KeyStatusPair("name", true),
1823 KeyStatusPair("type", true),
1824 KeyStatusPair("contents", false),
1825 KeyStatusPair("external-contents", false),
1826 KeyStatusPair("use-external-name", false),
1829 DenseMap
<StringRef
, KeyStatus
> Keys(std::begin(Fields
), std::end(Fields
));
1831 enum { CF_NotSet
, CF_List
, CF_External
} ContentsField
= CF_NotSet
;
1832 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>>
1834 SmallString
<256> ExternalContentsPath
;
1835 SmallString
<256> Name
;
1836 yaml::Node
*NameValueNode
= nullptr;
1837 auto UseExternalName
= RedirectingFileSystem::NK_NotSet
;
1838 RedirectingFileSystem::EntryKind Kind
;
1840 for (auto &I
: *M
) {
1842 // Reuse the buffer for key and value, since we don't look at key after
1844 SmallString
<256> Buffer
;
1845 if (!parseScalarString(I
.getKey(), Key
, Buffer
))
1848 if (!checkDuplicateOrUnknownKey(I
.getKey(), Key
, Keys
))
1852 if (Key
== "name") {
1853 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1856 NameValueNode
= I
.getValue();
1857 // Guarantee that old YAML files containing paths with ".." and "."
1858 // are properly canonicalized before read into the VFS.
1859 Name
= canonicalize(Value
).str();
1860 } else if (Key
== "type") {
1861 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1863 if (Value
== "file")
1864 Kind
= RedirectingFileSystem::EK_File
;
1865 else if (Value
== "directory")
1866 Kind
= RedirectingFileSystem::EK_Directory
;
1867 else if (Value
== "directory-remap")
1868 Kind
= RedirectingFileSystem::EK_DirectoryRemap
;
1870 error(I
.getValue(), "unknown value for 'type'");
1873 } else if (Key
== "contents") {
1874 if (ContentsField
!= CF_NotSet
) {
1876 "entry already has 'contents' or 'external-contents'");
1879 ContentsField
= CF_List
;
1880 auto *Contents
= dyn_cast
<yaml::SequenceNode
>(I
.getValue());
1882 // FIXME: this is only for directories, what about files?
1883 error(I
.getValue(), "expected array");
1887 for (auto &I
: *Contents
) {
1888 if (std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
1889 parseEntry(&I
, FS
, /*IsRootEntry*/ false))
1890 EntryArrayContents
.push_back(std::move(E
));
1894 } else if (Key
== "external-contents") {
1895 if (ContentsField
!= CF_NotSet
) {
1897 "entry already has 'contents' or 'external-contents'");
1900 ContentsField
= CF_External
;
1901 if (!parseScalarString(I
.getValue(), Value
, Buffer
))
1904 SmallString
<256> FullPath
;
1905 if (FS
->IsRelativeOverlay
) {
1906 FullPath
= FS
->getOverlayFileDir();
1907 assert(!FullPath
.empty() &&
1908 "External contents prefix directory must exist");
1909 llvm::sys::path::append(FullPath
, Value
);
1914 // Guarantee that old YAML files containing paths with ".." and "."
1915 // are properly canonicalized before read into the VFS.
1916 FullPath
= canonicalize(FullPath
);
1917 ExternalContentsPath
= FullPath
.str();
1918 } else if (Key
== "use-external-name") {
1920 if (!parseScalarBool(I
.getValue(), Val
))
1922 UseExternalName
= Val
? RedirectingFileSystem::NK_External
1923 : RedirectingFileSystem::NK_Virtual
;
1925 llvm_unreachable("key missing from Keys");
1929 if (Stream
.failed())
1932 // check for missing keys
1933 if (ContentsField
== CF_NotSet
) {
1934 error(N
, "missing key 'contents' or 'external-contents'");
1937 if (!checkMissingKeys(N
, Keys
))
1940 // check invalid configuration
1941 if (Kind
== RedirectingFileSystem::EK_Directory
&&
1942 UseExternalName
!= RedirectingFileSystem::NK_NotSet
) {
1943 error(N
, "'use-external-name' is not supported for 'directory' entries");
1947 if (Kind
== RedirectingFileSystem::EK_DirectoryRemap
&&
1948 ContentsField
== CF_List
) {
1949 error(N
, "'contents' is not supported for 'directory-remap' entries");
1953 sys::path::Style path_style
= sys::path::Style::native
;
1955 // VFS root entries may be in either Posix or Windows style. Figure out
1956 // which style we have, and use it consistently.
1957 if (sys::path::is_absolute(Name
, sys::path::Style::posix
)) {
1958 path_style
= sys::path::Style::posix
;
1959 } else if (sys::path::is_absolute(Name
,
1960 sys::path::Style::windows_backslash
)) {
1961 path_style
= sys::path::Style::windows_backslash
;
1963 // Relative VFS root entries are made absolute to either the overlay
1964 // directory, or the current working directory, then we can determine
1965 // the path style from that.
1967 if (FS
->RootRelative
==
1968 RedirectingFileSystem::RootRelativeKind::OverlayDir
) {
1969 StringRef FullPath
= FS
->getOverlayFileDir();
1970 assert(!FullPath
.empty() && "Overlay file directory must exist");
1971 EC
= FS
->makeAbsolute(FullPath
, Name
);
1972 Name
= canonicalize(Name
);
1974 EC
= sys::fs::make_absolute(Name
);
1977 assert(NameValueNode
&& "Name presence should be checked earlier");
1980 "entry with relative path at the root level is not discoverable");
1983 path_style
= sys::path::is_absolute(Name
, sys::path::Style::posix
)
1984 ? sys::path::Style::posix
1985 : sys::path::Style::windows_backslash
;
1987 // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will
1988 // return true even if `Name` is using forward slashes. Distinguish
1989 // between windows_backslash and windows_slash.
1990 if (path_style
== sys::path::Style::windows_backslash
&&
1991 getExistingStyle(Name
) != sys::path::Style::windows_backslash
)
1992 path_style
= sys::path::Style::windows_slash
;
1995 // Remove trailing slash(es), being careful not to remove the root path
1996 StringRef Trimmed
= Name
;
1997 size_t RootPathLen
= sys::path::root_path(Trimmed
, path_style
).size();
1998 while (Trimmed
.size() > RootPathLen
&&
1999 sys::path::is_separator(Trimmed
.back(), path_style
))
2000 Trimmed
= Trimmed
.slice(0, Trimmed
.size() - 1);
2002 // Get the last component
2003 StringRef LastComponent
= sys::path::filename(Trimmed
, path_style
);
2005 std::unique_ptr
<RedirectingFileSystem::Entry
> Result
;
2007 case RedirectingFileSystem::EK_File
:
2008 Result
= std::make_unique
<RedirectingFileSystem::FileEntry
>(
2009 LastComponent
, std::move(ExternalContentsPath
), UseExternalName
);
2011 case RedirectingFileSystem::EK_DirectoryRemap
:
2012 Result
= std::make_unique
<RedirectingFileSystem::DirectoryRemapEntry
>(
2013 LastComponent
, std::move(ExternalContentsPath
), UseExternalName
);
2015 case RedirectingFileSystem::EK_Directory
:
2016 Result
= std::make_unique
<RedirectingFileSystem::DirectoryEntry
>(
2017 LastComponent
, std::move(EntryArrayContents
),
2018 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2019 0, 0, 0, file_type::directory_file
, sys::fs::all_all
));
2023 StringRef Parent
= sys::path::parent_path(Trimmed
, path_style
);
2027 // if 'name' contains multiple components, create implicit directory entries
2028 for (sys::path::reverse_iterator I
= sys::path::rbegin(Parent
, path_style
),
2029 E
= sys::path::rend(Parent
);
2031 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>> Entries
;
2032 Entries
.push_back(std::move(Result
));
2033 Result
= std::make_unique
<RedirectingFileSystem::DirectoryEntry
>(
2034 *I
, std::move(Entries
),
2035 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2036 0, 0, 0, file_type::directory_file
, sys::fs::all_all
));
2042 RedirectingFileSystemParser(yaml::Stream
&S
) : Stream(S
) {}
2045 bool parse(yaml::Node
*Root
, RedirectingFileSystem
*FS
) {
2046 auto *Top
= dyn_cast
<yaml::MappingNode
>(Root
);
2048 error(Root
, "expected mapping node");
2052 KeyStatusPair Fields
[] = {
2053 KeyStatusPair("version", true),
2054 KeyStatusPair("case-sensitive", false),
2055 KeyStatusPair("use-external-names", false),
2056 KeyStatusPair("root-relative", false),
2057 KeyStatusPair("overlay-relative", false),
2058 KeyStatusPair("fallthrough", false),
2059 KeyStatusPair("redirecting-with", false),
2060 KeyStatusPair("roots", true),
2063 DenseMap
<StringRef
, KeyStatus
> Keys(std::begin(Fields
), std::end(Fields
));
2064 std::vector
<std::unique_ptr
<RedirectingFileSystem::Entry
>> RootEntries
;
2066 // Parse configuration and 'roots'
2067 for (auto &I
: *Top
) {
2068 SmallString
<10> KeyBuffer
;
2070 if (!parseScalarString(I
.getKey(), Key
, KeyBuffer
))
2073 if (!checkDuplicateOrUnknownKey(I
.getKey(), Key
, Keys
))
2076 if (Key
== "roots") {
2077 auto *Roots
= dyn_cast
<yaml::SequenceNode
>(I
.getValue());
2079 error(I
.getValue(), "expected array");
2083 for (auto &I
: *Roots
) {
2084 if (std::unique_ptr
<RedirectingFileSystem::Entry
> E
=
2085 parseEntry(&I
, FS
, /*IsRootEntry*/ true))
2086 RootEntries
.push_back(std::move(E
));
2090 } else if (Key
== "version") {
2091 StringRef VersionString
;
2092 SmallString
<4> Storage
;
2093 if (!parseScalarString(I
.getValue(), VersionString
, Storage
))
2096 if (VersionString
.getAsInteger
<int>(10, Version
)) {
2097 error(I
.getValue(), "expected integer");
2101 error(I
.getValue(), "invalid version number");
2105 error(I
.getValue(), "version mismatch, expected 0");
2108 } else if (Key
== "case-sensitive") {
2109 if (!parseScalarBool(I
.getValue(), FS
->CaseSensitive
))
2111 } else if (Key
== "overlay-relative") {
2112 if (!parseScalarBool(I
.getValue(), FS
->IsRelativeOverlay
))
2114 } else if (Key
== "use-external-names") {
2115 if (!parseScalarBool(I
.getValue(), FS
->UseExternalNames
))
2117 } else if (Key
== "fallthrough") {
2118 if (Keys
["redirecting-with"].Seen
) {
2120 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2124 bool ShouldFallthrough
= false;
2125 if (!parseScalarBool(I
.getValue(), ShouldFallthrough
))
2128 if (ShouldFallthrough
) {
2129 FS
->Redirection
= RedirectingFileSystem::RedirectKind::Fallthrough
;
2131 FS
->Redirection
= RedirectingFileSystem::RedirectKind::RedirectOnly
;
2133 } else if (Key
== "redirecting-with") {
2134 if (Keys
["fallthrough"].Seen
) {
2136 "'fallthrough' and 'redirecting-with' are mutually exclusive");
2140 if (auto Kind
= parseRedirectKind(I
.getValue())) {
2141 FS
->Redirection
= *Kind
;
2143 error(I
.getValue(), "expected valid redirect kind");
2146 } else if (Key
== "root-relative") {
2147 if (auto Kind
= parseRootRelativeKind(I
.getValue())) {
2148 FS
->RootRelative
= *Kind
;
2150 error(I
.getValue(), "expected valid root-relative kind");
2154 llvm_unreachable("key missing from Keys");
2158 if (Stream
.failed())
2161 if (!checkMissingKeys(Top
, Keys
))
2164 // Now that we sucessefully parsed the YAML file, canonicalize the internal
2165 // representation to a proper directory tree so that we can search faster
2167 for (auto &E
: RootEntries
)
2168 uniqueOverlayTree(FS
, E
.get());
2174 std::unique_ptr
<RedirectingFileSystem
>
2175 RedirectingFileSystem::create(std::unique_ptr
<MemoryBuffer
> Buffer
,
2176 SourceMgr::DiagHandlerTy DiagHandler
,
2177 StringRef YAMLFilePath
, void *DiagContext
,
2178 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
2180 yaml::Stream
Stream(Buffer
->getMemBufferRef(), SM
);
2182 SM
.setDiagHandler(DiagHandler
, DiagContext
);
2183 yaml::document_iterator DI
= Stream
.begin();
2184 yaml::Node
*Root
= DI
->getRoot();
2185 if (DI
== Stream
.end() || !Root
) {
2186 SM
.PrintMessage(SMLoc(), SourceMgr::DK_Error
, "expected root node");
2190 RedirectingFileSystemParser
P(Stream
);
2192 std::unique_ptr
<RedirectingFileSystem
> FS(
2193 new RedirectingFileSystem(ExternalFS
));
2195 if (!YAMLFilePath
.empty()) {
2196 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
2197 // to each 'external-contents' path.
2200 // -ivfsoverlay dummy.cache/vfs/vfs.yaml
2202 // FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs
2204 SmallString
<256> OverlayAbsDir
= sys::path::parent_path(YAMLFilePath
);
2205 std::error_code EC
= llvm::sys::fs::make_absolute(OverlayAbsDir
);
2206 assert(!EC
&& "Overlay dir final path must be absolute");
2208 FS
->setOverlayFileDir(OverlayAbsDir
);
2211 if (!P
.parse(Root
, FS
.get()))
2217 std::unique_ptr
<RedirectingFileSystem
> RedirectingFileSystem::create(
2218 ArrayRef
<std::pair
<std::string
, std::string
>> RemappedFiles
,
2219 bool UseExternalNames
, FileSystem
&ExternalFS
) {
2220 std::unique_ptr
<RedirectingFileSystem
> FS(
2221 new RedirectingFileSystem(&ExternalFS
));
2222 FS
->UseExternalNames
= UseExternalNames
;
2224 StringMap
<RedirectingFileSystem::Entry
*> Entries
;
2226 for (auto &Mapping
: llvm::reverse(RemappedFiles
)) {
2227 SmallString
<128> From
= StringRef(Mapping
.first
);
2228 SmallString
<128> To
= StringRef(Mapping
.second
);
2230 auto EC
= ExternalFS
.makeAbsolute(From
);
2232 assert(!EC
&& "Could not make absolute path");
2235 // Check if we've already mapped this file. The first one we see (in the
2236 // reverse iteration) wins.
2237 RedirectingFileSystem::Entry
*&ToEntry
= Entries
[From
];
2241 // Add parent directories.
2242 RedirectingFileSystem::Entry
*Parent
= nullptr;
2243 StringRef FromDirectory
= llvm::sys::path::parent_path(From
);
2244 for (auto I
= llvm::sys::path::begin(FromDirectory
),
2245 E
= llvm::sys::path::end(FromDirectory
);
2247 Parent
= RedirectingFileSystemParser::lookupOrCreateEntry(FS
.get(), *I
,
2250 assert(Parent
&& "File without a directory?");
2252 auto EC
= ExternalFS
.makeAbsolute(To
);
2254 assert(!EC
&& "Could not make absolute path");
2258 auto NewFile
= std::make_unique
<RedirectingFileSystem::FileEntry
>(
2259 llvm::sys::path::filename(From
), To
,
2260 UseExternalNames
? RedirectingFileSystem::NK_External
2261 : RedirectingFileSystem::NK_Virtual
);
2262 ToEntry
= NewFile
.get();
2263 cast
<RedirectingFileSystem::DirectoryEntry
>(Parent
)->addContent(
2264 std::move(NewFile
));
2270 RedirectingFileSystem::LookupResult::LookupResult(
2271 Entry
*E
, sys::path::const_iterator Start
, sys::path::const_iterator End
)
2273 assert(E
!= nullptr);
2274 // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
2275 // path of the directory it maps to in the external file system plus any
2276 // remaining path components in the provided iterator.
2277 if (auto *DRE
= dyn_cast
<RedirectingFileSystem::DirectoryRemapEntry
>(E
)) {
2278 SmallString
<256> Redirect(DRE
->getExternalContentsPath());
2279 sys::path::append(Redirect
, Start
, End
,
2280 getExistingStyle(DRE
->getExternalContentsPath()));
2281 ExternalRedirect
= std::string(Redirect
);
2285 void RedirectingFileSystem::LookupResult::getPath(
2286 llvm::SmallVectorImpl
<char> &Result
) const {
2288 for (Entry
*Parent
: Parents
)
2289 llvm::sys::path::append(Result
, Parent
->getName());
2290 llvm::sys::path::append(Result
, E
->getName());
2293 std::error_code
RedirectingFileSystem::makeCanonicalForLookup(
2294 SmallVectorImpl
<char> &Path
) const {
2295 if (std::error_code EC
= makeAbsolute(Path
))
2298 llvm::SmallString
<256> CanonicalPath
=
2299 canonicalize(StringRef(Path
.data(), Path
.size()));
2300 if (CanonicalPath
.empty())
2301 return make_error_code(llvm::errc::invalid_argument
);
2303 Path
.assign(CanonicalPath
.begin(), CanonicalPath
.end());
2307 ErrorOr
<RedirectingFileSystem::LookupResult
>
2308 RedirectingFileSystem::lookupPath(StringRef Path
) const {
2309 llvm::SmallString
<128> CanonicalPath(Path
);
2310 if (std::error_code EC
= makeCanonicalForLookup(CanonicalPath
))
2313 // RedirectOnly means the VFS is always used.
2314 if (UsageTrackingActive
&& Redirection
== RedirectKind::RedirectOnly
)
2317 sys::path::const_iterator Start
= sys::path::begin(CanonicalPath
);
2318 sys::path::const_iterator End
= sys::path::end(CanonicalPath
);
2319 llvm::SmallVector
<Entry
*, 32> Entries
;
2320 for (const auto &Root
: Roots
) {
2321 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
=
2322 lookupPathImpl(Start
, End
, Root
.get(), Entries
);
2323 if (UsageTrackingActive
&& Result
&& isa
<RemapEntry
>(Result
->E
))
2325 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
) {
2326 Result
->Parents
= std::move(Entries
);
2330 return make_error_code(llvm::errc::no_such_file_or_directory
);
2333 ErrorOr
<RedirectingFileSystem::LookupResult
>
2334 RedirectingFileSystem::lookupPathImpl(
2335 sys::path::const_iterator Start
, sys::path::const_iterator End
,
2336 RedirectingFileSystem::Entry
*From
,
2337 llvm::SmallVectorImpl
<Entry
*> &Entries
) const {
2338 assert(!isTraversalComponent(*Start
) &&
2339 !isTraversalComponent(From
->getName()) &&
2340 "Paths should not contain traversal components");
2342 StringRef FromName
= From
->getName();
2344 // Forward the search to the next component in case this is an empty one.
2345 if (!FromName
.empty()) {
2346 if (!pathComponentMatches(*Start
, FromName
))
2347 return make_error_code(llvm::errc::no_such_file_or_directory
);
2353 return LookupResult(From
, Start
, End
);
2357 if (isa
<RedirectingFileSystem::FileEntry
>(From
))
2358 return make_error_code(llvm::errc::not_a_directory
);
2360 if (isa
<RedirectingFileSystem::DirectoryRemapEntry
>(From
))
2361 return LookupResult(From
, Start
, End
);
2363 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(From
);
2364 for (const std::unique_ptr
<RedirectingFileSystem::Entry
> &DirEntry
:
2365 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
2366 Entries
.push_back(From
);
2367 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
=
2368 lookupPathImpl(Start
, End
, DirEntry
.get(), Entries
);
2369 if (Result
|| Result
.getError() != llvm::errc::no_such_file_or_directory
)
2374 return make_error_code(llvm::errc::no_such_file_or_directory
);
2377 static Status
getRedirectedFileStatus(const Twine
&OriginalPath
,
2378 bool UseExternalNames
,
2379 Status ExternalStatus
) {
2380 // The path has been mapped by some nested VFS and exposes an external path,
2381 // don't override it with the original path.
2382 if (ExternalStatus
.ExposesExternalVFSPath
)
2383 return ExternalStatus
;
2385 Status S
= ExternalStatus
;
2386 if (!UseExternalNames
)
2387 S
= Status::copyWithNewName(S
, OriginalPath
);
2389 S
.ExposesExternalVFSPath
= true;
2393 ErrorOr
<Status
> RedirectingFileSystem::status(
2394 const Twine
&LookupPath
, const Twine
&OriginalPath
,
2395 const RedirectingFileSystem::LookupResult
&Result
) {
2396 if (std::optional
<StringRef
> ExtRedirect
= Result
.getExternalRedirect()) {
2397 SmallString
<256> RemappedPath((*ExtRedirect
).str());
2398 if (std::error_code EC
= makeAbsolute(RemappedPath
))
2401 ErrorOr
<Status
> S
= ExternalFS
->status(RemappedPath
);
2404 S
= Status::copyWithNewName(*S
, *ExtRedirect
);
2405 auto *RE
= cast
<RedirectingFileSystem::RemapEntry
>(Result
.E
);
2406 return getRedirectedFileStatus(OriginalPath
,
2407 RE
->useExternalName(UseExternalNames
), *S
);
2410 auto *DE
= cast
<RedirectingFileSystem::DirectoryEntry
>(Result
.E
);
2411 return Status::copyWithNewName(DE
->getStatus(), LookupPath
);
2415 RedirectingFileSystem::getExternalStatus(const Twine
&LookupPath
,
2416 const Twine
&OriginalPath
) const {
2417 auto Result
= ExternalFS
->status(LookupPath
);
2419 // The path has been mapped by some nested VFS, don't override it with the
2421 if (!Result
|| Result
->ExposesExternalVFSPath
)
2423 return Status::copyWithNewName(Result
.get(), OriginalPath
);
2426 ErrorOr
<Status
> RedirectingFileSystem::status(const Twine
&OriginalPath
) {
2427 SmallString
<256> Path
;
2428 OriginalPath
.toVector(Path
);
2430 if (std::error_code EC
= makeAbsolute(Path
))
2433 if (Redirection
== RedirectKind::Fallback
) {
2434 // Attempt to find the original file first, only falling back to the
2435 // mapped file if that fails.
2436 ErrorOr
<Status
> S
= getExternalStatus(Path
, OriginalPath
);
2441 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
= lookupPath(Path
);
2443 // Was not able to map file, fallthrough to using the original path if
2444 // that was the specified redirection type.
2445 if (Redirection
== RedirectKind::Fallthrough
&&
2446 isFileNotFound(Result
.getError()))
2447 return getExternalStatus(Path
, OriginalPath
);
2448 return Result
.getError();
2451 ErrorOr
<Status
> S
= status(Path
, OriginalPath
, *Result
);
2452 if (!S
&& Redirection
== RedirectKind::Fallthrough
&&
2453 isFileNotFound(S
.getError(), Result
->E
)) {
2454 // Mapped the file but it wasn't found in the underlying filesystem,
2455 // fallthrough to using the original path if that was the specified
2456 // redirection type.
2457 return getExternalStatus(Path
, OriginalPath
);
2463 bool RedirectingFileSystem::exists(const Twine
&OriginalPath
) {
2464 SmallString
<256> Path
;
2465 OriginalPath
.toVector(Path
);
2467 if (makeAbsolute(Path
))
2470 if (Redirection
== RedirectKind::Fallback
) {
2471 // Attempt to find the original file first, only falling back to the
2472 // mapped file if that fails.
2473 if (ExternalFS
->exists(Path
))
2477 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
= lookupPath(Path
);
2479 // Was not able to map file, fallthrough to using the original path if
2480 // that was the specified redirection type.
2481 if (Redirection
== RedirectKind::Fallthrough
&&
2482 isFileNotFound(Result
.getError()))
2483 return ExternalFS
->exists(Path
);
2487 std::optional
<StringRef
> ExtRedirect
= Result
->getExternalRedirect();
2489 assert(isa
<RedirectingFileSystem::DirectoryEntry
>(Result
->E
));
2493 SmallString
<256> RemappedPath((*ExtRedirect
).str());
2494 if (makeAbsolute(RemappedPath
))
2497 if (ExternalFS
->exists(RemappedPath
))
2500 if (Redirection
== RedirectKind::Fallthrough
) {
2501 // Mapped the file but it wasn't found in the underlying filesystem,
2502 // fallthrough to using the original path if that was the specified
2503 // redirection type.
2504 return ExternalFS
->exists(Path
);
2512 /// Provide a file wrapper with an overriden status.
2513 class FileWithFixedStatus
: public File
{
2514 std::unique_ptr
<File
> InnerFile
;
2518 FileWithFixedStatus(std::unique_ptr
<File
> InnerFile
, Status S
)
2519 : InnerFile(std::move(InnerFile
)), S(std::move(S
)) {}
2521 ErrorOr
<Status
> status() override
{ return S
; }
2522 ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
2524 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
2525 bool IsVolatile
) override
{
2526 return InnerFile
->getBuffer(Name
, FileSize
, RequiresNullTerminator
,
2530 std::error_code
close() override
{ return InnerFile
->close(); }
2532 void setPath(const Twine
&Path
) override
{ S
= S
.copyWithNewName(S
, Path
); }
2537 ErrorOr
<std::unique_ptr
<File
>>
2538 File::getWithPath(ErrorOr
<std::unique_ptr
<File
>> Result
, const Twine
&P
) {
2539 // See \c getRedirectedFileStatus - don't update path if it's exposing an
2541 if (!Result
|| (*Result
)->status()->ExposesExternalVFSPath
)
2544 ErrorOr
<std::unique_ptr
<File
>> F
= std::move(*Result
);
2545 auto Name
= F
->get()->getName();
2546 if (Name
&& Name
.get() != P
.str())
2547 F
->get()->setPath(P
);
2551 ErrorOr
<std::unique_ptr
<File
>>
2552 RedirectingFileSystem::openFileForRead(const Twine
&OriginalPath
) {
2553 SmallString
<256> Path
;
2554 OriginalPath
.toVector(Path
);
2556 if (std::error_code EC
= makeAbsolute(Path
))
2559 if (Redirection
== RedirectKind::Fallback
) {
2560 // Attempt to find the original file first, only falling back to the
2561 // mapped file if that fails.
2562 auto F
= File::getWithPath(ExternalFS
->openFileForRead(Path
), OriginalPath
);
2567 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
= lookupPath(Path
);
2569 // Was not able to map file, fallthrough to using the original path if
2570 // that was the specified redirection type.
2571 if (Redirection
== RedirectKind::Fallthrough
&&
2572 isFileNotFound(Result
.getError()))
2573 return File::getWithPath(ExternalFS
->openFileForRead(Path
), OriginalPath
);
2574 return Result
.getError();
2577 if (!Result
->getExternalRedirect()) // FIXME: errc::not_a_file?
2578 return make_error_code(llvm::errc::invalid_argument
);
2580 StringRef ExtRedirect
= *Result
->getExternalRedirect();
2581 SmallString
<256> RemappedPath(ExtRedirect
.str());
2582 if (std::error_code EC
= makeAbsolute(RemappedPath
))
2585 auto *RE
= cast
<RedirectingFileSystem::RemapEntry
>(Result
->E
);
2588 File::getWithPath(ExternalFS
->openFileForRead(RemappedPath
), ExtRedirect
);
2589 if (!ExternalFile
) {
2590 if (Redirection
== RedirectKind::Fallthrough
&&
2591 isFileNotFound(ExternalFile
.getError(), Result
->E
)) {
2592 // Mapped the file but it wasn't found in the underlying filesystem,
2593 // fallthrough to using the original path if that was the specified
2594 // redirection type.
2595 return File::getWithPath(ExternalFS
->openFileForRead(Path
), OriginalPath
);
2597 return ExternalFile
;
2600 auto ExternalStatus
= (*ExternalFile
)->status();
2601 if (!ExternalStatus
)
2602 return ExternalStatus
.getError();
2604 // Otherwise, the file was successfully remapped. Mark it as such. Also
2605 // replace the underlying path if the external name is being used.
2606 Status S
= getRedirectedFileStatus(
2607 OriginalPath
, RE
->useExternalName(UseExternalNames
), *ExternalStatus
);
2608 return std::unique_ptr
<File
>(
2609 std::make_unique
<FileWithFixedStatus
>(std::move(*ExternalFile
), S
));
2613 RedirectingFileSystem::getRealPath(const Twine
&OriginalPath
,
2614 SmallVectorImpl
<char> &Output
) {
2615 SmallString
<256> Path
;
2616 OriginalPath
.toVector(Path
);
2618 if (std::error_code EC
= makeAbsolute(Path
))
2621 if (Redirection
== RedirectKind::Fallback
) {
2622 // Attempt to find the original file first, only falling back to the
2623 // mapped file if that fails.
2624 std::error_code EC
= ExternalFS
->getRealPath(Path
, Output
);
2629 ErrorOr
<RedirectingFileSystem::LookupResult
> Result
= lookupPath(Path
);
2631 // Was not able to map file, fallthrough to using the original path if
2632 // that was the specified redirection type.
2633 if (Redirection
== RedirectKind::Fallthrough
&&
2634 isFileNotFound(Result
.getError()))
2635 return ExternalFS
->getRealPath(Path
, Output
);
2636 return Result
.getError();
2639 // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2640 // path in the external file system.
2641 if (auto ExtRedirect
= Result
->getExternalRedirect()) {
2642 auto P
= ExternalFS
->getRealPath(*ExtRedirect
, Output
);
2643 if (P
&& Redirection
== RedirectKind::Fallthrough
&&
2644 isFileNotFound(P
, Result
->E
)) {
2645 // Mapped the file but it wasn't found in the underlying filesystem,
2646 // fallthrough to using the original path if that was the specified
2647 // redirection type.
2648 return ExternalFS
->getRealPath(Path
, Output
);
2653 // We found a DirectoryEntry, which does not have a single external contents
2654 // path. Use the canonical virtual path.
2655 if (Redirection
== RedirectKind::Fallthrough
) {
2656 Result
->getPath(Output
);
2659 return llvm::errc::invalid_argument
;
2662 std::unique_ptr
<FileSystem
>
2663 vfs::getVFSFromYAML(std::unique_ptr
<MemoryBuffer
> Buffer
,
2664 SourceMgr::DiagHandlerTy DiagHandler
,
2665 StringRef YAMLFilePath
, void *DiagContext
,
2666 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
2667 return RedirectingFileSystem::create(std::move(Buffer
), DiagHandler
,
2668 YAMLFilePath
, DiagContext
,
2669 std::move(ExternalFS
));
2672 static void getVFSEntries(RedirectingFileSystem::Entry
*SrcE
,
2673 SmallVectorImpl
<StringRef
> &Path
,
2674 SmallVectorImpl
<YAMLVFSEntry
> &Entries
) {
2675 auto Kind
= SrcE
->getKind();
2676 if (Kind
== RedirectingFileSystem::EK_Directory
) {
2677 auto *DE
= dyn_cast
<RedirectingFileSystem::DirectoryEntry
>(SrcE
);
2678 assert(DE
&& "Must be a directory");
2679 for (std::unique_ptr
<RedirectingFileSystem::Entry
> &SubEntry
:
2680 llvm::make_range(DE
->contents_begin(), DE
->contents_end())) {
2681 Path
.push_back(SubEntry
->getName());
2682 getVFSEntries(SubEntry
.get(), Path
, Entries
);
2688 if (Kind
== RedirectingFileSystem::EK_DirectoryRemap
) {
2689 auto *DR
= dyn_cast
<RedirectingFileSystem::DirectoryRemapEntry
>(SrcE
);
2690 assert(DR
&& "Must be a directory remap");
2691 SmallString
<128> VPath
;
2692 for (auto &Comp
: Path
)
2693 llvm::sys::path::append(VPath
, Comp
);
2695 YAMLVFSEntry(VPath
.c_str(), DR
->getExternalContentsPath()));
2699 assert(Kind
== RedirectingFileSystem::EK_File
&& "Must be a EK_File");
2700 auto *FE
= dyn_cast
<RedirectingFileSystem::FileEntry
>(SrcE
);
2701 assert(FE
&& "Must be a file");
2702 SmallString
<128> VPath
;
2703 for (auto &Comp
: Path
)
2704 llvm::sys::path::append(VPath
, Comp
);
2705 Entries
.push_back(YAMLVFSEntry(VPath
.c_str(), FE
->getExternalContentsPath()));
2708 void vfs::collectVFSFromYAML(std::unique_ptr
<MemoryBuffer
> Buffer
,
2709 SourceMgr::DiagHandlerTy DiagHandler
,
2710 StringRef YAMLFilePath
,
2711 SmallVectorImpl
<YAMLVFSEntry
> &CollectedEntries
,
2713 IntrusiveRefCntPtr
<FileSystem
> ExternalFS
) {
2714 std::unique_ptr
<RedirectingFileSystem
> VFS
= RedirectingFileSystem::create(
2715 std::move(Buffer
), DiagHandler
, YAMLFilePath
, DiagContext
,
2716 std::move(ExternalFS
));
2719 ErrorOr
<RedirectingFileSystem::LookupResult
> RootResult
=
2720 VFS
->lookupPath("/");
2723 SmallVector
<StringRef
, 8> Components
;
2724 Components
.push_back("/");
2725 getVFSEntries(RootResult
->E
, Components
, CollectedEntries
);
2728 UniqueID
vfs::getNextVirtualUniqueID() {
2729 static std::atomic
<unsigned> UID
;
2730 unsigned ID
= ++UID
;
2731 // The following assumes that uint64_t max will never collide with a real
2732 // dev_t value from the OS.
2733 return UniqueID(std::numeric_limits
<uint64_t>::max(), ID
);
2736 void YAMLVFSWriter::addEntry(StringRef VirtualPath
, StringRef RealPath
,
2738 assert(sys::path::is_absolute(VirtualPath
) && "virtual path not absolute");
2739 assert(sys::path::is_absolute(RealPath
) && "real path not absolute");
2740 assert(!pathHasTraversal(VirtualPath
) && "path traversal is not supported");
2741 Mappings
.emplace_back(VirtualPath
, RealPath
, IsDirectory
);
2744 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath
, StringRef RealPath
) {
2745 addEntry(VirtualPath
, RealPath
, /*IsDirectory=*/false);
2748 void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath
,
2749 StringRef RealPath
) {
2750 addEntry(VirtualPath
, RealPath
, /*IsDirectory=*/true);
2756 llvm::raw_ostream
&OS
;
2757 SmallVector
<StringRef
, 16> DirStack
;
2759 unsigned getDirIndent() { return 4 * DirStack
.size(); }
2760 unsigned getFileIndent() { return 4 * (DirStack
.size() + 1); }
2761 bool containedIn(StringRef Parent
, StringRef Path
);
2762 StringRef
containedPart(StringRef Parent
, StringRef Path
);
2763 void startDirectory(StringRef Path
);
2764 void endDirectory();
2765 void writeEntry(StringRef VPath
, StringRef RPath
);
2768 JSONWriter(llvm::raw_ostream
&OS
) : OS(OS
) {}
2770 void write(ArrayRef
<YAMLVFSEntry
> Entries
,
2771 std::optional
<bool> UseExternalNames
,
2772 std::optional
<bool> IsCaseSensitive
,
2773 std::optional
<bool> IsOverlayRelative
, StringRef OverlayDir
);
2778 bool JSONWriter::containedIn(StringRef Parent
, StringRef Path
) {
2779 using namespace llvm::sys
;
2781 // Compare each path component.
2782 auto IParent
= path::begin(Parent
), EParent
= path::end(Parent
);
2783 for (auto IChild
= path::begin(Path
), EChild
= path::end(Path
);
2784 IParent
!= EParent
&& IChild
!= EChild
; ++IParent
, ++IChild
) {
2785 if (*IParent
!= *IChild
)
2788 // Have we exhausted the parent path?
2789 return IParent
== EParent
;
2792 StringRef
JSONWriter::containedPart(StringRef Parent
, StringRef Path
) {
2793 assert(!Parent
.empty());
2794 assert(containedIn(Parent
, Path
));
2795 return Path
.substr(Parent
.size() + 1);
2798 void JSONWriter::startDirectory(StringRef Path
) {
2800 DirStack
.empty() ? Path
: containedPart(DirStack
.back(), Path
);
2801 DirStack
.push_back(Path
);
2802 unsigned Indent
= getDirIndent();
2803 OS
.indent(Indent
) << "{\n";
2804 OS
.indent(Indent
+ 2) << "'type': 'directory',\n";
2805 OS
.indent(Indent
+ 2) << "'name': \"" << llvm::yaml::escape(Name
) << "\",\n";
2806 OS
.indent(Indent
+ 2) << "'contents': [\n";
2809 void JSONWriter::endDirectory() {
2810 unsigned Indent
= getDirIndent();
2811 OS
.indent(Indent
+ 2) << "]\n";
2812 OS
.indent(Indent
) << "}";
2814 DirStack
.pop_back();
2817 void JSONWriter::writeEntry(StringRef VPath
, StringRef RPath
) {
2818 unsigned Indent
= getFileIndent();
2819 OS
.indent(Indent
) << "{\n";
2820 OS
.indent(Indent
+ 2) << "'type': 'file',\n";
2821 OS
.indent(Indent
+ 2) << "'name': \"" << llvm::yaml::escape(VPath
) << "\",\n";
2822 OS
.indent(Indent
+ 2) << "'external-contents': \""
2823 << llvm::yaml::escape(RPath
) << "\"\n";
2824 OS
.indent(Indent
) << "}";
2827 void JSONWriter::write(ArrayRef
<YAMLVFSEntry
> Entries
,
2828 std::optional
<bool> UseExternalNames
,
2829 std::optional
<bool> IsCaseSensitive
,
2830 std::optional
<bool> IsOverlayRelative
,
2831 StringRef OverlayDir
) {
2832 using namespace llvm::sys
;
2836 if (IsCaseSensitive
)
2837 OS
<< " 'case-sensitive': '" << (*IsCaseSensitive
? "true" : "false")
2839 if (UseExternalNames
)
2840 OS
<< " 'use-external-names': '" << (*UseExternalNames
? "true" : "false")
2842 bool UseOverlayRelative
= false;
2843 if (IsOverlayRelative
) {
2844 UseOverlayRelative
= *IsOverlayRelative
;
2845 OS
<< " 'overlay-relative': '" << (UseOverlayRelative
? "true" : "false")
2848 OS
<< " 'roots': [\n";
2850 if (!Entries
.empty()) {
2851 const YAMLVFSEntry
&Entry
= Entries
.front();
2854 Entry
.IsDirectory
? Entry
.VPath
: path::parent_path(Entry
.VPath
)
2857 StringRef RPath
= Entry
.RPath
;
2858 if (UseOverlayRelative
) {
2859 assert(RPath
.starts_with(OverlayDir
) &&
2860 "Overlay dir must be contained in RPath");
2861 RPath
= RPath
.substr(OverlayDir
.size());
2864 bool IsCurrentDirEmpty
= true;
2865 if (!Entry
.IsDirectory
) {
2866 writeEntry(path::filename(Entry
.VPath
), RPath
);
2867 IsCurrentDirEmpty
= false;
2870 for (const auto &Entry
: Entries
.slice(1)) {
2872 Entry
.IsDirectory
? Entry
.VPath
: path::parent_path(Entry
.VPath
);
2873 if (Dir
== DirStack
.back()) {
2874 if (!IsCurrentDirEmpty
) {
2878 bool IsDirPoppedFromStack
= false;
2879 while (!DirStack
.empty() && !containedIn(DirStack
.back(), Dir
)) {
2882 IsDirPoppedFromStack
= true;
2884 if (IsDirPoppedFromStack
|| !IsCurrentDirEmpty
) {
2887 startDirectory(Dir
);
2888 IsCurrentDirEmpty
= true;
2890 StringRef RPath
= Entry
.RPath
;
2891 if (UseOverlayRelative
) {
2892 assert(RPath
.starts_with(OverlayDir
) &&
2893 "Overlay dir must be contained in RPath");
2894 RPath
= RPath
.substr(OverlayDir
.size());
2896 if (!Entry
.IsDirectory
) {
2897 writeEntry(path::filename(Entry
.VPath
), RPath
);
2898 IsCurrentDirEmpty
= false;
2902 while (!DirStack
.empty()) {
2913 void YAMLVFSWriter::write(llvm::raw_ostream
&OS
) {
2914 llvm::sort(Mappings
, [](const YAMLVFSEntry
&LHS
, const YAMLVFSEntry
&RHS
) {
2915 return LHS
.VPath
< RHS
.VPath
;
2918 JSONWriter(OS
).write(Mappings
, UseExternalNames
, IsCaseSensitive
,
2919 IsOverlayRelative
, OverlayDir
);
2922 vfs::recursive_directory_iterator::recursive_directory_iterator(
2923 FileSystem
&FS_
, const Twine
&Path
, std::error_code
&EC
)
2925 directory_iterator I
= FS
->dir_begin(Path
, EC
);
2926 if (I
!= directory_iterator()) {
2927 State
= std::make_shared
<detail::RecDirIterState
>();
2928 State
->Stack
.push_back(I
);
2932 vfs::recursive_directory_iterator
&
2933 recursive_directory_iterator::increment(std::error_code
&EC
) {
2934 assert(FS
&& State
&& !State
->Stack
.empty() && "incrementing past end");
2935 assert(!State
->Stack
.back()->path().empty() && "non-canonical end iterator");
2936 vfs::directory_iterator End
;
2938 if (State
->HasNoPushRequest
)
2939 State
->HasNoPushRequest
= false;
2941 if (State
->Stack
.back()->type() == sys::fs::file_type::directory_file
) {
2942 vfs::directory_iterator I
=
2943 FS
->dir_begin(State
->Stack
.back()->path(), EC
);
2945 State
->Stack
.push_back(I
);
2951 while (!State
->Stack
.empty() && State
->Stack
.back().increment(EC
) == End
)
2952 State
->Stack
.pop_back();
2954 if (State
->Stack
.empty())
2955 State
.reset(); // end iterator
2960 void TracingFileSystem::printImpl(raw_ostream
&OS
, PrintType Type
,
2961 unsigned IndentLevel
) const {
2962 printIndent(OS
, IndentLevel
);
2963 OS
<< "TracingFileSystem\n";
2964 if (Type
== PrintType::Summary
)
2967 printIndent(OS
, IndentLevel
);
2968 OS
<< "NumStatusCalls=" << NumStatusCalls
<< "\n";
2969 printIndent(OS
, IndentLevel
);
2970 OS
<< "NumOpenFileForReadCalls=" << NumOpenFileForReadCalls
<< "\n";
2971 printIndent(OS
, IndentLevel
);
2972 OS
<< "NumDirBeginCalls=" << NumDirBeginCalls
<< "\n";
2973 printIndent(OS
, IndentLevel
);
2974 OS
<< "NumGetRealPathCalls=" << NumGetRealPathCalls
<< "\n";
2975 printIndent(OS
, IndentLevel
);
2976 OS
<< "NumExistsCalls=" << NumExistsCalls
<< "\n";
2977 printIndent(OS
, IndentLevel
);
2978 OS
<< "NumIsLocalCalls=" << NumIsLocalCalls
<< "\n";
2980 if (Type
== PrintType::Contents
)
2981 Type
= PrintType::Summary
;
2982 getUnderlyingFS().print(OS
, Type
, IndentLevel
+ 1);
2985 const char FileSystem::ID
= 0;
2986 const char OverlayFileSystem::ID
= 0;
2987 const char ProxyFileSystem::ID
= 0;
2988 const char InMemoryFileSystem::ID
= 0;
2989 const char RedirectingFileSystem::ID
= 0;
2990 const char TracingFileSystem::ID
= 0;