aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Support/VirtualFileSystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Support/VirtualFileSystem.cpp')
-rw-r--r--llvm/lib/Support/VirtualFileSystem.cpp559
1 files changed, 317 insertions, 242 deletions
diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp
index 05332b00bd1f..15bb54e61817 100644
--- a/llvm/lib/Support/VirtualFileSystem.cpp
+++ b/llvm/lib/Support/VirtualFileSystem.cpp
@@ -452,19 +452,27 @@ llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;
namespace {
-class OverlayFSDirIterImpl : public llvm::vfs::detail::DirIterImpl {
- OverlayFileSystem &Overlays;
- std::string Path;
- OverlayFileSystem::iterator CurrentFS;
+/// Combines and deduplicates directory entries across multiple file systems.
+class CombiningDirIterImpl : public llvm::vfs::detail::DirIterImpl {
+ using FileSystemPtr = llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>;
+
+ /// File systems to check for entries in. Processed in reverse order.
+ SmallVector<FileSystemPtr, 8> FSList;
+ /// The directory iterator for the current filesystem.
directory_iterator CurrentDirIter;
+ /// The path of the directory to iterate the entries of.
+ std::string DirPath;
+ /// The set of names already returned as entries.
llvm::StringSet<> SeenNames;
+ /// Sets \c CurrentDirIter to an iterator of \c DirPath in the next file
+ /// system in the list, or leaves it as is (at its end position) if we've
+ /// already gone through them all.
std::error_code incrementFS() {
- assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
- ++CurrentFS;
- for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
+ while (!FSList.empty()) {
std::error_code EC;
- CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
+ CurrentDirIter = FSList.back()->dir_begin(DirPath, EC);
+ FSList.pop_back();
if (EC && EC != errc::no_such_file_or_directory)
return EC;
if (CurrentDirIter != directory_iterator())
@@ -500,11 +508,24 @@ class OverlayFSDirIterImpl : public llvm::vfs::detail::DirIterImpl {
}
public:
- OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
+ CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems, std::string Dir,
std::error_code &EC)
- : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
- CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
- EC = incrementImpl(true);
+ : FSList(FileSystems.begin(), FileSystems.end()),
+ DirPath(std::move(Dir)) {
+ if (!FSList.empty()) {
+ CurrentDirIter = FSList.back()->dir_begin(DirPath, EC);
+ FSList.pop_back();
+ if (!EC || EC == errc::no_such_file_or_directory)
+ EC = incrementImpl(true);
+ }
+ }
+
+ CombiningDirIterImpl(directory_iterator FirstIter, FileSystemPtr Fallback,
+ std::string FallbackDir, std::error_code &EC)
+ : FSList({Fallback}), CurrentDirIter(FirstIter),
+ DirPath(std::move(FallbackDir)) {
+ if (!EC || EC == errc::no_such_file_or_directory)
+ EC = incrementImpl(true);
}
std::error_code increment() override { return incrementImpl(false); }
@@ -515,7 +536,7 @@ public:
directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
std::error_code &EC) {
return directory_iterator(
- std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
+ std::make_shared<CombiningDirIterImpl>(FSList, Dir.str(), EC));
}
void ProxyFileSystem::anchor() {}
@@ -990,14 +1011,20 @@ std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
namespace {
-/// Removes leading "./" as well as path components like ".." and ".".
-static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {
- // First detect the path style in use by checking the first separator.
+static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) {
+ // Detect the path style in use by checking the first separator.
llvm::sys::path::Style style = llvm::sys::path::Style::native;
const size_t n = Path.find_first_of("/\\");
if (n != static_cast<size_t>(-1))
style = (Path[n] == '/') ? llvm::sys::path::Style::posix
: llvm::sys::path::Style::windows;
+ return style;
+}
+
+/// Removes leading "./" as well as path components like ".." and ".".
+static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {
+ // First detect the path style in use by checking the first separator.
+ llvm::sys::path::Style style = getExistingStyle(Path);
// Now remove the dots. Explicitly specifying the path style prevents the
// direction of the slashes from changing.
@@ -1019,48 +1046,88 @@ RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
}
}
-// FIXME: reuse implementation common with OverlayFSDirIterImpl as these
-// iterators are conceptually similar.
-class llvm::vfs::VFSFromYamlDirIterImpl
+/// Directory iterator implementation for \c RedirectingFileSystem's
+/// directory entries.
+class llvm::vfs::RedirectingFSDirIterImpl
: public llvm::vfs::detail::DirIterImpl {
std::string Dir;
- RedirectingFileSystem::RedirectingDirectoryEntry::iterator Current, End;
-
- // To handle 'fallthrough' mode we need to iterate at first through
- // RedirectingDirectoryEntry and then through ExternalFS. These operations are
- // done sequentially, we just need to keep a track of what kind of iteration
- // we are currently performing.
-
- /// Flag telling if we should iterate through ExternalFS or stop at the last
- /// RedirectingDirectoryEntry::iterator.
- bool IterateExternalFS;
- /// Flag telling if we have switched to iterating through ExternalFS.
- bool IsExternalFSCurrent = false;
- FileSystem &ExternalFS;
- directory_iterator ExternalDirIter;
- llvm::StringSet<> SeenNames;
+ RedirectingFileSystem::DirectoryEntry::iterator Current, End;
+
+ std::error_code incrementImpl(bool IsFirstTime) {
+ assert((IsFirstTime || Current != End) && "cannot iterate past end");
+ if (!IsFirstTime)
+ ++Current;
+ if (Current != End) {
+ SmallString<128> PathStr(Dir);
+ llvm::sys::path::append(PathStr, (*Current)->getName());
+ sys::fs::file_type Type = sys::fs::file_type::type_unknown;
+ switch ((*Current)->getKind()) {
+ case RedirectingFileSystem::EK_Directory:
+ LLVM_FALLTHROUGH;
+ case RedirectingFileSystem::EK_DirectoryRemap:
+ Type = sys::fs::file_type::directory_file;
+ break;
+ case RedirectingFileSystem::EK_File:
+ Type = sys::fs::file_type::regular_file;
+ break;
+ }
+ CurrentEntry = directory_entry(std::string(PathStr.str()), Type);
+ } else {
+ CurrentEntry = directory_entry();
+ }
+ return {};
+ };
+
+public:
+ RedirectingFSDirIterImpl(
+ const Twine &Path, RedirectingFileSystem::DirectoryEntry::iterator Begin,
+ RedirectingFileSystem::DirectoryEntry::iterator End, std::error_code &EC)
+ : Dir(Path.str()), Current(Begin), End(End) {
+ EC = incrementImpl(/*IsFirstTime=*/true);
+ }
- /// To combine multiple iterations, different methods are responsible for
- /// different iteration steps.
- /// @{
+ std::error_code increment() override {
+ return incrementImpl(/*IsFirstTime=*/false);
+ }
+};
- /// Responsible for dispatching between RedirectingDirectoryEntry iteration
- /// and ExternalFS iteration.
- std::error_code incrementImpl(bool IsFirstTime);
- /// Responsible for RedirectingDirectoryEntry iteration.
- std::error_code incrementContent(bool IsFirstTime);
- /// Responsible for ExternalFS iteration.
- std::error_code incrementExternal();
- /// @}
+/// Directory iterator implementation for \c RedirectingFileSystem's
+/// directory remap entries that maps the paths reported by the external
+/// file system's directory iterator back to the virtual directory's path.
+class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl {
+ std::string Dir;
+ llvm::sys::path::Style DirStyle;
+ llvm::vfs::directory_iterator ExternalIter;
public:
- VFSFromYamlDirIterImpl(
- const Twine &Path,
- RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin,
- RedirectingFileSystem::RedirectingDirectoryEntry::iterator End,
- bool IterateExternalFS, FileSystem &ExternalFS, std::error_code &EC);
+ RedirectingFSDirRemapIterImpl(std::string DirPath,
+ llvm::vfs::directory_iterator ExtIter)
+ : Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)),
+ ExternalIter(ExtIter) {
+ if (ExternalIter != llvm::vfs::directory_iterator())
+ setCurrentEntry();
+ }
+
+ void setCurrentEntry() {
+ StringRef ExternalPath = ExternalIter->path();
+ llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath);
+ StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle);
- std::error_code increment() override;
+ SmallString<128> NewPath(Dir);
+ llvm::sys::path::append(NewPath, DirStyle, File);
+
+ CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type());
+ }
+
+ std::error_code increment() override {
+ std::error_code EC;
+ ExternalIter.increment(EC);
+ if (!EC && ExternalIter != llvm::vfs::directory_iterator())
+ setCurrentEntry();
+ else
+ CurrentEntry = directory_entry();
+ return EC;
+ }
};
llvm::ErrorOr<std::string>
@@ -1131,15 +1198,19 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
if (EC)
return {};
- ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);
- if (!E) {
- EC = E.getError();
- if (shouldUseExternalFS() && EC == errc::no_such_file_or_directory)
+ ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
+ if (!Result) {
+ EC = Result.getError();
+ if (shouldFallBackToExternalFS(EC))
return ExternalFS->dir_begin(Path, EC);
return {};
}
- ErrorOr<Status> S = status(Path, *E);
+
+ // Use status to make sure the path exists and refers to a directory.
+ ErrorOr<Status> S = status(Path, *Result);
if (!S) {
+ if (shouldFallBackToExternalFS(S.getError(), Result->E))
+ return ExternalFS->dir_begin(Dir, EC);
EC = S.getError();
return {};
}
@@ -1149,10 +1220,29 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
return {};
}
- auto *D = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(*E);
- return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(
- Path, D->contents_begin(), D->contents_end(),
- /*IterateExternalFS=*/shouldUseExternalFS(), *ExternalFS, EC));
+ // Create the appropriate directory iterator based on whether we found a
+ // DirectoryRemapEntry or DirectoryEntry.
+ directory_iterator DirIter;
+ if (auto ExtRedirect = Result->getExternalRedirect()) {
+ auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
+ DirIter = ExternalFS->dir_begin(*ExtRedirect, EC);
+
+ if (!RE->useExternalName(UseExternalNames)) {
+ // Update the paths in the results to use the virtual directory's path.
+ DirIter =
+ directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>(
+ std::string(Path), DirIter));
+ }
+ } else {
+ auto DE = cast<DirectoryEntry>(Result->E);
+ DirIter = directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
+ Path, DE->contents_begin(), DE->contents_end(), EC));
+ }
+
+ if (!shouldUseExternalFS())
+ return DirIter;
+ return directory_iterator(std::make_shared<CombiningDirIterImpl>(
+ DirIter, ExternalFS, std::string(Path), EC));
}
void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) {
@@ -1189,7 +1279,7 @@ void RedirectingFileSystem::dumpEntry(raw_ostream &OS,
<< "\n";
if (E->getKind() == RedirectingFileSystem::EK_Directory) {
- auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(E);
+ auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(E);
assert(DE && "Should be a directory");
for (std::unique_ptr<Entry> &SubEntry :
@@ -1228,12 +1318,13 @@ class llvm::vfs::RedirectingFileSystemParser {
if (!parseScalarString(N, Value, Storage))
return false;
- if (Value.equals_lower("true") || Value.equals_lower("on") ||
- Value.equals_lower("yes") || Value == "1") {
+ if (Value.equals_insensitive("true") || Value.equals_insensitive("on") ||
+ Value.equals_insensitive("yes") || Value == "1") {
Result = true;
return true;
- } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
- Value.equals_lower("no") || Value == "0") {
+ } else if (Value.equals_insensitive("false") ||
+ Value.equals_insensitive("off") ||
+ Value.equals_insensitive("no") || Value == "0") {
Result = false;
return true;
}
@@ -1290,13 +1381,11 @@ public:
}
}
} else { // Advance to the next component
- auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(
- ParentEntry);
+ auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :
llvm::make_range(DE->contents_begin(), DE->contents_end())) {
auto *DirContent =
- dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(
- Content.get());
+ dyn_cast<RedirectingFileSystem::DirectoryEntry>(Content.get());
if (DirContent && Name.equals(Content->getName()))
return DirContent;
}
@@ -1304,7 +1393,7 @@ public:
// ... or create a new one
std::unique_ptr<RedirectingFileSystem::Entry> E =
- std::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>(
+ std::make_unique<RedirectingFileSystem::DirectoryEntry>(
Name, Status("", getNextVirtualUniqueID(),
std::chrono::system_clock::now(), 0, 0, 0,
file_type::directory_file, sys::fs::all_all));
@@ -1315,8 +1404,7 @@ public:
return ParentEntry;
}
- auto *DE =
- cast<RedirectingFileSystem::RedirectingDirectoryEntry>(ParentEntry);
+ auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
DE->addContent(std::move(E));
return DE->getLastContent();
}
@@ -1328,7 +1416,7 @@ private:
StringRef Name = SrcE->getName();
switch (SrcE->getKind()) {
case RedirectingFileSystem::EK_Directory: {
- auto *DE = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(SrcE);
+ auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
// Empty directories could be present in the YAML as a way to
// describe a file for a current directory after some of its subdir
// is parsed. This only leads to redundant walks, ignore it.
@@ -1339,14 +1427,21 @@ private:
uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
break;
}
- case RedirectingFileSystem::EK_File: {
+ case RedirectingFileSystem::EK_DirectoryRemap: {
assert(NewParentE && "Parent entry must exist");
- auto *FE = cast<RedirectingFileSystem::RedirectingFileEntry>(SrcE);
- auto *DE =
- cast<RedirectingFileSystem::RedirectingDirectoryEntry>(NewParentE);
+ auto *DR = cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
+ auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
DE->addContent(
- std::make_unique<RedirectingFileSystem::RedirectingFileEntry>(
- Name, FE->getExternalContentsPath(), FE->getUseName()));
+ std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
+ Name, DR->getExternalContentsPath(), DR->getUseName()));
+ break;
+ }
+ case RedirectingFileSystem::EK_File: {
+ assert(NewParentE && "Parent entry must exist");
+ auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);
+ auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
+ DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>(
+ Name, FE->getExternalContentsPath(), FE->getUseName()));
break;
}
}
@@ -1370,14 +1465,13 @@ private:
DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
- bool HasContents = false; // external or otherwise
+ enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;
std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
EntryArrayContents;
SmallString<256> ExternalContentsPath;
SmallString<256> Name;
yaml::Node *NameValueNode = nullptr;
- auto UseExternalName =
- RedirectingFileSystem::RedirectingFileEntry::NK_NotSet;
+ auto UseExternalName = RedirectingFileSystem::NK_NotSet;
RedirectingFileSystem::EntryKind Kind;
for (auto &I : *M) {
@@ -1407,17 +1501,19 @@ private:
Kind = RedirectingFileSystem::EK_File;
else if (Value == "directory")
Kind = RedirectingFileSystem::EK_Directory;
+ else if (Value == "directory-remap")
+ Kind = RedirectingFileSystem::EK_DirectoryRemap;
else {
error(I.getValue(), "unknown value for 'type'");
return nullptr;
}
} else if (Key == "contents") {
- if (HasContents) {
+ if (ContentsField != CF_NotSet) {
error(I.getKey(),
"entry already has 'contents' or 'external-contents'");
return nullptr;
}
- HasContents = true;
+ ContentsField = CF_List;
auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
if (!Contents) {
// FIXME: this is only for directories, what about files?
@@ -1433,12 +1529,12 @@ private:
return nullptr;
}
} else if (Key == "external-contents") {
- if (HasContents) {
+ if (ContentsField != CF_NotSet) {
error(I.getKey(),
"entry already has 'contents' or 'external-contents'");
return nullptr;
}
- HasContents = true;
+ ContentsField = CF_External;
if (!parseScalarString(I.getValue(), Value, Buffer))
return nullptr;
@@ -1460,9 +1556,8 @@ private:
bool Val;
if (!parseScalarBool(I.getValue(), Val))
return nullptr;
- UseExternalName =
- Val ? RedirectingFileSystem::RedirectingFileEntry::NK_External
- : RedirectingFileSystem::RedirectingFileEntry::NK_Virtual;
+ UseExternalName = Val ? RedirectingFileSystem::NK_External
+ : RedirectingFileSystem::NK_Virtual;
} else {
llvm_unreachable("key missing from Keys");
}
@@ -1472,7 +1567,7 @@ private:
return nullptr;
// check for missing keys
- if (!HasContents) {
+ if (ContentsField == CF_NotSet) {
error(N, "missing key 'contents' or 'external-contents'");
return nullptr;
}
@@ -1481,9 +1576,14 @@ private:
// check invalid configuration
if (Kind == RedirectingFileSystem::EK_Directory &&
- UseExternalName !=
- RedirectingFileSystem::RedirectingFileEntry::NK_NotSet) {
- error(N, "'use-external-name' is not supported for directories");
+ UseExternalName != RedirectingFileSystem::NK_NotSet) {
+ error(N, "'use-external-name' is not supported for 'directory' entries");
+ return nullptr;
+ }
+
+ if (Kind == RedirectingFileSystem::EK_DirectoryRemap &&
+ ContentsField == CF_List) {
+ error(N, "'contents' is not supported for 'directory-remap' entries");
return nullptr;
}
@@ -1504,7 +1604,7 @@ private:
}
// Remove trailing slash(es), being careful not to remove the root path
- StringRef Trimmed(Name);
+ StringRef Trimmed = Name;
size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();
while (Trimmed.size() > RootPathLen &&
sys::path::is_separator(Trimmed.back(), path_style))
@@ -1516,16 +1616,18 @@ private:
std::unique_ptr<RedirectingFileSystem::Entry> Result;
switch (Kind) {
case RedirectingFileSystem::EK_File:
- Result = std::make_unique<RedirectingFileSystem::RedirectingFileEntry>(
+ Result = std::make_unique<RedirectingFileSystem::FileEntry>(
+ LastComponent, std::move(ExternalContentsPath), UseExternalName);
+ break;
+ case RedirectingFileSystem::EK_DirectoryRemap:
+ Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
LastComponent, std::move(ExternalContentsPath), UseExternalName);
break;
case RedirectingFileSystem::EK_Directory:
- Result =
- std::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>(
- LastComponent, std::move(EntryArrayContents),
- Status("", getNextVirtualUniqueID(),
- std::chrono::system_clock::now(), 0, 0, 0,
- file_type::directory_file, sys::fs::all_all));
+ Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
+ LastComponent, std::move(EntryArrayContents),
+ Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
+ 0, 0, 0, file_type::directory_file, sys::fs::all_all));
break;
}
@@ -1539,12 +1641,10 @@ private:
I != E; ++I) {
std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;
Entries.push_back(std::move(Result));
- Result =
- std::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>(
- *I, std::move(Entries),
- Status("", getNextVirtualUniqueID(),
- std::chrono::system_clock::now(), 0, 0, 0,
- file_type::directory_file, sys::fs::all_all));
+ Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
+ *I, std::move(Entries),
+ Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
+ 0, 0, 0, file_type::directory_file, sys::fs::all_all));
}
return Result;
}
@@ -1731,20 +1831,40 @@ std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
}
// Add the file.
- auto NewFile =
- std::make_unique<RedirectingFileSystem::RedirectingFileEntry>(
- llvm::sys::path::filename(From), To,
- UseExternalNames
- ? RedirectingFileSystem::RedirectingFileEntry::NK_External
- : RedirectingFileSystem::RedirectingFileEntry::NK_Virtual);
+ auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
+ llvm::sys::path::filename(From), To,
+ UseExternalNames ? RedirectingFileSystem::NK_External
+ : RedirectingFileSystem::NK_Virtual);
ToEntry = NewFile.get();
- cast<RedirectingFileSystem::RedirectingDirectoryEntry>(Parent)->addContent(
+ cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(
std::move(NewFile));
}
return FS;
}
+RedirectingFileSystem::LookupResult::LookupResult(
+ Entry *E, sys::path::const_iterator Start, sys::path::const_iterator End)
+ : E(E) {
+ assert(E != nullptr);
+ // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
+ // path of the directory it maps to in the external file system plus any
+ // remaining path components in the provided iterator.
+ if (auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(E)) {
+ SmallString<256> Redirect(DRE->getExternalContentsPath());
+ sys::path::append(Redirect, Start, End,
+ getExistingStyle(DRE->getExternalContentsPath()));
+ ExternalRedirect = std::string(Redirect);
+ }
+}
+
+bool RedirectingFileSystem::shouldFallBackToExternalFS(
+ std::error_code EC, RedirectingFileSystem::Entry *E) const {
+ if (E && !isa<RedirectingFileSystem::DirectoryRemapEntry>(E))
+ return false;
+ return shouldUseExternalFS() && EC == llvm::errc::no_such_file_or_directory;
+}
+
std::error_code
RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const {
if (std::error_code EC = makeAbsolute(Path))
@@ -1759,23 +1879,23 @@ RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const {
return {};
}
-ErrorOr<RedirectingFileSystem::Entry *>
+ErrorOr<RedirectingFileSystem::LookupResult>
RedirectingFileSystem::lookupPath(StringRef Path) const {
sys::path::const_iterator Start = sys::path::begin(Path);
sys::path::const_iterator End = sys::path::end(Path);
for (const auto &Root : Roots) {
- ErrorOr<RedirectingFileSystem::Entry *> Result =
- lookupPath(Start, End, Root.get());
+ ErrorOr<RedirectingFileSystem::LookupResult> Result =
+ lookupPathImpl(Start, End, Root.get());
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
return Result;
}
return make_error_code(llvm::errc::no_such_file_or_directory);
}
-ErrorOr<RedirectingFileSystem::Entry *>
-RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
- sys::path::const_iterator End,
- RedirectingFileSystem::Entry *From) const {
+ErrorOr<RedirectingFileSystem::LookupResult>
+RedirectingFileSystem::lookupPathImpl(
+ sys::path::const_iterator Start, sys::path::const_iterator End,
+ RedirectingFileSystem::Entry *From) const {
assert(!isTraversalComponent(*Start) &&
!isTraversalComponent(From->getName()) &&
"Paths should not contain traversal components");
@@ -1791,18 +1911,21 @@ RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
if (Start == End) {
// Match!
- return From;
+ return LookupResult(From, Start, End);
}
}
- auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(From);
- if (!DE)
+ if (isa<RedirectingFileSystem::FileEntry>(From))
return make_error_code(llvm::errc::not_a_directory);
+ if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From))
+ return LookupResult(From, Start, End);
+
+ auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From);
for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
llvm::make_range(DE->contents_begin(), DE->contents_end())) {
- ErrorOr<RedirectingFileSystem::Entry *> Result =
- lookupPath(Start, End, DirEntry.get());
+ ErrorOr<RedirectingFileSystem::LookupResult> Result =
+ lookupPathImpl(Start, End, DirEntry.get());
if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
return Result;
}
@@ -1819,20 +1942,19 @@ static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
return S;
}
-ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path,
- RedirectingFileSystem::Entry *E) {
- assert(E != nullptr);
- if (auto *F = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(E)) {
- ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
- assert(!S || S->getName() == F->getExternalContentsPath());
- if (S)
- return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
- *S);
- return S;
- } else { // directory
- auto *DE = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(E);
- return Status::copyWithNewName(DE->getStatus(), Path);
+ErrorOr<Status> RedirectingFileSystem::status(
+ const Twine &Path, const RedirectingFileSystem::LookupResult &Result) {
+ if (Optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
+ ErrorOr<Status> S = ExternalFS->status(*ExtRedirect);
+ if (!S)
+ return S;
+ auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E);
+ return getRedirectedFileStatus(Path, RE->useExternalName(UseExternalNames),
+ *S);
}
+
+ auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
+ return Status::copyWithNewName(DE->getStatus(), Path);
}
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path_) {
@@ -1842,15 +1964,17 @@ ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path_) {
if (std::error_code EC = makeCanonical(Path))
return EC;
- ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
+ ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
if (!Result) {
- if (shouldUseExternalFS() &&
- Result.getError() == llvm::errc::no_such_file_or_directory) {
+ if (shouldFallBackToExternalFS(Result.getError()))
return ExternalFS->status(Path);
- }
return Result.getError();
}
- return status(Path, *Result);
+
+ ErrorOr<Status> S = status(Path, *Result);
+ if (!S && shouldFallBackToExternalFS(S.getError(), Result->E))
+ S = ExternalFS->status(Path);
+ return S;
}
namespace {
@@ -1886,32 +2010,35 @@ RedirectingFileSystem::openFileForRead(const Twine &Path_) {
if (std::error_code EC = makeCanonical(Path))
return EC;
- ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);
- if (!E) {
- if (shouldUseExternalFS() &&
- E.getError() == llvm::errc::no_such_file_or_directory) {
+ ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
+ if (!Result) {
+ if (shouldFallBackToExternalFS(Result.getError()))
return ExternalFS->openFileForRead(Path);
- }
- return E.getError();
+ return Result.getError();
}
- auto *F = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(*E);
- if (!F) // FIXME: errc::not_a_file?
+ if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
return make_error_code(llvm::errc::invalid_argument);
- auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
- if (!Result)
- return Result;
+ StringRef ExtRedirect = *Result->getExternalRedirect();
+ auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
+
+ auto ExternalFile = ExternalFS->openFileForRead(ExtRedirect);
+ if (!ExternalFile) {
+ if (shouldFallBackToExternalFS(ExternalFile.getError(), Result->E))
+ return ExternalFS->openFileForRead(Path);
+ return ExternalFile;
+ }
- auto ExternalStatus = (*Result)->status();
+ auto ExternalStatus = (*ExternalFile)->status();
if (!ExternalStatus)
return ExternalStatus.getError();
// FIXME: Update the status with the name and VFSMapped.
- Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
- *ExternalStatus);
+ Status S = getRedirectedFileStatus(
+ Path, RE->useExternalName(UseExternalNames), *ExternalStatus);
return std::unique_ptr<File>(
- std::make_unique<FileWithFixedStatus>(std::move(*Result), S));
+ std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
}
std::error_code
@@ -1923,20 +2050,24 @@ RedirectingFileSystem::getRealPath(const Twine &Path_,
if (std::error_code EC = makeCanonical(Path))
return EC;
- ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
+ ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
if (!Result) {
- if (shouldUseExternalFS() &&
- Result.getError() == llvm::errc::no_such_file_or_directory) {
+ if (shouldFallBackToExternalFS(Result.getError()))
return ExternalFS->getRealPath(Path, Output);
- }
return Result.getError();
}
- if (auto *F =
- dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(*Result)) {
- return ExternalFS->getRealPath(F->getExternalContentsPath(), Output);
+ // If we found FileEntry or DirectoryRemapEntry, look up the mapped
+ // path in the external file system.
+ if (auto ExtRedirect = Result->getExternalRedirect()) {
+ auto P = ExternalFS->getRealPath(*ExtRedirect, Output);
+ if (!P && shouldFallBackToExternalFS(P, Result->E)) {
+ return ExternalFS->getRealPath(Path, Output);
+ }
+ return P;
}
- // Even if there is a directory entry, fall back to ExternalFS if allowed,
+
+ // If we found a DirectoryEntry, still fall back to ExternalFS if allowed,
// because directories don't have a single external contents path.
return shouldUseExternalFS() ? ExternalFS->getRealPath(Path, Output)
: llvm::errc::invalid_argument;
@@ -1957,7 +2088,7 @@ static void getVFSEntries(RedirectingFileSystem::Entry *SrcE,
SmallVectorImpl<YAMLVFSEntry> &Entries) {
auto Kind = SrcE->getKind();
if (Kind == RedirectingFileSystem::EK_Directory) {
- auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(SrcE);
+ auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
assert(DE && "Must be a directory");
for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
llvm::make_range(DE->contents_begin(), DE->contents_end())) {
@@ -1968,8 +2099,19 @@ static void getVFSEntries(RedirectingFileSystem::Entry *SrcE,
return;
}
+ if (Kind == RedirectingFileSystem::EK_DirectoryRemap) {
+ auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
+ assert(DR && "Must be a directory remap");
+ SmallString<128> VPath;
+ for (auto &Comp : Path)
+ llvm::sys::path::append(VPath, Comp);
+ Entries.push_back(
+ YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
+ return;
+ }
+
assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
- auto *FE = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(SrcE);
+ auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
assert(FE && "Must be a file");
SmallString<128> VPath;
for (auto &Comp : Path)
@@ -1986,12 +2128,15 @@ void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create(
std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
std::move(ExternalFS));
- ErrorOr<RedirectingFileSystem::Entry *> RootE = VFS->lookupPath("/");
- if (!RootE)
+ if (!VFS)
+ return;
+ ErrorOr<RedirectingFileSystem::LookupResult> RootResult =
+ VFS->lookupPath("/");
+ if (!RootResult)
return;
SmallVector<StringRef, 8> Components;
Components.push_back("/");
- getVFSEntries(*RootE, Components, CollectedEntries);
+ getVFSEntries(RootResult->E, Components, CollectedEntries);
}
UniqueID vfs::getNextVirtualUniqueID() {
@@ -2189,76 +2334,6 @@ void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
IsOverlayRelative, OverlayDir);
}
-VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(
- const Twine &_Path,
- RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin,
- RedirectingFileSystem::RedirectingDirectoryEntry::iterator End,
- bool IterateExternalFS, FileSystem &ExternalFS, std::error_code &EC)
- : Dir(_Path.str()), Current(Begin), End(End),
- IterateExternalFS(IterateExternalFS), ExternalFS(ExternalFS) {
- EC = incrementImpl(/*IsFirstTime=*/true);
-}
-
-std::error_code VFSFromYamlDirIterImpl::increment() {
- return incrementImpl(/*IsFirstTime=*/false);
-}
-
-std::error_code VFSFromYamlDirIterImpl::incrementExternal() {
- assert(!(IsExternalFSCurrent && ExternalDirIter == directory_iterator()) &&
- "incrementing past end");
- std::error_code EC;
- if (IsExternalFSCurrent) {
- ExternalDirIter.increment(EC);
- } else if (IterateExternalFS) {
- ExternalDirIter = ExternalFS.dir_begin(Dir, EC);
- IsExternalFSCurrent = true;
- if (EC && EC != errc::no_such_file_or_directory)
- return EC;
- EC = {};
- }
- if (EC || ExternalDirIter == directory_iterator()) {
- CurrentEntry = directory_entry();
- } else {
- CurrentEntry = *ExternalDirIter;
- }
- return EC;
-}
-
-std::error_code VFSFromYamlDirIterImpl::incrementContent(bool IsFirstTime) {
- assert((IsFirstTime || Current != End) && "cannot iterate past end");
- if (!IsFirstTime)
- ++Current;
- while (Current != End) {
- SmallString<128> PathStr(Dir);
- llvm::sys::path::append(PathStr, (*Current)->getName());
- sys::fs::file_type Type = sys::fs::file_type::type_unknown;
- switch ((*Current)->getKind()) {
- case RedirectingFileSystem::EK_Directory:
- Type = sys::fs::file_type::directory_file;
- break;
- case RedirectingFileSystem::EK_File:
- Type = sys::fs::file_type::regular_file;
- break;
- }
- CurrentEntry = directory_entry(std::string(PathStr.str()), Type);
- return {};
- }
- return incrementExternal();
-}
-
-std::error_code VFSFromYamlDirIterImpl::incrementImpl(bool IsFirstTime) {
- while (true) {
- std::error_code EC = IsExternalFSCurrent ? incrementExternal()
- : incrementContent(IsFirstTime);
- if (EC || CurrentEntry.path().empty())
- return EC;
- StringRef Name = llvm::sys::path::filename(CurrentEntry.path());
- if (SeenNames.insert(Name).second)
- return EC; // name not seen before
- }
- llvm_unreachable("returned above");
-}
-
vfs::recursive_directory_iterator::recursive_directory_iterator(
FileSystem &FS_, const Twine &Path, std::error_code &EC)
: FS(&FS_) {