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 @@ -671,20 +671,6 @@ enum EntryKind { EK_Directory, EK_DirectoryRemap, EK_File }; enum NameKind { NK_NotSet, NK_External, NK_Virtual }; - /// The type of redirection to perform. - enum class RedirectKind { - /// Lookup the redirected path first (ie. the one specified in - /// 'external-contents') and if that fails "fallthrough" to a lookup of the - /// originally provided path. - Fallthrough, - /// Lookup the provided path first and if that fails, "fallback" to a - /// lookup of the redirected path. - Fallback, - /// Only lookup the redirected path, do not lookup the originally provided - /// path. - RedirectOnly - }; - /// A single file or directory in the VFS. class Entry { EntryKind Kind; @@ -714,7 +700,7 @@ DirectoryEntry(StringRef Name, Status S) : Entry(EK_Directory, Name), S(std::move(S)) {} - Status getStatus() { return S; } + Status getStatus() const { return S; } void addContent(std::unique_ptr Content) { Contents.push_back(std::move(Content)); @@ -824,12 +810,6 @@ /// but canonicalize in order to perform the correct entry search. std::error_code makeCanonical(SmallVectorImpl &Path) const; - /// Get the File status, or error, from the underlying external file system. - /// This returns the status with the originally requested name, while looking - /// up the entry using the canonical path. - ErrorOr getExternalStatus(const Twine &CanonicalPath, - const Twine &OriginalPath) 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 @@ -850,11 +830,6 @@ /// The file system to use for external references. IntrusiveRefCntPtr ExternalFS; - /// If IsRelativeOverlay is set, this represents the directory - /// path that should be prefixed to each 'external-contents' entry - /// when reading from YAML files. - std::string ExternalContentsPrefixDir; - /// @name Configuration /// @{ @@ -863,17 +838,10 @@ /// Currently, case-insensitive matching only works correctly with ASCII. bool CaseSensitive = is_style_posix(sys::path::Style::native); - /// IsRelativeOverlay marks whether a ExternalContentsPrefixDir path must - /// be prefixed in every 'external-contents' when reading from YAML files. - bool IsRelativeOverlay = false; - /// Whether to use to use the value of 'external-contents' for the /// names of files. This global value is overridable on a per-file basis. bool UseExternalNames = true; - /// Determines the lookups to perform, as well as their order. See - /// \c RedirectKind for details. - RedirectKind Redirection = RedirectKind::Fallthrough; /// @} RedirectingFileSystem(IntrusiveRefCntPtr ExternalFS); @@ -886,15 +854,11 @@ llvm::sys::path::const_iterator End, Entry *From) const; - /// Get the status for a path with the provided \c LookupResult. - ErrorOr status(const Twine &CanonicalPath, const Twine &OriginalPath, - const LookupResult &Result); - public: /// Looks up \p Path in \c Roots and returns a LookupResult giving the /// matched entry and, if the entry was a FileEntry or DirectoryRemapEntry, /// the path it redirects to in the external file system. - ErrorOr lookupPath(StringRef Path) const; + ErrorOr lookupPath(StringRef CanonicalPath) const; /// Parses \p Buffer, which is expected to be in YAML format and /// returns a virtual file system representing its contents. @@ -909,6 +873,7 @@ bool UseExternalNames, FileSystem &ExternalFS); ErrorOr status(const Twine &Path) override; + ErrorOr> openFileForRead(const Twine &Path) override; std::error_code getRealPath(const Twine &Path, @@ -928,12 +893,6 @@ StringRef getExternalContentsPrefixDir() const; - /// Sets the redirection kind to \c Fallthrough if true or \c RedirectOnly - /// otherwise. Will removed in the future, use \c setRedirection instead. - void setFallthrough(bool Fallthrough); - - void setRedirection(RedirectingFileSystem::RedirectKind Kind); - std::vector getRoots() const; void dump(raw_ostream &OS) const override; 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 @@ -1104,8 +1104,6 @@ // RedirectingFileSystem implementation //===-----------------------------------------------------------------------===/ -namespace { - 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; @@ -1130,17 +1128,6 @@ return result; } -/// Whether the error and entry specify a file/directory that was not found. -static bool isFileNotFound(std::error_code EC, - RedirectingFileSystem::Entry *E = nullptr) { - if (E && !isa(E)) - return false; - return EC == llvm::errc::no_such_file_or_directory; -} - -} // anonymous namespace - - RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr FS) : ExternalFS(std::move(FS)) { if (ExternalFS) @@ -1243,14 +1230,15 @@ std::error_code RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) { - // Don't change the working directory if the path doesn't exist. - if (!exists(Path)) - return errc::no_such_file_or_directory; - SmallString<128> AbsolutePath; Path.toVector(AbsolutePath); if (std::error_code EC = makeAbsolute(AbsolutePath)) return EC; + + // Don't change the working directory if the path doesn't exist. + if (!exists(AbsolutePath)) + return errc::no_such_file_or_directory; + WorkingDirectory = std::string(AbsolutePath.str()); return {}; } @@ -1261,7 +1249,7 @@ Path_.toVector(Path); if (std::error_code EC = makeCanonical(Path)) - return {}; + return EC; return ExternalFS->isLocal(Path, Result); } @@ -1305,123 +1293,51 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir, std::error_code &EC) { - SmallString<256> Path; - Dir.toVector(Path); - - EC = makeCanonical(Path); + SmallString<256> CanonicalPath; + Dir.toVector(CanonicalPath); + EC = makeCanonical(CanonicalPath); if (EC) return {}; - ErrorOr Result = lookupPath(Path); + ErrorOr Result = + lookupPath(CanonicalPath); if (!Result) { - if (Redirection != RedirectKind::RedirectOnly && - isFileNotFound(Result.getError())) - return ExternalFS->dir_begin(Path, EC); - EC = Result.getError(); return {}; } - // Use status to make sure the path exists and refers to a directory. - ErrorOr S = status(Path, Dir, *Result); - if (!S) { - if (Redirection != RedirectKind::RedirectOnly && - isFileNotFound(S.getError(), Result->E)) - return ExternalFS->dir_begin(Dir, EC); + // Create the appropriate directory iterator based on whether we found a + // DirectoryRemapEntry or DirectoryEntry. + Optional ExtRedirect = Result->getExternalRedirect(); + if (!ExtRedirect) { + // DirectoryEntry - just make an iterator for it + auto DE = cast(Result->E); + return directory_iterator(std::make_shared( + CanonicalPath, DE->contents_begin(), DE->contents_end(), EC)); + } + // Have a DirectoryRemapEntry - need to check that the external directory + // actually exists and then possibly remap names to the virtual path depending + // on 'use-external-names'. + ErrorOr S = ExternalFS->status(*ExtRedirect); + if (!S) { EC = S.getError(); return {}; } - if (!S->isDirectory()) { EC = errc::not_a_directory; return {}; } - // Create the appropriate directory iterator based on whether we found a - // DirectoryRemapEntry or DirectoryEntry. - directory_iterator RedirectIter; - std::error_code RedirectEC; - if (auto ExtRedirect = Result->getExternalRedirect()) { - auto RE = cast(Result->E); - RedirectIter = ExternalFS->dir_begin(*ExtRedirect, RedirectEC); - - if (!RE->useExternalName(UseExternalNames)) { - // Update the paths in the results to use the virtual directory's path. - RedirectIter = - directory_iterator(std::make_shared( - std::string(Path), RedirectIter)); - } - } else { - auto DE = cast(Result->E); + auto RE = cast(Result->E); + directory_iterator RedirectIter = ExternalFS->dir_begin(*ExtRedirect, EC); + if (!RE->useExternalName(UseExternalNames)) { + // Update the paths in the results to use the virtual directory's path. RedirectIter = - directory_iterator(std::make_shared( - Path, DE->contents_begin(), DE->contents_end(), RedirectEC)); - } - - if (RedirectEC) { - if (RedirectEC != errc::no_such_file_or_directory) { - EC = RedirectEC; - return {}; - } - RedirectIter = {}; - } - - if (Redirection == RedirectKind::RedirectOnly) { - EC = RedirectEC; - return RedirectIter; - } - - std::error_code ExternalEC; - directory_iterator ExternalIter = ExternalFS->dir_begin(Path, ExternalEC); - if (ExternalEC) { - if (ExternalEC != errc::no_such_file_or_directory) { - EC = ExternalEC; - return {}; - } - ExternalIter = {}; - } - - SmallVector Iters; - switch (Redirection) { - case RedirectKind::Fallthrough: - Iters.push_back(ExternalIter); - Iters.push_back(RedirectIter); - break; - case RedirectKind::Fallback: - Iters.push_back(RedirectIter); - Iters.push_back(ExternalIter); - break; - default: - llvm_unreachable("unhandled RedirectKind"); - } - - directory_iterator Combined{ - std::make_shared(Iters, EC)}; - if (EC) - return {}; - return Combined; -} - -void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) { - ExternalContentsPrefixDir = PrefixDir.str(); -} - -StringRef RedirectingFileSystem::getExternalContentsPrefixDir() const { - return ExternalContentsPrefixDir; -} - -void RedirectingFileSystem::setFallthrough(bool Fallthrough) { - if (Fallthrough) { - Redirection = RedirectingFileSystem::RedirectKind::Fallthrough; - } else { - Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly; + directory_iterator(std::make_shared( + std::string(CanonicalPath), RedirectIter)); } -} - -void RedirectingFileSystem::setRedirection( - RedirectingFileSystem::RedirectKind Kind) { - Redirection = Kind; + return RedirectIter; } std::vector RedirectingFileSystem::getRoots() const { @@ -1466,6 +1382,28 @@ } } +namespace { + +struct YAMLParseResult { + /// The way in which this VFS should be added to an overlay. Note that the + /// order is right to left - given [A, B, C], any FS operations run on C + /// *first* and if failing, B, followed by A. + enum class RedirectKind { + /// Allow "falling through" to the next (ie. to the left) VFS. + Fallthrough, + /// Run any operations on the next VFS *first*, using this VFS as a + /// "fallback". + Fallback, + /// Do not perform any more operations on further VFS. + RedirectOnly + }; + + std::unique_ptr FS; + RedirectKind Redirection; +}; + +} // namespace + /// A helper class to hold the common YAML parsing state. class llvm::vfs::RedirectingFileSystemParser { yaml::Stream &Stream; @@ -1507,19 +1445,18 @@ return false; } - Optional - parseRedirectKind(yaml::Node *N) { + Optional parseRedirectKind(yaml::Node *N) { SmallString<12> Storage; StringRef Value; if (!parseScalarString(N, Value, Storage)) return None; if (Value.equals_insensitive("fallthrough")) { - return RedirectingFileSystem::RedirectKind::Fallthrough; + return YAMLParseResult::RedirectKind::Fallthrough; } else if (Value.equals_insensitive("fallback")) { - return RedirectingFileSystem::RedirectKind::Fallback; + return YAMLParseResult::RedirectKind::Fallback; } else if (Value.equals_insensitive("redirect-only")) { - return RedirectingFileSystem::RedirectKind::RedirectOnly; + return YAMLParseResult::RedirectKind::RedirectOnly; } return None; } @@ -1562,10 +1499,10 @@ public: static RedirectingFileSystem::Entry * - lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name, + lookupOrCreateEntry(RedirectingFileSystem &FS, StringRef Name, RedirectingFileSystem::Entry *ParentEntry = nullptr) { if (!ParentEntry) { // Look for a existent root - for (const auto &Root : FS->Roots) { + for (const auto &Root : FS.Roots) { if (Name.equals(Root->getName())) { ParentEntry = Root.get(); return ParentEntry; @@ -1590,8 +1527,8 @@ file_type::directory_file, sys::fs::all_all)); if (!ParentEntry) { // Add a new root to the overlay - FS->Roots.push_back(std::move(E)); - ParentEntry = FS->Roots.back().get(); + FS.Roots.push_back(std::move(E)); + ParentEntry = FS.Roots.back().get(); return ParentEntry; } @@ -1601,7 +1538,7 @@ } private: - void uniqueOverlayTree(RedirectingFileSystem *FS, + void uniqueOverlayTree(RedirectingFileSystem &FS, RedirectingFileSystem::Entry *SrcE, RedirectingFileSystem::Entry *NewParentE = nullptr) { StringRef Name = SrcE->getName(); @@ -1639,7 +1576,8 @@ } std::unique_ptr - parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) { + parseEntry(yaml::Node *N, bool IsRelativeOverlay, + StringRef ExternalContentsPrefixDir, bool IsRootEntry) { auto *M = dyn_cast(N); if (!M) { error(N, "expected mapping node for file or directory entry"); @@ -1714,7 +1652,8 @@ for (auto &I : *Contents) { if (std::unique_ptr E = - parseEntry(&I, FS, /*IsRootEntry*/ false)) + parseEntry(&I, IsRelativeOverlay, ExternalContentsPrefixDir, + /*IsRootEntry=*/false)) EntryArrayContents.push_back(std::move(E)); else return nullptr; @@ -1730,8 +1669,8 @@ return nullptr; SmallString<256> FullPath; - if (FS->IsRelativeOverlay) { - FullPath = FS->getExternalContentsPrefixDir(); + if (IsRelativeOverlay) { + FullPath.append(ExternalContentsPrefixDir); assert(!FullPath.empty() && "External contents prefix directory must exist"); llvm::sys::path::append(FullPath, Value); @@ -1853,12 +1792,45 @@ public: RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {} - // false on error - bool parse(yaml::Node *Root, RedirectingFileSystem *FS) { + static Optional + parse(MemoryBufferRef Buffer, SourceMgr &SM, + IntrusiveRefCntPtr ExternalFS) { + yaml::Stream Stream(Buffer, SM); + yaml::document_iterator DI = Stream.begin(); + yaml::Node *Root = DI->getRoot(); + if (DI == Stream.end() || !Root) { + SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node"); + return None; + } + + SmallString<256> ExternalContentsPrefixDir; + if (!Buffer.getBufferIdentifier().empty()) { + // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed + // to each 'external-contents' path. + // + // Example: + // -ivfsoverlay dummy.cache/vfs/vfs.yaml + // yields: + // FS->ExternalContentsPrefixDir => //dummy.cache/vfs + // + ExternalContentsPrefixDir = + sys::path::parent_path(Buffer.getBufferIdentifier()); + std::error_code EC = ExternalFS->makeAbsolute(ExternalContentsPrefixDir); + assert(!EC && "Overlay dir final path must be absolute"); + (void)EC; + } + + RedirectingFileSystemParser P(Stream); + return P.parse(Root, ExternalContentsPrefixDir, std::move(ExternalFS)); + } + + Optional parse(yaml::Node *Root, + StringRef ExternalContentsPrefixDir, + IntrusiveRefCntPtr ExternalFS) { auto *Top = dyn_cast(Root); if (!Top) { error(Root, "expected mapping node"); - return false; + return None; } KeyStatusPair Fields[] = { @@ -1874,85 +1846,91 @@ DenseMap Keys(std::begin(Fields), std::end(Fields)); std::vector> RootEntries; + YAMLParseResult Result{std::unique_ptr( + new RedirectingFileSystem(ExternalFS)), + YAMLParseResult::RedirectKind::Fallthrough}; + bool IsRelativeOverlay = false; + // Parse configuration and 'roots' for (auto &I : *Top) { SmallString<10> KeyBuffer; StringRef Key; if (!parseScalarString(I.getKey(), Key, KeyBuffer)) - return false; + return None; if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) - return false; + return None; if (Key == "roots") { auto *Roots = dyn_cast(I.getValue()); if (!Roots) { error(I.getValue(), "expected array"); - return false; + return None; } for (auto &I : *Roots) { if (std::unique_ptr E = - parseEntry(&I, FS, /*IsRootEntry*/ true)) + parseEntry(&I, IsRelativeOverlay, ExternalContentsPrefixDir, + /*IsRootEntry=*/true)) RootEntries.push_back(std::move(E)); else - return false; + return None; } } else if (Key == "version") { StringRef VersionString; SmallString<4> Storage; if (!parseScalarString(I.getValue(), VersionString, Storage)) - return false; + return None; int Version; if (VersionString.getAsInteger(10, Version)) { error(I.getValue(), "expected integer"); - return false; + return None; } if (Version < 0) { error(I.getValue(), "invalid version number"); - return false; + return None; } if (Version != 0) { error(I.getValue(), "version mismatch, expected 0"); - return false; + return None; } } else if (Key == "case-sensitive") { - if (!parseScalarBool(I.getValue(), FS->CaseSensitive)) - return false; + if (!parseScalarBool(I.getValue(), Result.FS->CaseSensitive)) + return None; } else if (Key == "overlay-relative") { - if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay)) - return false; + if (!parseScalarBool(I.getValue(), IsRelativeOverlay)) + return None; } else if (Key == "use-external-names") { - if (!parseScalarBool(I.getValue(), FS->UseExternalNames)) - return false; + if (!parseScalarBool(I.getValue(), Result.FS->UseExternalNames)) + return None; } else if (Key == "fallthrough") { if (Keys["redirecting-with"].Seen) { error(I.getValue(), "'fallthrough' and 'redirecting-with' are mutually exclusive"); - return false; + return None; } bool ShouldFallthrough = false; if (!parseScalarBool(I.getValue(), ShouldFallthrough)) - return false; + return None; if (ShouldFallthrough) { - FS->Redirection = RedirectingFileSystem::RedirectKind::Fallthrough; + Result.Redirection = YAMLParseResult::RedirectKind::Fallthrough; } else { - FS->Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly; + Result.Redirection = YAMLParseResult::RedirectKind::RedirectOnly; } } else if (Key == "redirecting-with") { if (Keys["fallthrough"].Seen) { error(I.getValue(), "'fallthrough' and 'redirecting-with' are mutually exclusive"); - return false; + return None; } if (auto Kind = parseRedirectKind(I.getValue())) { - FS->Redirection = *Kind; + Result.Redirection = *Kind; } else { error(I.getValue(), "expected valid redirect kind"); - return false; + return None; } } else { llvm_unreachable("key missing from Keys"); @@ -1960,18 +1938,18 @@ } if (Stream.failed()) - return false; + return None; if (!checkMissingKeys(Top, Keys)) - return false; + return None; // Now that we sucessefully parsed the YAML file, canonicalize the internal // representation to a proper directory tree so that we can search faster // inside the VFS. for (auto &E : RootEntries) - uniqueOverlayTree(FS, E.get()); + uniqueOverlayTree(*Result.FS, E.get()); - return true; + return Result; } }; @@ -1981,41 +1959,12 @@ StringRef YAMLFilePath, void *DiagContext, IntrusiveRefCntPtr ExternalFS) { SourceMgr SM; - yaml::Stream Stream(Buffer->getMemBufferRef(), SM); - SM.setDiagHandler(DiagHandler, DiagContext); - yaml::document_iterator DI = Stream.begin(); - yaml::Node *Root = DI->getRoot(); - if (DI == Stream.end() || !Root) { - SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node"); - return nullptr; - } - - RedirectingFileSystemParser P(Stream); - - std::unique_ptr FS( - new RedirectingFileSystem(ExternalFS)); - - if (!YAMLFilePath.empty()) { - // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed - // to each 'external-contents' path. - // - // Example: - // -ivfsoverlay dummy.cache/vfs/vfs.yaml - // yields: - // FS->ExternalContentsPrefixDir => //dummy.cache/vfs - // - SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath); - std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir); - assert(!EC && "Overlay dir final path must be absolute"); - (void)EC; - FS->setExternalContentsPrefixDir(OverlayAbsDir); - } - - if (!P.parse(Root, FS.get())) - return nullptr; - return FS; + if (Optional Result = RedirectingFileSystemParser::parse( + Buffer->getMemBufferRef(), SM, std::move(ExternalFS))) + return std::move(Result->FS); + return nullptr; } std::unique_ptr RedirectingFileSystem::create( @@ -2048,8 +1997,8 @@ for (auto I = llvm::sys::path::begin(FromDirectory), E = llvm::sys::path::end(FromDirectory); I != E; ++I) { - Parent = RedirectingFileSystemParser::lookupOrCreateEntry(FS.get(), *I, - Parent); + Parent = + RedirectingFileSystemParser::lookupOrCreateEntry(*FS, *I, Parent); } assert(Parent && "File without a directory?"); { @@ -2101,9 +2050,9 @@ } ErrorOr -RedirectingFileSystem::lookupPath(StringRef Path) const { - sys::path::const_iterator Start = sys::path::begin(Path); - sys::path::const_iterator End = sys::path::end(Path); +RedirectingFileSystem::lookupPath(StringRef CanonicalPath) const { + sys::path::const_iterator Start = sys::path::begin(CanonicalPath); + sys::path::const_iterator End = sys::path::end(CanonicalPath); for (const auto &Root : Roots) { ErrorOr Result = lookupPathImpl(Start, End, Root.get()); @@ -2154,82 +2103,37 @@ return make_error_code(llvm::errc::no_such_file_or_directory); } -static Status getRedirectedFileStatus(const Twine &OriginalPath, - bool UseExternalNames, - Status ExternalStatus) { - Status S = ExternalStatus; - if (!UseExternalNames) - S = Status::copyWithNewName(S, OriginalPath); - S.IsVFSMapped = true; - return S; -} - -ErrorOr RedirectingFileSystem::status( - const Twine &CanonicalPath, const Twine &OriginalPath, - const RedirectingFileSystem::LookupResult &Result) { - if (Optional ExtRedirect = Result.getExternalRedirect()) { - SmallString<256> CanonicalRemappedPath((*ExtRedirect).str()); - if (std::error_code EC = makeCanonical(CanonicalRemappedPath)) - return EC; - - ErrorOr S = ExternalFS->status(CanonicalRemappedPath); - if (!S) - return S; - S = Status::copyWithNewName(*S, *ExtRedirect); - auto *RE = cast(Result.E); - return getRedirectedFileStatus(OriginalPath, - RE->useExternalName(UseExternalNames), *S); - } - - auto *DE = cast(Result.E); - return Status::copyWithNewName(DE->getStatus(), CanonicalPath); -} - -ErrorOr -RedirectingFileSystem::getExternalStatus(const Twine &CanonicalPath, - const Twine &OriginalPath) const { - if (auto Result = ExternalFS->status(CanonicalPath)) { - return Result.get().copyWithNewName(Result.get(), OriginalPath); - } else { - return Result.getError(); - } -} - ErrorOr RedirectingFileSystem::status(const Twine &OriginalPath) { SmallString<256> CanonicalPath; OriginalPath.toVector(CanonicalPath); - if (std::error_code EC = makeCanonical(CanonicalPath)) return EC; - if (Redirection == RedirectKind::Fallback) { - // Attempt to find the original file first, only falling back to the - // mapped file if that fails. - ErrorOr S = getExternalStatus(CanonicalPath, OriginalPath); - if (S) - return S; - } - ErrorOr Result = lookupPath(CanonicalPath); - if (!Result) { - // Was not able to map file, fallthrough to using the original path if - // that was the specified redirection type. - if (Redirection == RedirectKind::Fallthrough && - isFileNotFound(Result.getError())) - return getExternalStatus(CanonicalPath, OriginalPath); + if (!Result) return Result.getError(); - } - ErrorOr S = status(CanonicalPath, OriginalPath, *Result); - if (!S && Redirection == RedirectKind::Fallthrough && - isFileNotFound(S.getError(), Result->E)) { - // Mapped the file but it wasn't found in the underlying filesystem, - // fallthrough to using the original path if that was the specified - // redirection type. - return getExternalStatus(CanonicalPath, OriginalPath); + Optional ExtRedirect = Result->getExternalRedirect(); + if (!ExtRedirect) { + auto *DE = cast(Result->E); + Status S = DE->getStatus(); + if (!UseExternalNames) + S = Status::copyWithNewName(S, OriginalPath); + else + S = Status::copyWithNewName(S, CanonicalPath); + S.IsVFSMapped = true; + return S; } + ErrorOr S = ExternalFS->status(*ExtRedirect); + if (!S) + return S; + + auto *RE = cast(Result->E); + if (!RE->useExternalName(UseExternalNames)) + S = Status::copyWithNewName(*S, OriginalPath); + S->IsVFSMapped = true; return S; } @@ -2245,8 +2149,8 @@ : InnerFile(std::move(InnerFile)), S(std::move(S)) {} ErrorOr status() override { return S; } - ErrorOr> + ErrorOr> getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) override { return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator, @@ -2276,65 +2180,32 @@ RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) { SmallString<256> CanonicalPath; OriginalPath.toVector(CanonicalPath); - if (std::error_code EC = makeCanonical(CanonicalPath)) return EC; - if (Redirection == RedirectKind::Fallback) { - // Attempt to find the original file first, only falling back to the - // mapped file if that fails. - auto F = File::getWithPath(ExternalFS->openFileForRead(CanonicalPath), - OriginalPath); - if (F) - return F; - } - ErrorOr Result = lookupPath(CanonicalPath); - if (!Result) { - // Was not able to map file, fallthrough to using the original path if - // that was the specified redirection type. - if (Redirection == RedirectKind::Fallthrough && - isFileNotFound(Result.getError())) - return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath), - OriginalPath); + if (!Result) return Result.getError(); - } - if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file? + Optional ExtRedirect = Result->getExternalRedirect(); + if (!ExtRedirect) return make_error_code(llvm::errc::invalid_argument); - StringRef ExtRedirect = *Result->getExternalRedirect(); - SmallString<256> CanonicalRemappedPath(ExtRedirect.str()); - if (std::error_code EC = makeCanonical(CanonicalRemappedPath)) - return EC; - auto *RE = cast(Result->E); - - auto ExternalFile = File::getWithPath( - ExternalFS->openFileForRead(CanonicalRemappedPath), ExtRedirect); - if (!ExternalFile) { - if (Redirection == RedirectKind::Fallthrough && - isFileNotFound(ExternalFile.getError(), Result->E)) { - // Mapped the file but it wasn't found in the underlying filesystem, - // fallthrough to using the original path if that was the specified - // redirection type. - return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath), - OriginalPath); - } + ErrorOr> ExternalFile = + ExternalFS->openFileForRead(*ExtRedirect); + if (!RE->useExternalName(UseExternalNames)) + ExternalFile = File::getWithPath(std::move(ExternalFile), OriginalPath); + if (!ExternalFile) return ExternalFile; - } - auto ExternalStatus = (*ExternalFile)->status(); + ErrorOr ExternalStatus = (*ExternalFile)->status(); if (!ExternalStatus) return ExternalStatus.getError(); - - // Otherwise, the file was successfully remapped. Mark it as such. Also - // replace the underlying path if the external name is being used. - Status S = getRedirectedFileStatus( - OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus); - return std::unique_ptr( - std::make_unique(std::move(*ExternalFile), S)); + ExternalStatus->IsVFSMapped = true; + return std::make_unique(std::move(*ExternalFile), + *ExternalStatus); } std::error_code @@ -2342,48 +2213,20 @@ SmallVectorImpl &Output) const { SmallString<256> CanonicalPath; OriginalPath.toVector(CanonicalPath); - if (std::error_code EC = makeCanonical(CanonicalPath)) return EC; - if (Redirection == RedirectKind::Fallback) { - // Attempt to find the original file first, only falling back to the - // mapped file if that fails. - std::error_code EC = ExternalFS->getRealPath(CanonicalPath, Output); - if (!EC) - return EC; - } - ErrorOr Result = lookupPath(CanonicalPath); - if (!Result) { - // Was not able to map file, fallthrough to using the original path if - // that was the specified redirection type. - if (Redirection == RedirectKind::Fallthrough && - isFileNotFound(Result.getError())) - return ExternalFS->getRealPath(CanonicalPath, Output); + if (!Result) return Result.getError(); - } - // 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 && Redirection == RedirectKind::Fallthrough && - isFileNotFound(P, Result->E)) { - // Mapped the file but it wasn't found in the underlying filesystem, - // fallthrough to using the original path if that was the specified - // redirection type. - return ExternalFS->getRealPath(CanonicalPath, Output); - } - return P; - } + if (Optional ExternalRedirect = Result->getExternalRedirect()) + return ExternalFS->getRealPath(*ExternalRedirect, Output); - // If we found a DirectoryEntry, still fallthrough to the original path if - // allowed, because directories don't have a single external contents path. - if (Redirection == RedirectKind::Fallthrough) - return ExternalFS->getRealPath(CanonicalPath, Output); - return llvm::errc::invalid_argument; + // Otherwise we have a DirectoryEntry, just use its path + Output.assign(CanonicalPath); + return {}; } std::unique_ptr diff --git a/llvm/unittests/Support/VirtualFileSystemTest.cpp b/llvm/unittests/Support/VirtualFileSystemTest.cpp --- a/llvm/unittests/Support/VirtualFileSystemTest.cpp +++ b/llvm/unittests/Support/VirtualFileSystemTest.cpp @@ -2997,3 +2997,51 @@ EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer()); EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer()); } + +// Check that a mapped directory and its parents all return a status with the +// correct path +TEST(RedirectingFileSystemTest, DirectoryStatus) { + auto Real = makeIntrusiveRefCnt(); + Real->addDirectory("/real"); + + SmallVector> Mappings = { + {"/a/b/c", "/real"}}; + auto FS = vfs::RedirectingFileSystem::create(Mappings, true, *Real); + ASSERT_TRUE(FS); + + auto S = FS->status("/a"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("/a", S->getName()); + + S = FS->status("/a/b"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("/a/b", S->getName()); + + S = FS->status("/a/b/c"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("/real", S->getName()); + + FS->setCurrentWorkingDirectory("/a"); + + S = FS->status("b"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("/a/b", S->getName()); + + S = FS->status("b/c"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("/real", S->getName()); + + FS = vfs::RedirectingFileSystem::create(Mappings, false, *Real); + ASSERT_TRUE(FS); + + // External names is false, use the passed in path instead + FS->setCurrentWorkingDirectory("/a"); + + S = FS->status("b"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("b", S->getName()); + + S = FS->status("b/c"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("b/c", S->getName()); +}