Skip to content

Commit

Permalink
[llvm][vfs] Avoid silent fallback to process-wide working directory
Browse files Browse the repository at this point in the history
In createPhysicalFileSystem, preserve the per-instance working
directory, even after the first call to getcwd fails.

rdar://108213753

Differential Revision: https://reviews.llvm.org/D149173
  • Loading branch information
benlangmuir committed May 2, 2023
1 parent 64888d4 commit 5437a4c
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 10 deletions.
22 changes: 12 additions & 10 deletions llvm/lib/Support/VirtualFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,12 @@ class RealFileSystem : public FileSystem {
explicit RealFileSystem(bool LinkCWDToProcess) {
if (!LinkCWDToProcess) {
SmallString<128> PWD, RealPWD;
if (llvm::sys::fs::current_path(PWD))
return; // Awful, but nothing to do here.
if (llvm::sys::fs::real_path(PWD, RealPWD))
WD = {PWD, PWD};
if (std::error_code EC = llvm::sys::fs::current_path(PWD))
WD = EC;
else if (llvm::sys::fs::real_path(PWD, RealPWD))
WD = WorkingDirectory{PWD, PWD};
else
WD = {PWD, RealPWD};
WD = WorkingDirectory{PWD, RealPWD};
}
}

Expand All @@ -284,10 +284,10 @@ class RealFileSystem : public FileSystem {
// If this FS has its own working dir, use it to make Path absolute.
// The returned twine is safe to use as long as both Storage and Path live.
Twine adjustPath(const Twine &Path, SmallVectorImpl<char> &Storage) const {
if (!WD)
if (!WD || !*WD)
return Path;
Path.toVector(Storage);
sys::fs::make_absolute(WD->Resolved, Storage);
sys::fs::make_absolute(WD->get().Resolved, Storage);
return Storage;
}

Expand All @@ -297,7 +297,7 @@ class RealFileSystem : public FileSystem {
// The current working directory, with links resolved. (readlink .).
SmallString<128> Resolved;
};
std::optional<WorkingDirectory> WD;
std::optional<llvm::ErrorOr<WorkingDirectory>> WD;
};

} // namespace
Expand All @@ -323,8 +323,10 @@ RealFileSystem::openFileForRead(const Twine &Name) {
}

llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
if (WD && *WD)
return std::string(WD->get().Specified.str());
if (WD)
return std::string(WD->Specified.str());
return WD->getError();

SmallString<128> Dir;
if (std::error_code EC = llvm::sys::fs::current_path(Dir))
Expand All @@ -345,7 +347,7 @@ std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
return std::make_error_code(std::errc::not_a_directory);
if (auto Err = llvm::sys::fs::real_path(Absolute, Resolved))
return Err;
WD = {Absolute, Resolved};
WD = WorkingDirectory{Absolute, Resolved};
return std::error_code();
}

Expand Down
26 changes: 26 additions & 0 deletions llvm/unittests/Support/VirtualFileSystemTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/MemoryBuffer.h"
Expand Down Expand Up @@ -526,6 +527,31 @@ TEST(VirtualFileSystemTest, MultipleWorkingDirs) {
ASSERT_EQ(CIt, vfs::directory_iterator());
}

TEST(VirtualFileSystemTest, PhysicalFileSystemWorkingDirFailure) {
TempDir D2("d2", /*Unique*/ true);
SmallString<128> WD, PrevWD;
ASSERT_EQ(sys::fs::current_path(PrevWD), std::error_code());
ASSERT_EQ(sys::fs::createUniqueDirectory("d1", WD), std::error_code());
ASSERT_EQ(sys::fs::set_current_path(WD), std::error_code());
auto Restore =
llvm::make_scope_exit([&] { sys::fs::set_current_path(PrevWD); });

// Delete the working directory to create an error.
ASSERT_EQ(sys::fs::remove_directories(WD), std::error_code());

// Verify that we still get two separate working directories.
auto FS1 = vfs::createPhysicalFileSystem();
auto FS2 = vfs::createPhysicalFileSystem();
ASSERT_EQ(FS1->getCurrentWorkingDirectory().getError(),
errc::no_such_file_or_directory);
ASSERT_EQ(FS1->setCurrentWorkingDirectory(D2.path()), std::error_code());
ASSERT_EQ(FS1->getCurrentWorkingDirectory().get(), D2.path());
EXPECT_EQ(FS2->getCurrentWorkingDirectory().getError(),
errc::no_such_file_or_directory);
SmallString<128> WD2;
EXPECT_EQ(sys::fs::current_path(WD2), errc::no_such_file_or_directory);
}

TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {
TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
Expand Down

0 comments on commit 5437a4c

Please sign in to comment.