aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Support/VirtualFileSystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Support/VirtualFileSystem.cpp')
-rw-r--r--llvm/lib/Support/VirtualFileSystem.cpp128
1 files changed, 111 insertions, 17 deletions
diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp
index 2f457e1..9c6a0c0 100644
--- a/llvm/lib/Support/VirtualFileSystem.cpp
+++ b/llvm/lib/Support/VirtualFileSystem.cpp
@@ -590,10 +590,15 @@ namespace vfs {
namespace detail {
-enum InMemoryNodeKind { IME_File, IME_Directory, IME_HardLink };
+enum InMemoryNodeKind {
+ IME_File,
+ IME_Directory,
+ IME_HardLink,
+ IME_SymbolicLink,
+};
/// The in memory file system is a tree of Nodes. Every node can either be a
-/// file , hardlink or a directory.
+/// file, symlink, hardlink or a directory.
class InMemoryNode {
InMemoryNodeKind Kind;
std::string FileName;
@@ -662,6 +667,30 @@ public:
}
};
+class InMemorySymbolicLink : public InMemoryNode {
+ std::string TargetPath;
+ Status Stat;
+
+public:
+ InMemorySymbolicLink(StringRef Path, StringRef TargetPath, Status Stat)
+ : InMemoryNode(Path, IME_SymbolicLink), TargetPath(std::move(TargetPath)),
+ Stat(Stat) {}
+
+ std::string toString(unsigned Indent) const override {
+ return std::string(Indent, ' ') + "SymbolicLink to -> " + TargetPath;
+ }
+
+ Status getStatus(const Twine &RequestedName) const override {
+ return Status::copyWithNewName(Stat, RequestedName);
+ }
+
+ StringRef getTargetPath() const { return TargetPath; }
+
+ static bool classof(const InMemoryNode *N) {
+ return N->getKind() == IME_SymbolicLink;
+ }
+};
+
/// Adapt a InMemoryFile for VFS' File interface. The goal is to make
/// \p InMemoryFileAdaptor mimic as much as possible the behavior of
/// \p RealFile.
@@ -897,8 +926,9 @@ bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
});
}
-ErrorOr<const detail::InMemoryNode *>
-InMemoryFileSystem::lookupNode(const Twine &P) const {
+detail::NamedNodeOrError
+InMemoryFileSystem::lookupNode(const Twine &P, bool FollowFinalSymlink,
+ size_t SymlinkDepth) const {
SmallString<128> Path;
P.toVector(Path);
@@ -912,7 +942,7 @@ InMemoryFileSystem::lookupNode(const Twine &P) const {
const detail::InMemoryDirectory *Dir = Root.get();
if (Path.empty())
- return Dir;
+ return detail::NamedNodeOrError(Path, Dir);
auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
while (true) {
@@ -921,30 +951,63 @@ InMemoryFileSystem::lookupNode(const Twine &P) const {
if (!Node)
return errc::no_such_file_or_directory;
+ if (auto Symlink = dyn_cast<detail::InMemorySymbolicLink>(Node)) {
+ // If we're at the end of the path, and we're not following through
+ // terminal symlinks, then we're done.
+ if (I == E && !FollowFinalSymlink)
+ return detail::NamedNodeOrError(Path, Symlink);
+
+ if (SymlinkDepth > InMemoryFileSystem::MaxSymlinkDepth)
+ return errc::no_such_file_or_directory;
+
+ SmallString<128> TargetPath = Symlink->getTargetPath();
+ if (std::error_code EC = makeAbsolute(TargetPath))
+ return EC;
+
+ // Keep going with the target. We always want to follow symlinks here
+ // because we're either at the end of a path that we want to follow, or
+ // not at the end of a path, in which case we need to follow the symlink
+ // regardless.
+ auto Target =
+ lookupNode(TargetPath, /*FollowFinalSymlink=*/true, SymlinkDepth + 1);
+ if (!Target || I == E)
+ return Target;
+
+ if (!isa<detail::InMemoryDirectory>(*Target))
+ return errc::no_such_file_or_directory;
+
+ // Otherwise, continue on the search in the symlinked directory.
+ Dir = cast<detail::InMemoryDirectory>(*Target);
+ continue;
+ }
+
// Return the file if it's at the end of the path.
if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
if (I == E)
- return File;
+ return detail::NamedNodeOrError(Path, File);
return errc::no_such_file_or_directory;
}
// If Node is HardLink then return the resolved file.
if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) {
if (I == E)
- return &File->getResolvedFile();
+ return detail::NamedNodeOrError(Path, &File->getResolvedFile());
return errc::no_such_file_or_directory;
}
// Traverse directories.
Dir = cast<detail::InMemoryDirectory>(Node);
if (I == E)
- return Dir;
+ return detail::NamedNodeOrError(Path, Dir);
}
}
bool InMemoryFileSystem::addHardLink(const Twine &NewLink,
const Twine &Target) {
- auto NewLinkNode = lookupNode(NewLink);
- auto TargetNode = lookupNode(Target);
+ auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
+ // Whether symlinks in the hardlink target are followed is
+ // implementation-defined in POSIX.
+ // We're following symlinks here to be consistent with macOS.
+ auto TargetNode = lookupNode(Target, /*FollowFinalSymlink=*/true);
// FromPath must not have been added before. ToPath must have been added
// before. Resolved ToPath must be a File.
if (!TargetNode || NewLinkNode || !isa<detail::InMemoryFile>(*TargetNode))
@@ -957,8 +1020,30 @@ bool InMemoryFileSystem::addHardLink(const Twine &NewLink,
});
}
+bool InMemoryFileSystem::addSymbolicLink(const Twine &NewLink,
+ const Twine &Target,
+ time_t ModificationTime,
+ Optional<uint32_t> User,
+ Optional<uint32_t> Group,
+ Optional<llvm::sys::fs::perms> Perms) {
+ auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
+ if (NewLinkNode)
+ return false;
+
+ SmallString<128> NewLinkStr, TargetStr;
+ NewLink.toVector(NewLinkStr);
+ Target.toVector(TargetStr);
+
+ return addFile(NewLinkStr, ModificationTime, nullptr, User, Group,
+ sys::fs::file_type::symlink_file, Perms,
+ [&](detail::NewInMemoryNodeInfo NNI) {
+ return std::make_unique<detail::InMemorySymbolicLink>(
+ NewLinkStr, TargetStr, NNI.makeStatus());
+ });
+}
+
llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
- auto Node = lookupNode(Path);
+ auto Node = lookupNode(Path, /*FollowFinalSymlink=*/true);
if (Node)
return (*Node)->getStatus(Path);
return Node.getError();
@@ -966,7 +1051,7 @@ llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
llvm::ErrorOr<std::unique_ptr<File>>
InMemoryFileSystem::openFileForRead(const Twine &Path) {
- auto Node = lookupNode(Path);
+ auto Node = lookupNode(Path,/*FollowFinalSymlink=*/true);
if (!Node)
return Node.getError();
@@ -982,6 +1067,7 @@ InMemoryFileSystem::openFileForRead(const Twine &Path) {
/// Adaptor from InMemoryDir::iterator to directory_iterator.
class InMemoryFileSystem::DirIterator : public llvm::vfs::detail::DirIterImpl {
+ const InMemoryFileSystem *FS;
detail::InMemoryDirectory::const_iterator I;
detail::InMemoryDirectory::const_iterator E;
std::string RequestedDirName;
@@ -999,6 +1085,13 @@ class InMemoryFileSystem::DirIterator : public llvm::vfs::detail::DirIterImpl {
case detail::IME_Directory:
Type = sys::fs::file_type::directory_file;
break;
+ case detail::IME_SymbolicLink:
+ if (auto SymlinkTarget =
+ FS->lookupNode(Path, /*FollowFinalSymlink=*/true)) {
+ Path = SymlinkTarget.getName();
+ Type = (*SymlinkTarget)->getStatus(Path).getType();
+ }
+ break;
}
CurrentEntry = directory_entry(std::string(Path.str()), Type);
} else {
@@ -1011,9 +1104,10 @@ class InMemoryFileSystem::DirIterator : public llvm::vfs::detail::DirIterImpl {
public:
DirIterator() = default;
- explicit DirIterator(const detail::InMemoryDirectory &Dir,
- std::string RequestedDirName)
- : I(Dir.begin()), E(Dir.end()),
+ DirIterator(const InMemoryFileSystem *FS,
+ const detail::InMemoryDirectory &Dir,
+ std::string RequestedDirName)
+ : FS(FS), I(Dir.begin()), E(Dir.end()),
RequestedDirName(std::move(RequestedDirName)) {
setCurrentEntry();
}
@@ -1027,7 +1121,7 @@ public:
directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
std::error_code &EC) {
- auto Node = lookupNode(Dir);
+ auto Node = lookupNode(Dir, /*FollowFinalSymlink=*/true);
if (!Node) {
EC = Node.getError();
return directory_iterator(std::make_shared<DirIterator>());
@@ -1035,7 +1129,7 @@ directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
return directory_iterator(
- std::make_shared<DirIterator>(*DirNode, Dir.str()));
+ std::make_shared<DirIterator>(this, *DirNode, Dir.str()));
EC = make_error_code(llvm::errc::not_a_directory);
return directory_iterator(std::make_shared<DirIterator>());