Skip to content

Commit 2e58d66

Browse files
committed
[Reland][VirtualFileSystem] Support virtual working directory in the RedirectingFS
Before this patch, changing the working directory of the RedirectingFS would just forward to its external file system. This prevented us from having a working directory that only existed in the VFS mapping. This patch adds support for a virtual working directory in the RedirectingFileSystem. It now keeps track of its own WD in addition to updating the WD of the external file system. This ensures that we can still fall through for relative paths. This change was originally motivated by the reproducer infrastructure in LLDB where we want to deal transparently with relative paths. Differential revision: https://reviews.llvm.org/D65677 llvm-svn: 374955 (cherry picked from commit 2170354)
1 parent 6871462 commit 2e58d66

File tree

3 files changed

+217
-16
lines changed

3 files changed

+217
-16
lines changed

llvm/include/llvm/Support/VirtualFileSystem.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -647,9 +647,19 @@ class RedirectingFileSystem : public vfs::FileSystem {
647647
friend class VFSFromYamlDirIterImpl;
648648
friend class RedirectingFileSystemParser;
649649

650+
bool shouldUseExternalFS() const {
651+
return ExternalFSValidWD && IsFallthrough;
652+
}
653+
650654
/// The root(s) of the virtual file system.
651655
std::vector<std::unique_ptr<Entry>> Roots;
652656

657+
/// The current working directory of the file system.
658+
std::string WorkingDirectory;
659+
660+
/// Whether the current working directory is valid for the external FS.
661+
bool ExternalFSValidWD = false;
662+
653663
/// The file system to use for external references.
654664
IntrusiveRefCntPtr<FileSystem> ExternalFS;
655665

@@ -689,8 +699,7 @@ class RedirectingFileSystem : public vfs::FileSystem {
689699
true;
690700
#endif
691701

692-
RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)
693-
: ExternalFS(std::move(ExternalFS)) {}
702+
RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS);
694703

695704
/// Looks up the path <tt>[Start, End)</tt> in \p From, possibly
696705
/// recursing into the contents of \p From if it is a directory.

llvm/lib/Support/VirtualFileSystem.cpp

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,16 @@ std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
986986
// RedirectingFileSystem implementation
987987
//===-----------------------------------------------------------------------===/
988988

989+
RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
990+
: ExternalFS(std::move(FS)) {
991+
if (ExternalFS)
992+
if (auto ExternalWorkingDirectory =
993+
ExternalFS->getCurrentWorkingDirectory()) {
994+
WorkingDirectory = *ExternalWorkingDirectory;
995+
ExternalFSValidWD = true;
996+
}
997+
}
998+
989999
// FIXME: reuse implementation common with OverlayFSDirIterImpl as these
9901000
// iterators are conceptually similar.
9911001
class llvm::vfs::VFSFromYamlDirIterImpl
@@ -1032,12 +1042,27 @@ class llvm::vfs::VFSFromYamlDirIterImpl
10321042

10331043
llvm::ErrorOr<std::string>
10341044
RedirectingFileSystem::getCurrentWorkingDirectory() const {
1035-
return ExternalFS->getCurrentWorkingDirectory();
1045+
return WorkingDirectory;
10361046
}
10371047

10381048
std::error_code
10391049
RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
1040-
return ExternalFS->setCurrentWorkingDirectory(Path);
1050+
// Don't change the working directory if the path doesn't exist.
1051+
if (!exists(Path))
1052+
return errc::no_such_file_or_directory;
1053+
1054+
// Always change the external FS but ignore its result.
1055+
if (ExternalFS) {
1056+
auto EC = ExternalFS->setCurrentWorkingDirectory(Path);
1057+
ExternalFSValidWD = !static_cast<bool>(EC);
1058+
}
1059+
1060+
SmallString<128> AbsolutePath;
1061+
Path.toVector(AbsolutePath);
1062+
if (std::error_code EC = makeAbsolute(AbsolutePath))
1063+
return EC;
1064+
WorkingDirectory = AbsolutePath.str();
1065+
return {};
10411066
}
10421067

10431068
std::error_code RedirectingFileSystem::isLocal(const Twine &Path,
@@ -1050,7 +1075,7 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
10501075
ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Dir);
10511076
if (!E) {
10521077
EC = E.getError();
1053-
if (IsFallthrough && EC == errc::no_such_file_or_directory)
1078+
if (shouldUseExternalFS() && EC == errc::no_such_file_or_directory)
10541079
return ExternalFS->dir_begin(Dir, EC);
10551080
return {};
10561081
}
@@ -1068,7 +1093,7 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
10681093
auto *D = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(*E);
10691094
return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(
10701095
Dir, D->contents_begin(), D->contents_end(),
1071-
/*IterateExternalFS=*/IsFallthrough, *ExternalFS, EC));
1096+
/*IterateExternalFS=*/shouldUseExternalFS(), *ExternalFS, EC));
10721097
}
10731098

10741099
void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) {
@@ -1570,7 +1595,7 @@ RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
15701595
RedirectingFileSystemParser P(Stream);
15711596

15721597
std::unique_ptr<RedirectingFileSystem> FS(
1573-
new RedirectingFileSystem(std::move(ExternalFS)));
1598+
new RedirectingFileSystem(ExternalFS));
15741599

15751600
if (!YAMLFilePath.empty()) {
15761601
// Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
@@ -1699,7 +1724,7 @@ ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path,
16991724
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
17001725
ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
17011726
if (!Result) {
1702-
if (IsFallthrough &&
1727+
if (shouldUseExternalFS() &&
17031728
Result.getError() == llvm::errc::no_such_file_or_directory) {
17041729
return ExternalFS->status(Path);
17051730
}
@@ -1737,7 +1762,7 @@ ErrorOr<std::unique_ptr<File>>
17371762
RedirectingFileSystem::openFileForRead(const Twine &Path) {
17381763
ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);
17391764
if (!E) {
1740-
if (IsFallthrough &&
1765+
if (shouldUseExternalFS() &
17411766
E.getError() == llvm::errc::no_such_file_or_directory) {
17421767
return ExternalFS->openFileForRead(Path);
17431768
}
@@ -1768,7 +1793,7 @@ RedirectingFileSystem::getRealPath(const Twine &Path,
17681793
SmallVectorImpl<char> &Output) const {
17691794
ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
17701795
if (!Result) {
1771-
if (IsFallthrough &&
1796+
if (shouldUseExternalFS() &&
17721797
Result.getError() == llvm::errc::no_such_file_or_directory) {
17731798
return ExternalFS->getRealPath(Path, Output);
17741799
}
@@ -1781,8 +1806,8 @@ RedirectingFileSystem::getRealPath(const Twine &Path,
17811806
}
17821807
// Even if there is a directory entry, fall back to ExternalFS if allowed,
17831808
// because directories don't have a single external contents path.
1784-
return IsFallthrough ? ExternalFS->getRealPath(Path, Output)
1785-
: llvm::errc::invalid_argument;
1809+
return shouldUseExternalFS() ? ExternalFS->getRealPath(Path, Output)
1810+
: llvm::errc::invalid_argument;
17861811
}
17871812

17881813
IntrusiveRefCntPtr<FileSystem>

llvm/unittests/Support/VirtualFileSystemTest.cpp

Lines changed: 171 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ struct DummyFile : public vfs::File {
4141
class DummyFileSystem : public vfs::FileSystem {
4242
int FSID; // used to produce UniqueIDs
4343
int FileID; // used to produce UniqueIDs
44+
std::string WorkingDirectory;
4445
std::map<std::string, vfs::Status> FilesAndDirs;
46+
typedef std::map<std::string, vfs::Status>::const_iterator const_iterator;
4547

4648
static int getNextFSID() {
4749
static int Count = 0;
@@ -52,8 +54,7 @@ class DummyFileSystem : public vfs::FileSystem {
5254
DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
5355

5456
ErrorOr<vfs::Status> status(const Twine &Path) override {
55-
std::map<std::string, vfs::Status>::iterator I =
56-
FilesAndDirs.find(Path.str());
57+
auto I = findEntry(Path);
5758
if (I == FilesAndDirs.end())
5859
return make_error_code(llvm::errc::no_such_file_or_directory);
5960
return I->second;
@@ -66,15 +67,16 @@ class DummyFileSystem : public vfs::FileSystem {
6667
return S.getError();
6768
}
6869
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
69-
return std::string();
70+
return WorkingDirectory;
7071
}
7172
std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
73+
WorkingDirectory = Path.str();
7274
return std::error_code();
7375
}
7476
// Map any symlink to "/symlink".
7577
std::error_code getRealPath(const Twine &Path,
7678
SmallVectorImpl<char> &Output) const override {
77-
auto I = FilesAndDirs.find(Path.str());
79+
auto I = findEntry(Path);
7880
if (I == FilesAndDirs.end())
7981
return make_error_code(llvm::errc::no_such_file_or_directory);
8082
if (I->second.isSymlink()) {
@@ -136,6 +138,14 @@ class DummyFileSystem : public vfs::FileSystem {
136138
FilesAndDirs[Path] = Status;
137139
}
138140

141+
const_iterator findEntry(const Twine &Path) const {
142+
SmallString<128> P;
143+
Path.toVector(P);
144+
std::error_code EC = makeAbsolute(P);
145+
assert(!EC);
146+
return FilesAndDirs.find(P.str());
147+
}
148+
139149
void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
140150
vfs::Status S(Path, UniqueID(FSID, FileID++),
141151
std::chrono::system_clock::now(), 0, 0, 1024,
@@ -158,6 +168,12 @@ class DummyFileSystem : public vfs::FileSystem {
158168
}
159169
};
160170

171+
class ErrorDummyFileSystem : public DummyFileSystem {
172+
std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
173+
return llvm::errc::no_such_file_or_directory;
174+
}
175+
};
176+
161177
/// Replace back-slashes by front-slashes.
162178
std::string getPosixPath(std::string S) {
163179
SmallString<128> Result;
@@ -1994,3 +2010,154 @@ TEST_F(VFSFromYAMLTest, GetRealPath) {
19942010
EXPECT_EQ(FS->getRealPath("/non_existing", RealPath),
19952011
errc::no_such_file_or_directory);
19962012
}
2013+
2014+
TEST_F(VFSFromYAMLTest, WorkingDirectory) {
2015+
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2016+
Lower->addDirectory("//root/");
2017+
Lower->addDirectory("//root/foo");
2018+
Lower->addRegularFile("//root/foo/a");
2019+
Lower->addRegularFile("//root/foo/b");
2020+
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2021+
"{ 'use-external-names': false,\n"
2022+
" 'roots': [\n"
2023+
"{\n"
2024+
" 'type': 'directory',\n"
2025+
" 'name': '//root/bar',\n"
2026+
" 'contents': [ {\n"
2027+
" 'type': 'file',\n"
2028+
" 'name': 'a',\n"
2029+
" 'external-contents': '//root/foo/a'\n"
2030+
" }\n"
2031+
" ]\n"
2032+
"}\n"
2033+
"]\n"
2034+
"}",
2035+
Lower);
2036+
ASSERT_TRUE(FS.get() != nullptr);
2037+
std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar");
2038+
ASSERT_FALSE(EC);
2039+
2040+
llvm::ErrorOr<std::string> WorkingDir = FS->getCurrentWorkingDirectory();
2041+
ASSERT_TRUE(WorkingDir);
2042+
EXPECT_EQ(*WorkingDir, "//root/bar");
2043+
2044+
llvm::ErrorOr<vfs::Status> Status = FS->status("./a");
2045+
ASSERT_FALSE(Status.getError());
2046+
EXPECT_TRUE(Status->isStatusKnown());
2047+
EXPECT_FALSE(Status->isDirectory());
2048+
EXPECT_TRUE(Status->isRegularFile());
2049+
EXPECT_FALSE(Status->isSymlink());
2050+
EXPECT_FALSE(Status->isOther());
2051+
EXPECT_TRUE(Status->exists());
2052+
2053+
EC = FS->setCurrentWorkingDirectory("bogus");
2054+
ASSERT_TRUE(EC);
2055+
WorkingDir = FS->getCurrentWorkingDirectory();
2056+
ASSERT_TRUE(WorkingDir);
2057+
EXPECT_EQ(*WorkingDir, "//root/bar");
2058+
2059+
EC = FS->setCurrentWorkingDirectory("//root/");
2060+
ASSERT_FALSE(EC);
2061+
WorkingDir = FS->getCurrentWorkingDirectory();
2062+
ASSERT_TRUE(WorkingDir);
2063+
EXPECT_EQ(*WorkingDir, "//root/");
2064+
2065+
EC = FS->setCurrentWorkingDirectory("bar");
2066+
ASSERT_FALSE(EC);
2067+
WorkingDir = FS->getCurrentWorkingDirectory();
2068+
ASSERT_TRUE(WorkingDir);
2069+
EXPECT_EQ(*WorkingDir, "//root/bar");
2070+
}
2071+
2072+
TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthrough) {
2073+
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2074+
Lower->addDirectory("//root/");
2075+
Lower->addDirectory("//root/foo");
2076+
Lower->addRegularFile("//root/foo/a");
2077+
Lower->addRegularFile("//root/foo/b");
2078+
Lower->addRegularFile("//root/c");
2079+
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2080+
"{ 'use-external-names': false,\n"
2081+
" 'roots': [\n"
2082+
"{\n"
2083+
" 'type': 'directory',\n"
2084+
" 'name': '//root/bar',\n"
2085+
" 'contents': [ {\n"
2086+
" 'type': 'file',\n"
2087+
" 'name': 'a',\n"
2088+
" 'external-contents': '//root/foo/a'\n"
2089+
" }\n"
2090+
" ]\n"
2091+
"}\n"
2092+
"]\n"
2093+
"}",
2094+
Lower);
2095+
ASSERT_TRUE(FS.get() != nullptr);
2096+
std::error_code EC = FS->setCurrentWorkingDirectory("//root/");
2097+
ASSERT_FALSE(EC);
2098+
ASSERT_TRUE(FS.get() != nullptr);
2099+
2100+
llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a");
2101+
ASSERT_FALSE(Status.getError());
2102+
EXPECT_TRUE(Status->exists());
2103+
2104+
Status = FS->status("foo/a");
2105+
ASSERT_FALSE(Status.getError());
2106+
EXPECT_TRUE(Status->exists());
2107+
2108+
EC = FS->setCurrentWorkingDirectory("//root/bar");
2109+
ASSERT_FALSE(EC);
2110+
2111+
Status = FS->status("./a");
2112+
ASSERT_FALSE(Status.getError());
2113+
EXPECT_TRUE(Status->exists());
2114+
2115+
Status = FS->status("./b");
2116+
ASSERT_TRUE(Status.getError());
2117+
2118+
Status = FS->status("./c");
2119+
ASSERT_TRUE(Status.getError());
2120+
2121+
EC = FS->setCurrentWorkingDirectory("//root/");
2122+
ASSERT_FALSE(EC);
2123+
2124+
Status = FS->status("c");
2125+
ASSERT_FALSE(Status.getError());
2126+
EXPECT_TRUE(Status->exists());
2127+
}
2128+
2129+
TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) {
2130+
IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2131+
Lower->addDirectory("//root/");
2132+
Lower->addDirectory("//root/foo");
2133+
Lower->addRegularFile("//root/foo/a");
2134+
Lower->addRegularFile("//root/foo/b");
2135+
Lower->addRegularFile("//root/c");
2136+
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2137+
"{ 'use-external-names': false,\n"
2138+
" 'roots': [\n"
2139+
"{\n"
2140+
" 'type': 'directory',\n"
2141+
" 'name': '//root/bar',\n"
2142+
" 'contents': [ {\n"
2143+
" 'type': 'file',\n"
2144+
" 'name': 'a',\n"
2145+
" 'external-contents': '//root/foo/a'\n"
2146+
" }\n"
2147+
" ]\n"
2148+
"}\n"
2149+
"]\n"
2150+
"}",
2151+
Lower);
2152+
ASSERT_TRUE(FS.get() != nullptr);
2153+
std::error_code EC = FS->setCurrentWorkingDirectory("//root/");
2154+
ASSERT_FALSE(EC);
2155+
ASSERT_TRUE(FS.get() != nullptr);
2156+
2157+
llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a");
2158+
ASSERT_FALSE(Status.getError());
2159+
EXPECT_TRUE(Status->exists());
2160+
2161+
Status = FS->status("foo/a");
2162+
ASSERT_TRUE(Status.getError());
2163+
}

0 commit comments

Comments
 (0)