Skip to content

Commit 0b9981b

Browse files
committed
[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: 374917
1 parent d3bd5b3 commit 0b9981b

File tree

3 files changed

+217
-16
lines changed

3 files changed

+217
-16
lines changed

llvm/include/llvm/Support/VirtualFileSystem.h

+11-2
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

+35-10
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,16 @@ std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
989989
// RedirectingFileSystem implementation
990990
//===-----------------------------------------------------------------------===/
991991

992+
RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
993+
: ExternalFS(std::move(FS)) {
994+
if (ExternalFS)
995+
if (auto ExternalWorkingDirectory =
996+
ExternalFS->getCurrentWorkingDirectory()) {
997+
WorkingDirectory = *ExternalWorkingDirectory;
998+
ExternalFSValidWD = true;
999+
}
1000+
}
1001+
9921002
// FIXME: reuse implementation common with OverlayFSDirIterImpl as these
9931003
// iterators are conceptually similar.
9941004
class llvm::vfs::VFSFromYamlDirIterImpl
@@ -1035,12 +1045,27 @@ class llvm::vfs::VFSFromYamlDirIterImpl
10351045

10361046
llvm::ErrorOr<std::string>
10371047
RedirectingFileSystem::getCurrentWorkingDirectory() const {
1038-
return ExternalFS->getCurrentWorkingDirectory();
1048+
return WorkingDirectory;
10391049
}
10401050

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

10461071
std::error_code RedirectingFileSystem::isLocal(const Twine &Path,
@@ -1053,7 +1078,7 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
10531078
ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Dir);
10541079
if (!E) {
10551080
EC = E.getError();
1056-
if (IsFallthrough && EC == errc::no_such_file_or_directory)
1081+
if (shouldUseExternalFS() && EC == errc::no_such_file_or_directory)
10571082
return ExternalFS->dir_begin(Dir, EC);
10581083
return {};
10591084
}
@@ -1071,7 +1096,7 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
10711096
auto *D = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(*E);
10721097
return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(
10731098
Dir, D->contents_begin(), D->contents_end(),
1074-
/*IterateExternalFS=*/IsFallthrough, *ExternalFS, EC));
1099+
/*IterateExternalFS=*/shouldUseExternalFS(), *ExternalFS, EC));
10751100
}
10761101

10771102
void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) {
@@ -1572,7 +1597,7 @@ RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
15721597
RedirectingFileSystemParser P(Stream);
15731598

15741599
std::unique_ptr<RedirectingFileSystem> FS(
1575-
new RedirectingFileSystem(std::move(ExternalFS)));
1600+
new RedirectingFileSystem(ExternalFS));
15761601

15771602
if (!YAMLFilePath.empty()) {
15781603
// Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
@@ -1701,7 +1726,7 @@ ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path,
17011726
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
17021727
ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
17031728
if (!Result) {
1704-
if (IsFallthrough &&
1729+
if (shouldUseExternalFS() &&
17051730
Result.getError() == llvm::errc::no_such_file_or_directory) {
17061731
return ExternalFS->status(Path);
17071732
}
@@ -1739,7 +1764,7 @@ ErrorOr<std::unique_ptr<File>>
17391764
RedirectingFileSystem::openFileForRead(const Twine &Path) {
17401765
ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path);
17411766
if (!E) {
1742-
if (IsFallthrough &&
1767+
if (shouldUseExternalFS() &
17431768
E.getError() == llvm::errc::no_such_file_or_directory) {
17441769
return ExternalFS->openFileForRead(Path);
17451770
}
@@ -1770,7 +1795,7 @@ RedirectingFileSystem::getRealPath(const Twine &Path,
17701795
SmallVectorImpl<char> &Output) const {
17711796
ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path);
17721797
if (!Result) {
1773-
if (IsFallthrough &&
1798+
if (shouldUseExternalFS() &&
17741799
Result.getError() == llvm::errc::no_such_file_or_directory) {
17751800
return ExternalFS->getRealPath(Path, Output);
17761801
}
@@ -1783,8 +1808,8 @@ RedirectingFileSystem::getRealPath(const Twine &Path,
17831808
}
17841809
// Even if there is a directory entry, fall back to ExternalFS if allowed,
17851810
// because directories don't have a single external contents path.
1786-
return IsFallthrough ? ExternalFS->getRealPath(Path, Output)
1787-
: llvm::errc::invalid_argument;
1811+
return shouldUseExternalFS() ? ExternalFS->getRealPath(Path, Output)
1812+
: llvm::errc::invalid_argument;
17881813
}
17891814

17901815
IntrusiveRefCntPtr<FileSystem>

llvm/unittests/Support/VirtualFileSystemTest.cpp

+171-4
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/',\n"
2026+
" 'contents': [ {\n"
2027+
" 'type': 'file',\n"
2028+
" 'name': 'bar/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/',\n"
2085+
" 'contents': [ {\n"
2086+
" 'type': 'file',\n"
2087+
" 'name': 'bar/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/',\n"
2142+
" 'contents': [ {\n"
2143+
" 'type': 'file',\n"
2144+
" 'name': 'bar/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)