Skip to content

Commit 0be9ca7

Browse files
committed
[VFS] Fix inconsistencies between relative paths and fallthrough.
This patch addresses inconsistencies in the way fallthrough is handled in the RedirectingFileSystem. Rather than trying to change the working directory of the external filesystem, the RedirectingFileSystem will canonicalize every path before handing it down. This guarantees that relative paths are resolved relative to the RedirectingFileSystem's working directory. This allows us to have a strictly virtual working directory, and still fallthrough for absolute paths, but not for relative paths that would get resolved incorrectly at the lower layer (for example, in case of the RealFileSystem, because the strictly virtual path does not exist). Differential revision: https://reviews.llvm.org/D95188
1 parent 2bb92bf commit 0be9ca7

File tree

3 files changed

+92
-34
lines changed

3 files changed

+92
-34
lines changed

llvm/include/llvm/Support/VirtualFileSystem.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -651,9 +651,12 @@ class RedirectingFileSystem : public vfs::FileSystem {
651651
friend class VFSFromYamlDirIterImpl;
652652
friend class RedirectingFileSystemParser;
653653

654-
bool shouldUseExternalFS() const {
655-
return ExternalFSValidWD && IsFallthrough;
656-
}
654+
bool shouldUseExternalFS() const { return IsFallthrough; }
655+
656+
/// Canonicalize path by removing ".", "..", "./", components. This is
657+
/// a VFS request, do not bother about symlinks in the path components
658+
/// but canonicalize in order to perform the correct entry search.
659+
std::error_code makeCanonical(SmallVectorImpl<char> &Path) const;
657660

658661
// In a RedirectingFileSystem, keys can be specified in Posix or Windows
659662
// style (or even a mixture of both), so this comparison helper allows
@@ -672,9 +675,6 @@ class RedirectingFileSystem : public vfs::FileSystem {
672675
/// The current working directory of the file system.
673676
std::string WorkingDirectory;
674677

675-
/// Whether the current working directory is valid for the external FS.
676-
bool ExternalFSValidWD = false;
677-
678678
/// The file system to use for external references.
679679
IntrusiveRefCntPtr<FileSystem> ExternalFS;
680680

@@ -722,7 +722,7 @@ class RedirectingFileSystem : public vfs::FileSystem {
722722

723723
public:
724724
/// Looks up \p Path in \c Roots.
725-
ErrorOr<Entry *> lookupPath(const Twine &Path) const;
725+
ErrorOr<Entry *> lookupPath(StringRef Path) const;
726726

727727
/// Parses \p Buffer, which is expected to be in YAML format and
728728
/// returns a virtual file system representing its contents.

llvm/lib/Support/VirtualFileSystem.cpp

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,6 @@ RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
10161016
if (auto ExternalWorkingDirectory =
10171017
ExternalFS->getCurrentWorkingDirectory()) {
10181018
WorkingDirectory = *ExternalWorkingDirectory;
1019-
ExternalFSValidWD = true;
10201019
}
10211020
}
10221021

@@ -1075,12 +1074,6 @@ RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
10751074
if (!exists(Path))
10761075
return errc::no_such_file_or_directory;
10771076

1078-
// Always change the external FS but ignore its result.
1079-
if (ExternalFS) {
1080-
auto EC = ExternalFS->setCurrentWorkingDirectory(Path);
1081-
ExternalFSValidWD = !static_cast<bool>(EC);
1082-
}
1083-
10841077
SmallString<128> AbsolutePath;
10851078
Path.toVector(AbsolutePath);
10861079
if (std::error_code EC = makeAbsolute(AbsolutePath))
@@ -1089,8 +1082,14 @@ RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
10891082
return {};
10901083
}
10911084

1092-
std::error_code RedirectingFileSystem::isLocal(const Twine &Path,
1085+
std::error_code RedirectingFileSystem::isLocal(const Twine &Path_,
10931086
bool &Result) {
1087+
SmallString<256> Path;
1088+
Path_.toVector(Path);
1089+
1090+
if (std::error_code EC = makeCanonical(Path))
1091+
return {};
1092+
10941093
return ExternalFS->isLocal(Path, Result);
10951094
}
10961095

@@ -1125,14 +1124,21 @@ std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path)
11251124

11261125
directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
11271126
std::error_code &EC) {
1128-
ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Dir);
1127+
SmallString<256> Path;
1128+
Dir.toVector(Path);
1129+
1130+
EC = makeCanonical(Path);
1131+
if (EC)
1132+
return {};
1133+
1134+
ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);
11291135
if (!E) {
11301136
EC = E.getError();
11311137
if (shouldUseExternalFS() && EC == errc::no_such_file_or_directory)
1132-
return ExternalFS->dir_begin(Dir, EC);
1138+
return ExternalFS->dir_begin(Path, EC);
11331139
return {};
11341140
}
1135-
ErrorOr<Status> S = status(Dir, *E);
1141+
ErrorOr<Status> S = status(Path, *E);
11361142
if (!S) {
11371143
EC = S.getError();
11381144
return {};
@@ -1145,7 +1151,7 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
11451151

11461152
auto *D = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(*E);
11471153
return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(
1148-
Dir, D->contents_begin(), D->contents_end(),
1154+
Path, D->contents_begin(), D->contents_end(),
11491155
/*IterateExternalFS=*/shouldUseExternalFS(), *ExternalFS, EC));
11501156
}
11511157

@@ -1739,22 +1745,22 @@ std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
17391745
return FS;
17401746
}
17411747

1742-
ErrorOr<RedirectingFileSystem::Entry *>
1743-
RedirectingFileSystem::lookupPath(const Twine &Path_) const {
1744-
SmallString<256> Path;
1745-
Path_.toVector(Path);
1746-
1747-
// Handle relative paths
1748+
std::error_code
1749+
RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const {
17481750
if (std::error_code EC = makeAbsolute(Path))
17491751
return EC;
17501752

1751-
// Canonicalize path by removing ".", "..", "./", components. This is
1752-
// a VFS request, do not bother about symlinks in the path components
1753-
// but canonicalize in order to perform the correct entry search.
1754-
Path = canonicalize(Path);
1755-
if (Path.empty())
1753+
llvm::SmallString<256> CanonicalPath =
1754+
canonicalize(StringRef(Path.data(), Path.size()));
1755+
if (CanonicalPath.empty())
17561756
return make_error_code(llvm::errc::invalid_argument);
17571757

1758+
Path.assign(CanonicalPath.begin(), CanonicalPath.end());
1759+
return {};
1760+
}
1761+
1762+
ErrorOr<RedirectingFileSystem::Entry *>
1763+
RedirectingFileSystem::lookupPath(StringRef Path) const {
17581764
sys::path::const_iterator Start = sys::path::begin(Path);
17591765
sys::path::const_iterator End = sys::path::end(Path);
17601766
for (const auto &Root : Roots) {
@@ -1829,7 +1835,13 @@ ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path,
18291835
}
18301836
}
18311837

1832-
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
1838+
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path_) {
1839+
SmallString<256> Path;
1840+
Path_.toVector(Path);
1841+
1842+
if (std::error_code EC = makeCanonical(Path))
1843+
return EC;
1844+
18331845
ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
18341846
if (!Result) {
18351847
if (shouldUseExternalFS() &&
@@ -1867,7 +1879,13 @@ class FileWithFixedStatus : public File {
18671879
} // namespace
18681880

18691881
ErrorOr<std::unique_ptr<File>>
1870-
RedirectingFileSystem::openFileForRead(const Twine &Path) {
1882+
RedirectingFileSystem::openFileForRead(const Twine &Path_) {
1883+
SmallString<256> Path;
1884+
Path_.toVector(Path);
1885+
1886+
if (std::error_code EC = makeCanonical(Path))
1887+
return EC;
1888+
18711889
ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);
18721890
if (!E) {
18731891
if (shouldUseExternalFS() &&
@@ -1897,8 +1915,14 @@ RedirectingFileSystem::openFileForRead(const Twine &Path) {
18971915
}
18981916

18991917
std::error_code
1900-
RedirectingFileSystem::getRealPath(const Twine &Path,
1918+
RedirectingFileSystem::getRealPath(const Twine &Path_,
19011919
SmallVectorImpl<char> &Output) const {
1920+
SmallString<256> Path;
1921+
Path_.toVector(Path);
1922+
1923+
if (std::error_code EC = makeCanonical(Path))
1924+
return EC;
1925+
19021926
ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
19031927
if (!Result) {
19041928
if (shouldUseExternalFS() &&

llvm/unittests/Support/VirtualFileSystemTest.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2135,7 +2135,41 @@ TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) {
21352135
EXPECT_TRUE(Status->exists());
21362136

21372137
Status = FS->status("foo/a");
2138-
ASSERT_TRUE(Status.getError());
2138+
ASSERT_FALSE(Status.getError());
2139+
EXPECT_TRUE(Status->exists());
2140+
}
2141+
2142+
TEST_F(VFSFromYAMLTest, VirtualWorkingDirectory) {
2143+
IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2144+
Lower->addDirectory("//root/");
2145+
Lower->addDirectory("//root/foo");
2146+
Lower->addRegularFile("//root/foo/a");
2147+
Lower->addRegularFile("//root/foo/b");
2148+
Lower->addRegularFile("//root/c");
2149+
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2150+
"{ 'use-external-names': false,\n"
2151+
" 'roots': [\n"
2152+
"{\n"
2153+
" 'type': 'directory',\n"
2154+
" 'name': '//root/bar',\n"
2155+
" 'contents': [ {\n"
2156+
" 'type': 'file',\n"
2157+
" 'name': 'a',\n"
2158+
" 'external-contents': '//root/foo/a'\n"
2159+
" }\n"
2160+
" ]\n"
2161+
"}\n"
2162+
"]\n"
2163+
"}",
2164+
Lower);
2165+
ASSERT_TRUE(FS.get() != nullptr);
2166+
std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar");
2167+
ASSERT_FALSE(EC);
2168+
ASSERT_TRUE(FS.get() != nullptr);
2169+
2170+
llvm::ErrorOr<vfs::Status> Status = FS->status("a");
2171+
ASSERT_FALSE(Status.getError());
2172+
EXPECT_TRUE(Status->exists());
21392173
}
21402174

21412175
TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest) {

0 commit comments

Comments
 (0)