diff --git a/lldb/source/Host/common/FileSystem.cpp b/lldb/source/Host/common/FileSystem.cpp --- a/lldb/source/Host/common/FileSystem.cpp +++ b/lldb/source/Host/common/FileSystem.cpp @@ -487,7 +487,7 @@ return E.getError(); } - auto *F = dyn_cast(*E); + auto *F = dyn_cast(*E); if (!F) return make_error_code(llvm::errc::not_supported); diff --git a/llvm/include/llvm/Support/VirtualFileSystem.h b/llvm/include/llvm/Support/VirtualFileSystem.h --- a/llvm/include/llvm/Support/VirtualFileSystem.h +++ b/llvm/include/llvm/Support/VirtualFileSystem.h @@ -516,7 +516,7 @@ bool IsDirectory = false; }; -class VFSFromYamlDirIterImpl; +class RedirectingFSDirIterImpl; class RedirectingFileSystemParser; /// A virtual file system parsed from a YAML file. @@ -591,17 +591,19 @@ EntryKind getKind() const { return Kind; } }; - class RedirectingDirectoryEntry : public Entry { + class DirectoryEntry : public Entry { std::vector> Contents; Status S; public: - RedirectingDirectoryEntry(StringRef Name, - std::vector> Contents, - Status S) + /// Constructs a directory entry with explicitly specified contents. + DirectoryEntry(StringRef Name, std::vector> Contents, + Status S) : Entry(EK_Directory, Name), Contents(std::move(Contents)), S(std::move(S)) {} - RedirectingDirectoryEntry(StringRef Name, Status S) + + /// Constructs an empty directory entry. + DirectoryEntry(StringRef Name, Status S) : Entry(EK_Directory, Name), S(std::move(S)) {} Status getStatus() { return S; } @@ -620,7 +622,7 @@ static bool classof(const Entry *E) { return E->getKind() == EK_Directory; } }; - class RedirectingFileEntry : public Entry { + class FileEntry : public Entry { public: enum NameKind { NK_NotSet, NK_External, NK_Virtual }; @@ -629,8 +631,7 @@ NameKind UseName; public: - RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath, - NameKind UseName) + FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName) : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath), UseName(UseName) {} @@ -648,7 +649,7 @@ }; private: - friend class VFSFromYamlDirIterImpl; + friend class RedirectingFSDirIterImpl; friend class RedirectingFileSystemParser; bool shouldUseExternalFS() const { return IsFallthrough; } @@ -658,6 +659,10 @@ /// but canonicalize in order to perform the correct entry search. std::error_code makeCanonical(SmallVectorImpl &Path) const; + /// Whether to fall back to the external file system when an operation fails + /// with the given error code. + bool shouldFallBackToExternalFS(std::error_code EC) const; + // In a RedirectingFileSystem, keys can be specified in Posix or Windows // style (or even a mixture of both), so this comparison helper allows // slashes (representing a root) to match backslashes (and vice versa). Note diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -452,19 +452,27 @@ 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; + + /// File systems to check for entries in. Processed in reverse order. + SmallVector 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 @@ } public: - OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS, + CombiningDirIterImpl(ArrayRef 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 @@ directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir, std::error_code &EC) { return directory_iterator( - std::make_shared(Dir, *this, EC)); + std::make_shared(FSList, Dir.str(), EC)); } void ProxyFileSystem::anchor() {} @@ -1019,48 +1040,47 @@ } } -// 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; - - /// To combine multiple iterations, different methods are responsible for - /// different iteration steps. - /// @{ + RedirectingFileSystem::DirectoryEntry::iterator Current, End; - /// 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(); - /// @} + 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: + 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: - VFSFromYamlDirIterImpl( - const Twine &Path, - RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin, - RedirectingFileSystem::RedirectingDirectoryEntry::iterator End, - bool IterateExternalFS, FileSystem &ExternalFS, std::error_code &EC); + 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); + } - std::error_code increment() override; + std::error_code increment() override { + return incrementImpl(/*IsFirstTime=*/false); + } }; llvm::ErrorOr @@ -1134,7 +1154,7 @@ ErrorOr E = lookupPath(Path); if (!E) { EC = E.getError(); - if (shouldUseExternalFS() && EC == errc::no_such_file_or_directory) + if (shouldFallBackToExternalFS(EC)) return ExternalFS->dir_begin(Path, EC); return {}; } @@ -1149,10 +1169,14 @@ return {}; } - auto *D = cast(*E); - return directory_iterator(std::make_shared( - Path, D->contents_begin(), D->contents_end(), - /*IterateExternalFS=*/shouldUseExternalFS(), *ExternalFS, EC)); + auto *D = cast(*E); + auto DirIter = directory_iterator(std::make_shared( + Path, D->contents_begin(), D->contents_end(), EC)); + + if (!shouldUseExternalFS()) + return DirIter; + return directory_iterator(std::make_shared( + DirIter, ExternalFS, std::string(Path), EC)); } void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) { @@ -1189,7 +1213,7 @@ << "\n"; if (E->getKind() == RedirectingFileSystem::EK_Directory) { - auto *DE = dyn_cast(E); + auto *DE = dyn_cast(E); assert(DE && "Should be a directory"); for (std::unique_ptr &SubEntry : @@ -1290,13 +1314,11 @@ } } } else { // Advance to the next component - auto *DE = dyn_cast( - ParentEntry); + auto *DE = dyn_cast(ParentEntry); for (std::unique_ptr &Content : llvm::make_range(DE->contents_begin(), DE->contents_end())) { auto *DirContent = - dyn_cast( - Content.get()); + dyn_cast(Content.get()); if (DirContent && Name.equals(Content->getName())) return DirContent; } @@ -1304,7 +1326,7 @@ // ... or create a new one std::unique_ptr E = - std::make_unique( + std::make_unique( Name, Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(), 0, 0, 0, file_type::directory_file, sys::fs::all_all)); @@ -1315,8 +1337,7 @@ return ParentEntry; } - auto *DE = - cast(ParentEntry); + auto *DE = cast(ParentEntry); DE->addContent(std::move(E)); return DE->getLastContent(); } @@ -1328,7 +1349,7 @@ StringRef Name = SrcE->getName(); switch (SrcE->getKind()) { case RedirectingFileSystem::EK_Directory: { - auto *DE = cast(SrcE); + auto *DE = cast(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. @@ -1341,12 +1362,10 @@ } case RedirectingFileSystem::EK_File: { assert(NewParentE && "Parent entry must exist"); - auto *FE = cast(SrcE); - auto *DE = - cast(NewParentE); - DE->addContent( - std::make_unique( - Name, FE->getExternalContentsPath(), FE->getUseName())); + auto *FE = cast(SrcE); + auto *DE = cast(NewParentE); + DE->addContent(std::make_unique( + Name, FE->getExternalContentsPath(), FE->getUseName())); break; } } @@ -1376,8 +1395,7 @@ SmallString<256> ExternalContentsPath; SmallString<256> Name; yaml::Node *NameValueNode = nullptr; - auto UseExternalName = - RedirectingFileSystem::RedirectingFileEntry::NK_NotSet; + auto UseExternalName = RedirectingFileSystem::FileEntry::NK_NotSet; RedirectingFileSystem::EntryKind Kind; for (auto &I : *M) { @@ -1460,9 +1478,8 @@ bool Val; if (!parseScalarBool(I.getValue(), Val)) return nullptr; - UseExternalName = - Val ? RedirectingFileSystem::RedirectingFileEntry::NK_External - : RedirectingFileSystem::RedirectingFileEntry::NK_Virtual; + UseExternalName = Val ? RedirectingFileSystem::FileEntry::NK_External + : RedirectingFileSystem::FileEntry::NK_Virtual; } else { llvm_unreachable("key missing from Keys"); } @@ -1481,8 +1498,7 @@ // check invalid configuration if (Kind == RedirectingFileSystem::EK_Directory && - UseExternalName != - RedirectingFileSystem::RedirectingFileEntry::NK_NotSet) { + UseExternalName != RedirectingFileSystem::FileEntry::NK_NotSet) { error(N, "'use-external-name' is not supported for directories"); return nullptr; } @@ -1516,16 +1532,14 @@ std::unique_ptr Result; switch (Kind) { case RedirectingFileSystem::EK_File: - Result = std::make_unique( + Result = std::make_unique( LastComponent, std::move(ExternalContentsPath), UseExternalName); break; case RedirectingFileSystem::EK_Directory: - Result = - std::make_unique( - 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( + 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 +1553,10 @@ I != E; ++I) { std::vector> Entries; Entries.push_back(std::move(Result)); - Result = - std::make_unique( - *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( + *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 +1743,23 @@ } // Add the file. - auto NewFile = - std::make_unique( - llvm::sys::path::filename(From), To, - UseExternalNames - ? RedirectingFileSystem::RedirectingFileEntry::NK_External - : RedirectingFileSystem::RedirectingFileEntry::NK_Virtual); + auto NewFile = std::make_unique( + llvm::sys::path::filename(From), To, + UseExternalNames ? RedirectingFileSystem::FileEntry::NK_External + : RedirectingFileSystem::FileEntry::NK_Virtual); ToEntry = NewFile.get(); - cast(Parent)->addContent( + cast(Parent)->addContent( std::move(NewFile)); } return FS; } +bool RedirectingFileSystem::shouldFallBackToExternalFS( + std::error_code EC) const { + return shouldUseExternalFS() && EC == llvm::errc::no_such_file_or_directory; +}; + std::error_code RedirectingFileSystem::makeCanonical(SmallVectorImpl &Path) const { if (std::error_code EC = makeAbsolute(Path)) @@ -1795,7 +1810,7 @@ } } - auto *DE = dyn_cast(From); + auto *DE = dyn_cast(From); if (!DE) return make_error_code(llvm::errc::not_a_directory); @@ -1822,7 +1837,7 @@ ErrorOr RedirectingFileSystem::status(const Twine &Path, RedirectingFileSystem::Entry *E) { assert(E != nullptr); - if (auto *F = dyn_cast(E)) { + if (auto *F = dyn_cast(E)) { ErrorOr S = ExternalFS->status(F->getExternalContentsPath()); assert(!S || S->getName() == F->getExternalContentsPath()); if (S) @@ -1830,7 +1845,7 @@ *S); return S; } else { // directory - auto *DE = cast(E); + auto *DE = cast(E); return Status::copyWithNewName(DE->getStatus(), Path); } } @@ -1844,10 +1859,8 @@ ErrorOr 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); @@ -1888,14 +1901,12 @@ ErrorOr E = lookupPath(Path); if (!E) { - if (shouldUseExternalFS() && - E.getError() == llvm::errc::no_such_file_or_directory) { + if (shouldFallBackToExternalFS(E.getError())) return ExternalFS->openFileForRead(Path); - } return E.getError(); } - auto *F = dyn_cast(*E); + auto *F = dyn_cast(*E); if (!F) // FIXME: errc::not_a_file? return make_error_code(llvm::errc::invalid_argument); @@ -1925,15 +1936,12 @@ ErrorOr 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(*Result)) { + if (auto *F = dyn_cast(*Result)) { return ExternalFS->getRealPath(F->getExternalContentsPath(), Output); } // Even if there is a directory entry, fall back to ExternalFS if allowed, @@ -1957,7 +1965,7 @@ SmallVectorImpl &Entries) { auto Kind = SrcE->getKind(); if (Kind == RedirectingFileSystem::EK_Directory) { - auto *DE = dyn_cast(SrcE); + auto *DE = dyn_cast(SrcE); assert(DE && "Must be a directory"); for (std::unique_ptr &SubEntry : llvm::make_range(DE->contents_begin(), DE->contents_end())) { @@ -1969,7 +1977,7 @@ } assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File"); - auto *FE = dyn_cast(SrcE); + auto *FE = dyn_cast(SrcE); assert(FE && "Must be a file"); SmallString<128> VPath; for (auto &Comp : Path) @@ -2189,76 +2197,6 @@ 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_) {