aboutsummaryrefslogtreecommitdiff
path: root/lib/Support/VirtualFileSystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Support/VirtualFileSystem.cpp')
-rw-r--r--lib/Support/VirtualFileSystem.cpp159
1 files changed, 102 insertions, 57 deletions
diff --git a/lib/Support/VirtualFileSystem.cpp b/lib/Support/VirtualFileSystem.cpp
index f2a8a1bb27af..5d3480e97148 100644
--- a/lib/Support/VirtualFileSystem.cpp
+++ b/lib/Support/VirtualFileSystem.cpp
@@ -1,9 +1,8 @@
//===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -57,8 +56,10 @@
using namespace llvm;
using namespace llvm::vfs;
+using llvm::sys::fs::file_t;
using llvm::sys::fs::file_status;
using llvm::sys::fs::file_type;
+using llvm::sys::fs::kInvalidFile;
using llvm::sys::fs::perms;
using llvm::sys::fs::UniqueID;
@@ -67,19 +68,19 @@ Status::Status(const file_status &Status)
User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
Type(Status.type()), Perms(Status.permissions()) {}
-Status::Status(StringRef Name, UniqueID UID, sys::TimePoint<> MTime,
+Status::Status(const Twine &Name, UniqueID UID, sys::TimePoint<> MTime,
uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
perms Perms)
- : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
- Type(Type), Perms(Perms) {}
+ : Name(Name.str()), UID(UID), MTime(MTime), User(User), Group(Group),
+ Size(Size), Type(Type), Perms(Perms) {}
-Status Status::copyWithNewName(const Status &In, StringRef NewName) {
+Status Status::copyWithNewName(const Status &In, const Twine &NewName) {
return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
In.getUser(), In.getGroup(), In.getSize(), In.getType(),
In.getPermissions());
}
-Status Status::copyWithNewName(const file_status &In, StringRef NewName) {
+Status Status::copyWithNewName(const file_status &In, const Twine &NewName) {
return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
In.getUser(), In.getGroup(), In.getSize(), In.type(),
In.permissions());
@@ -171,15 +172,15 @@ namespace {
class RealFile : public File {
friend class RealFileSystem;
- int FD;
+ file_t FD;
Status S;
std::string RealName;
- RealFile(int FD, StringRef NewName, StringRef NewRealPathName)
+ RealFile(file_t FD, StringRef NewName, StringRef NewRealPathName)
: FD(FD), S(NewName, {}, {}, {}, {}, {},
llvm::sys::fs::file_type::status_error, {}),
RealName(NewRealPathName.str()) {
- assert(FD >= 0 && "Invalid or inactive file descriptor");
+ assert(FD != kInvalidFile && "Invalid or inactive file descriptor");
}
public:
@@ -199,7 +200,7 @@ public:
RealFile::~RealFile() { close(); }
ErrorOr<Status> RealFile::status() {
- assert(FD != -1 && "cannot stat closed file");
+ assert(FD != kInvalidFile && "cannot stat closed file");
if (!S.isStatusKnown()) {
file_status RealStatus;
if (std::error_code EC = sys::fs::status(FD, RealStatus))
@@ -216,22 +217,41 @@ ErrorOr<std::string> RealFile::getName() {
ErrorOr<std::unique_ptr<MemoryBuffer>>
RealFile::getBuffer(const Twine &Name, int64_t FileSize,
bool RequiresNullTerminator, bool IsVolatile) {
- assert(FD != -1 && "cannot get buffer for closed file");
+ assert(FD != kInvalidFile && "cannot get buffer for closed file");
return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
IsVolatile);
}
std::error_code RealFile::close() {
- std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD);
- FD = -1;
+ std::error_code EC = sys::fs::closeFile(FD);
+ FD = kInvalidFile;
return EC;
}
namespace {
-/// The file system according to your operating system.
+/// A file system according to your operating system.
+/// This may be linked to the process's working directory, or maintain its own.
+///
+/// Currently, its own working directory is emulated by storing the path and
+/// sending absolute paths to llvm::sys::fs:: functions.
+/// A more principled approach would be to push this down a level, modelling
+/// the working dir as an llvm::sys::fs::WorkingDir or similar.
+/// This would enable the use of openat()-style functions on some platforms.
class RealFileSystem : public FileSystem {
public:
+ 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};
+ else
+ WD = {PWD, RealPWD};
+ }
+ }
+
ErrorOr<Status> status(const Twine &Path) override;
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
@@ -243,72 +263,95 @@ public:
SmallVectorImpl<char> &Output) const override;
private:
- mutable std::mutex CWDMutex;
- mutable std::string CWDCache;
+ // 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)
+ return Path;
+ Path.toVector(Storage);
+ sys::fs::make_absolute(WD->Resolved, Storage);
+ return Storage;
+ }
+
+ struct WorkingDirectory {
+ // The current working directory, without symlinks resolved. (echo $PWD).
+ SmallString<128> Specified;
+ // The current working directory, with links resolved. (readlink .).
+ SmallString<128> Resolved;
+ };
+ Optional<WorkingDirectory> WD;
};
} // namespace
ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
+ SmallString<256> Storage;
sys::fs::file_status RealStatus;
- if (std::error_code EC = sys::fs::status(Path, RealStatus))
+ if (std::error_code EC =
+ sys::fs::status(adjustPath(Path, Storage), RealStatus))
return EC;
- return Status::copyWithNewName(RealStatus, Path.str());
+ return Status::copyWithNewName(RealStatus, Path);
}
ErrorOr<std::unique_ptr<File>>
RealFileSystem::openFileForRead(const Twine &Name) {
- int FD;
- SmallString<256> RealName;
- if (std::error_code EC =
- sys::fs::openFileForRead(Name, FD, sys::fs::OF_None, &RealName))
- return EC;
- return std::unique_ptr<File>(new RealFile(FD, Name.str(), RealName.str()));
+ SmallString<256> RealName, Storage;
+ Expected<file_t> FDOrErr = sys::fs::openNativeFileForRead(
+ adjustPath(Name, Storage), sys::fs::OF_None, &RealName);
+ if (!FDOrErr)
+ return errorToErrorCode(FDOrErr.takeError());
+ return std::unique_ptr<File>(
+ new RealFile(*FDOrErr, Name.str(), RealName.str()));
}
llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
- std::lock_guard<std::mutex> Lock(CWDMutex);
- if (!CWDCache.empty())
- return CWDCache;
- SmallString<256> Dir;
+ if (WD)
+ return WD->Specified.str();
+
+ SmallString<128> Dir;
if (std::error_code EC = llvm::sys::fs::current_path(Dir))
return EC;
- CWDCache = Dir.str();
- return CWDCache;
+ return Dir.str();
}
std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
- // FIXME: chdir is thread hostile; on the other hand, creating the same
- // behavior as chdir is complex: chdir resolves the path once, thus
- // guaranteeing that all subsequent relative path operations work
- // on the same path the original chdir resulted in. This makes a
- // difference for example on network filesystems, where symlinks might be
- // switched during runtime of the tool. Fixing this depends on having a
- // file system abstraction that allows openat() style interactions.
- if (auto EC = llvm::sys::fs::set_current_path(Path))
- return EC;
-
- // Invalidate cache.
- std::lock_guard<std::mutex> Lock(CWDMutex);
- CWDCache.clear();
+ if (!WD)
+ return llvm::sys::fs::set_current_path(Path);
+
+ SmallString<128> Absolute, Resolved, Storage;
+ adjustPath(Path, Storage).toVector(Absolute);
+ bool IsDir;
+ if (auto Err = llvm::sys::fs::is_directory(Absolute, IsDir))
+ return Err;
+ if (!IsDir)
+ 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};
return std::error_code();
}
std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) {
- return llvm::sys::fs::is_local(Path, Result);
+ SmallString<256> Storage;
+ return llvm::sys::fs::is_local(adjustPath(Path, Storage), Result);
}
std::error_code
RealFileSystem::getRealPath(const Twine &Path,
SmallVectorImpl<char> &Output) const {
- return llvm::sys::fs::real_path(Path, Output);
+ SmallString<256> Storage;
+ return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output);
}
IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
- static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
+ static IntrusiveRefCntPtr<FileSystem> FS(new RealFileSystem(true));
return FS;
}
+std::unique_ptr<FileSystem> vfs::createPhysicalFileSystem() {
+ return llvm::make_unique<RealFileSystem>(false);
+}
+
namespace {
class RealFSDirIter : public llvm::vfs::detail::DirIterImpl {
@@ -334,7 +377,9 @@ public:
directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
std::error_code &EC) {
- return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
+ SmallString<128> Storage;
+ return directory_iterator(
+ std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC));
}
//===-----------------------------------------------------------------------===/
@@ -511,7 +556,7 @@ public:
/// Return the \p Status for this node. \p RequestedName should be the name
/// through which the caller referred to this node. It will override
/// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
- Status getStatus(StringRef RequestedName) const {
+ Status getStatus(const Twine &RequestedName) const {
return Status::copyWithNewName(Stat, RequestedName);
}
llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); }
@@ -585,7 +630,7 @@ public:
/// Return the \p Status for this node. \p RequestedName should be the name
/// through which the caller referred to this node. It will override
/// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
- Status getStatus(StringRef RequestedName) const {
+ Status getStatus(const Twine &RequestedName) const {
return Status::copyWithNewName(Stat, RequestedName);
}
InMemoryNode *getChild(StringRef Name) {
@@ -619,7 +664,7 @@ public:
};
namespace {
-Status getNodeStatus(const InMemoryNode *Node, StringRef RequestedName) {
+Status getNodeStatus(const InMemoryNode *Node, const Twine &RequestedName) {
if (auto Dir = dyn_cast<detail::InMemoryDirectory>(Node))
return Dir->getStatus(RequestedName);
if (auto File = dyn_cast<detail::InMemoryFile>(Node))
@@ -817,7 +862,7 @@ bool InMemoryFileSystem::addHardLink(const Twine &FromPath,
llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
auto Node = lookupInMemoryNode(*this, Root.get(), Path);
if (Node)
- return detail::getNodeStatus(*Node, Path.str());
+ return detail::getNodeStatus(*Node, Path);
return Node.getError();
}
@@ -1237,7 +1282,7 @@ class llvm::vfs::RedirectingFileSystemParser {
EntryArrayContents;
std::string ExternalContentsPath;
std::string Name;
- yaml::Node *NameValueNode;
+ yaml::Node *NameValueNode = nullptr;
auto UseExternalName =
RedirectingFileSystem::RedirectingFileEntry::NK_NotSet;
RedirectingFileSystem::EntryKind Kind;
@@ -1633,7 +1678,7 @@ static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
Status ExternalStatus) {
Status S = ExternalStatus;
if (!UseExternalNames)
- S = Status::copyWithNewName(S, Path.str());
+ S = Status::copyWithNewName(S, Path);
S.IsVFSMapped = true;
return S;
}
@@ -1650,7 +1695,7 @@ ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path,
return S;
} else { // directory
auto *DE = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(E);
- return Status::copyWithNewName(DE->getStatus(), Path.str());
+ return Status::copyWithNewName(DE->getStatus(), Path);
}
}