#ifndef LLVM_SUPPORT_VIRTUALFILESYSTEM_H
#define LLVM_SUPPORT_VIRTUALFILESYSTEM_H
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
#include <cassert>
#include <cstdint>
#include <ctime>
#include <memory>
#include <stack>
#include <string>
#include <system_error>
#include <utility>
#include <vector>
namespace llvm {
class MemoryBuffer;
class MemoryBufferRef;
class Twine;
namespace vfs {
class Status {
std::string Name;
llvm::sys::fs::UniqueID UID;
llvm::sys::TimePoint<> MTime;
uint32_t User;
uint32_t Group;
uint64_t Size;
llvm::sys::fs::file_type Type = llvm::sys::fs::file_type::status_error;
llvm::sys::fs::perms Perms;
public:
bool IsVFSMapped = false;
bool ExposesExternalVFSPath = false;
Status() = default;
Status(const llvm::sys::fs::file_status &Status);
Status(const Twine &Name, llvm::sys::fs::UniqueID UID,
llvm::sys::TimePoint<> MTime, uint32_t User, uint32_t Group,
uint64_t Size, llvm::sys::fs::file_type Type,
llvm::sys::fs::perms Perms);
static Status copyWithNewSize(const Status &In, uint64_t NewSize);
static Status copyWithNewName(const Status &In, const Twine &NewName);
static Status copyWithNewName(const llvm::sys::fs::file_status &In,
const Twine &NewName);
StringRef getName() const { return Name; }
llvm::sys::fs::file_type getType() const { return Type; }
llvm::sys::fs::perms getPermissions() const { return Perms; }
llvm::sys::TimePoint<> getLastModificationTime() const { return MTime; }
llvm::sys::fs::UniqueID getUniqueID() const { return UID; }
uint32_t getUser() const { return User; }
uint32_t getGroup() const { return Group; }
uint64_t getSize() const { return Size; }
bool equivalent(const Status &Other) const;
bool isDirectory() const;
bool isRegularFile() const;
bool isOther() const;
bool isSymlink() const;
bool isStatusKnown() const;
bool exists() const;
};
class File {
public:
virtual ~File();
virtual llvm::ErrorOr<Status> status() = 0;
virtual llvm::ErrorOr<std::string> getName() {
if (auto Status = status())
return Status->getName().str();
else
return Status.getError();
}
virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer(const Twine &Name, int64_t FileSize = -1,
bool RequiresNullTerminator = true, bool IsVolatile = false) = 0;
virtual std::error_code close() = 0;
static ErrorOr<std::unique_ptr<File>>
getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P);
protected:
virtual void setPath(const Twine &Path) {}
};
class directory_entry {
std::string Path;
llvm::sys::fs::file_type Type = llvm::sys::fs::file_type::type_unknown;
public:
directory_entry() = default;
directory_entry(std::string Path, llvm::sys::fs::file_type Type)
: Path(std::move(Path)), Type(Type) {}
llvm::StringRef path() const { return Path; }
llvm::sys::fs::file_type type() const { return Type; }
};
namespace detail {
struct DirIterImpl {
virtual ~DirIterImpl();
virtual std::error_code increment() = 0;
directory_entry CurrentEntry;
};
}
class directory_iterator {
std::shared_ptr<detail::DirIterImpl> Impl;
public:
directory_iterator(std::shared_ptr<detail::DirIterImpl> I)
: Impl(std::move(I)) {
assert(Impl.get() != nullptr && "requires non-null implementation");
if (Impl->CurrentEntry.path().empty())
Impl.reset(); }
directory_iterator() = default;
directory_iterator &increment(std::error_code &EC) {
assert(Impl && "attempting to increment past end");
EC = Impl->increment();
if (Impl->CurrentEntry.path().empty())
Impl.reset(); return *this;
}
const directory_entry &operator*() const { return Impl->CurrentEntry; }
const directory_entry *operator->() const { return &Impl->CurrentEntry; }
bool operator==(const directory_iterator &RHS) const {
if (Impl && RHS.Impl)
return Impl->CurrentEntry.path() == RHS.Impl->CurrentEntry.path();
return !Impl && !RHS.Impl;
}
bool operator!=(const directory_iterator &RHS) const {
return !(*this == RHS);
}
};
class FileSystem;
namespace detail {
struct RecDirIterState {
std::stack<directory_iterator, std::vector<directory_iterator>> Stack;
bool HasNoPushRequest = false;
};
}
class recursive_directory_iterator {
FileSystem *FS;
std::shared_ptr<detail::RecDirIterState>
State;
public:
recursive_directory_iterator(FileSystem &FS, const Twine &Path,
std::error_code &EC);
recursive_directory_iterator() = default;
recursive_directory_iterator &increment(std::error_code &EC);
const directory_entry &operator*() const { return *State->Stack.top(); }
const directory_entry *operator->() const { return &*State->Stack.top(); }
bool operator==(const recursive_directory_iterator &Other) const {
return State == Other.State; }
bool operator!=(const recursive_directory_iterator &RHS) const {
return !(*this == RHS);
}
int level() const {
assert(!State->Stack.empty() &&
"Cannot get level without any iteration state");
return State->Stack.size() - 1;
}
void no_push() { State->HasNoPushRequest = true; }
};
class FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem> {
public:
virtual ~FileSystem();
virtual llvm::ErrorOr<Status> status(const Twine &Path) = 0;
virtual llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine &Path) = 0;
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBufferForFile(const Twine &Name, int64_t FileSize = -1,
bool RequiresNullTerminator = true, bool IsVolatile = false);
virtual directory_iterator dir_begin(const Twine &Dir,
std::error_code &EC) = 0;
virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) = 0;
virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const = 0;
virtual std::error_code getRealPath(const Twine &Path,
SmallVectorImpl<char> &Output) const;
bool exists(const Twine &Path);
virtual std::error_code isLocal(const Twine &Path, bool &Result);
virtual std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const;
enum class PrintType { Summary, Contents, RecursiveContents };
void print(raw_ostream &OS, PrintType Type = PrintType::Contents,
unsigned IndentLevel = 0) const {
printImpl(OS, Type, IndentLevel);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void dump() const;
#endif
protected:
virtual void printImpl(raw_ostream &OS, PrintType Type,
unsigned IndentLevel) const {
printIndent(OS, IndentLevel);
OS << "FileSystem\n";
}
void printIndent(raw_ostream &OS, unsigned IndentLevel) const {
for (unsigned i = 0; i < IndentLevel; ++i)
OS << " ";
}
};
IntrusiveRefCntPtr<FileSystem> getRealFileSystem();
std::unique_ptr<FileSystem> createPhysicalFileSystem();
class OverlayFileSystem : public FileSystem {
using FileSystemList = SmallVector<IntrusiveRefCntPtr<FileSystem>, 1>;
FileSystemList FSList;
public:
OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> Base);
void pushOverlay(IntrusiveRefCntPtr<FileSystem> FS);
llvm::ErrorOr<Status> status(const Twine &Path) override;
llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine &Path) override;
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
std::error_code isLocal(const Twine &Path, bool &Result) override;
std::error_code getRealPath(const Twine &Path,
SmallVectorImpl<char> &Output) const override;
using iterator = FileSystemList::reverse_iterator;
using const_iterator = FileSystemList::const_reverse_iterator;
using reverse_iterator = FileSystemList::iterator;
using const_reverse_iterator = FileSystemList::const_iterator;
using range = iterator_range<iterator>;
using const_range = iterator_range<const_iterator>;
iterator overlays_begin() { return FSList.rbegin(); }
const_iterator overlays_begin() const { return FSList.rbegin(); }
iterator overlays_end() { return FSList.rend(); }
const_iterator overlays_end() const { return FSList.rend(); }
reverse_iterator overlays_rbegin() { return FSList.begin(); }
const_reverse_iterator overlays_rbegin() const { return FSList.begin(); }
reverse_iterator overlays_rend() { return FSList.end(); }
const_reverse_iterator overlays_rend() const { return FSList.end(); }
range overlays_range() { return llvm::reverse(FSList); }
const_range overlays_range() const { return llvm::reverse(FSList); }
protected:
void printImpl(raw_ostream &OS, PrintType Type,
unsigned IndentLevel) const override;
};
class ProxyFileSystem : public FileSystem {
public:
explicit ProxyFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
: FS(std::move(FS)) {}
llvm::ErrorOr<Status> status(const Twine &Path) override {
return FS->status(Path);
}
llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine &Path) override {
return FS->openFileForRead(Path);
}
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override {
return FS->dir_begin(Dir, EC);
}
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
return FS->getCurrentWorkingDirectory();
}
std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
return FS->setCurrentWorkingDirectory(Path);
}
std::error_code getRealPath(const Twine &Path,
SmallVectorImpl<char> &Output) const override {
return FS->getRealPath(Path, Output);
}
std::error_code isLocal(const Twine &Path, bool &Result) override {
return FS->isLocal(Path, Result);
}
protected:
FileSystem &getUnderlyingFS() { return *FS; }
private:
IntrusiveRefCntPtr<FileSystem> FS;
virtual void anchor();
};
namespace detail {
class InMemoryDirectory;
class InMemoryNode;
struct NewInMemoryNodeInfo {
llvm::sys::fs::UniqueID DirUID;
StringRef Path;
StringRef Name;
time_t ModificationTime;
std::unique_ptr<llvm::MemoryBuffer> Buffer;
uint32_t User;
uint32_t Group;
llvm::sys::fs::file_type Type;
llvm::sys::fs::perms Perms;
Status makeStatus() const;
};
class NamedNodeOrError {
ErrorOr<std::pair<llvm::SmallString<128>, const detail::InMemoryNode *>>
Value;
public:
NamedNodeOrError(llvm::SmallString<128> Name,
const detail::InMemoryNode *Node)
: Value(std::make_pair(Name, Node)) {}
NamedNodeOrError(std::error_code EC) : Value(EC) {}
NamedNodeOrError(llvm::errc EC) : Value(EC) {}
StringRef getName() const { return (*Value).first; }
explicit operator bool() const { return static_cast<bool>(Value); }
operator std::error_code() const { return Value.getError(); }
std::error_code getError() const { return Value.getError(); }
const detail::InMemoryNode *operator*() const { return (*Value).second; }
};
}
class InMemoryFileSystem : public FileSystem {
std::unique_ptr<detail::InMemoryDirectory> Root;
std::string WorkingDirectory;
bool UseNormalizedPaths = true;
using MakeNodeFn = llvm::function_ref<std::unique_ptr<detail::InMemoryNode>(
detail::NewInMemoryNodeInfo)>;
bool addFile(const Twine &Path, time_t ModificationTime,
std::unique_ptr<llvm::MemoryBuffer> Buffer,
Optional<uint32_t> User, Optional<uint32_t> Group,
Optional<llvm::sys::fs::file_type> Type,
Optional<llvm::sys::fs::perms> Perms, MakeNodeFn MakeNode);
detail::NamedNodeOrError lookupNode(const Twine &P, bool FollowFinalSymlink,
size_t SymlinkDepth = 0) const;
class DirIterator;
public:
explicit InMemoryFileSystem(bool UseNormalizedPaths = true);
~InMemoryFileSystem() override;
bool addFile(const Twine &Path, time_t ModificationTime,
std::unique_ptr<llvm::MemoryBuffer> Buffer,
Optional<uint32_t> User = None, Optional<uint32_t> Group = None,
Optional<llvm::sys::fs::file_type> Type = None,
Optional<llvm::sys::fs::perms> Perms = None);
bool addHardLink(const Twine &NewLink, const Twine &Target);
static constexpr size_t MaxSymlinkDepth = 16;
bool addSymbolicLink(const Twine &NewLink, const Twine &Target,
time_t ModificationTime, Optional<uint32_t> User = None,
Optional<uint32_t> Group = None,
Optional<llvm::sys::fs::perms> Perms = None);
bool addFileNoOwn(const Twine &Path, time_t ModificationTime,
const llvm::MemoryBufferRef &Buffer,
Optional<uint32_t> User = None,
Optional<uint32_t> Group = None,
Optional<llvm::sys::fs::file_type> Type = None,
Optional<llvm::sys::fs::perms> Perms = None);
std::string toString() const;
bool useNormalizedPaths() const { return UseNormalizedPaths; }
llvm::ErrorOr<Status> status(const Twine &Path) override;
llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine &Path) override;
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
return WorkingDirectory;
}
std::error_code getRealPath(const Twine &Path,
SmallVectorImpl<char> &Output) const override;
std::error_code isLocal(const Twine &Path, bool &Result) override;
std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
protected:
void printImpl(raw_ostream &OS, PrintType Type,
unsigned IndentLevel) const override;
};
llvm::sys::fs::UniqueID getNextVirtualUniqueID();
std::unique_ptr<FileSystem>
getVFSFromYAML(std::unique_ptr<llvm::MemoryBuffer> Buffer,
llvm::SourceMgr::DiagHandlerTy DiagHandler,
StringRef YAMLFilePath, void *DiagContext = nullptr,
IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem());
struct YAMLVFSEntry {
template <typename T1, typename T2>
YAMLVFSEntry(T1 &&VPath, T2 &&RPath, bool IsDirectory = false)
: VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)),
IsDirectory(IsDirectory) {}
std::string VPath;
std::string RPath;
bool IsDirectory = false;
};
class RedirectingFSDirIterImpl;
class RedirectingFileSystemParser;
class RedirectingFileSystem : public vfs::FileSystem {
public:
enum EntryKind { EK_Directory, EK_DirectoryRemap, EK_File };
enum NameKind { NK_NotSet, NK_External, NK_Virtual };
enum class RedirectKind {
Fallthrough,
Fallback,
RedirectOnly
};
class Entry {
EntryKind Kind;
std::string Name;
public:
Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
virtual ~Entry() = default;
StringRef getName() const { return Name; }
EntryKind getKind() const { return Kind; }
};
class DirectoryEntry : public Entry {
std::vector<std::unique_ptr<Entry>> Contents;
Status S;
public:
DirectoryEntry(StringRef Name, std::vector<std::unique_ptr<Entry>> Contents,
Status S)
: Entry(EK_Directory, Name), Contents(std::move(Contents)),
S(std::move(S)) {}
DirectoryEntry(StringRef Name, Status S)
: Entry(EK_Directory, Name), S(std::move(S)) {}
Status getStatus() { return S; }
void addContent(std::unique_ptr<Entry> Content) {
Contents.push_back(std::move(Content));
}
Entry *getLastContent() const { return Contents.back().get(); }
using iterator = decltype(Contents)::iterator;
iterator contents_begin() { return Contents.begin(); }
iterator contents_end() { return Contents.end(); }
static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
};
class RemapEntry : public Entry {
std::string ExternalContentsPath;
NameKind UseName;
protected:
RemapEntry(EntryKind K, StringRef Name, StringRef ExternalContentsPath,
NameKind UseName)
: Entry(K, Name), ExternalContentsPath(ExternalContentsPath),
UseName(UseName) {}
public:
StringRef getExternalContentsPath() const { return ExternalContentsPath; }
bool useExternalName(bool GlobalUseExternalName) const {
return UseName == NK_NotSet ? GlobalUseExternalName
: (UseName == NK_External);
}
NameKind getUseName() const { return UseName; }
static bool classof(const Entry *E) {
switch (E->getKind()) {
case EK_DirectoryRemap:
LLVM_FALLTHROUGH;
case EK_File:
return true;
case EK_Directory:
return false;
}
llvm_unreachable("invalid entry kind");
}
};
class DirectoryRemapEntry : public RemapEntry {
public:
DirectoryRemapEntry(StringRef Name, StringRef ExternalContentsPath,
NameKind UseName)
: RemapEntry(EK_DirectoryRemap, Name, ExternalContentsPath, UseName) {}
static bool classof(const Entry *E) {
return E->getKind() == EK_DirectoryRemap;
}
};
class FileEntry : public RemapEntry {
public:
FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
: RemapEntry(EK_File, Name, ExternalContentsPath, UseName) {}
static bool classof(const Entry *E) { return E->getKind() == EK_File; }
};
struct LookupResult {
Entry *E;
private:
Optional<std::string> ExternalRedirect;
public:
LookupResult(Entry *E, sys::path::const_iterator Start,
sys::path::const_iterator End);
Optional<StringRef> getExternalRedirect() const {
if (isa<DirectoryRemapEntry>(E))
return StringRef(*ExternalRedirect);
if (auto *FE = dyn_cast<FileEntry>(E))
return FE->getExternalContentsPath();
return None;
}
};
private:
friend class RedirectingFSDirIterImpl;
friend class RedirectingFileSystemParser;
std::error_code makeCanonical(SmallVectorImpl<char> &Path) const;
ErrorOr<Status> getExternalStatus(const Twine &CanonicalPath,
const Twine &OriginalPath) const;
bool pathComponentMatches(llvm::StringRef lhs, llvm::StringRef rhs) const {
if ((CaseSensitive ? lhs.equals(rhs) : lhs.equals_insensitive(rhs)))
return true;
return (lhs == "/" && rhs == "\\") || (lhs == "\\" && rhs == "/");
}
std::vector<std::unique_ptr<Entry>> Roots;
std::string WorkingDirectory;
IntrusiveRefCntPtr<FileSystem> ExternalFS;
std::string ExternalContentsPrefixDir;
bool CaseSensitive = is_style_posix(sys::path::Style::native);
bool IsRelativeOverlay = false;
bool UseExternalNames = true;
RedirectKind Redirection = RedirectKind::Fallthrough;
RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS);
ErrorOr<LookupResult> lookupPathImpl(llvm::sys::path::const_iterator Start,
llvm::sys::path::const_iterator End,
Entry *From) const;
ErrorOr<Status> status(const Twine &CanonicalPath, const Twine &OriginalPath,
const LookupResult &Result);
public:
ErrorOr<LookupResult> lookupPath(StringRef Path) const;
static std::unique_ptr<RedirectingFileSystem>
create(std::unique_ptr<MemoryBuffer> Buffer,
SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath,
void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS);
static std::unique_ptr<RedirectingFileSystem>
create(ArrayRef<std::pair<std::string, std::string>> RemappedFiles,
bool UseExternalNames, FileSystem &ExternalFS);
ErrorOr<Status> status(const Twine &Path) override;
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
std::error_code getRealPath(const Twine &Path,
SmallVectorImpl<char> &Output) const override;
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
std::error_code isLocal(const Twine &Path, bool &Result) override;
std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const override;
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
void setExternalContentsPrefixDir(StringRef PrefixDir);
StringRef getExternalContentsPrefixDir() const;
void setFallthrough(bool Fallthrough);
void setRedirection(RedirectingFileSystem::RedirectKind Kind);
std::vector<llvm::StringRef> getRoots() const;
void printEntry(raw_ostream &OS, Entry *E, unsigned IndentLevel = 0) const;
protected:
void printImpl(raw_ostream &OS, PrintType Type,
unsigned IndentLevel) const override;
};
void collectVFSFromYAML(
std::unique_ptr<llvm::MemoryBuffer> Buffer,
llvm::SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath,
SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
void *DiagContext = nullptr,
IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem());
class YAMLVFSWriter {
std::vector<YAMLVFSEntry> Mappings;
Optional<bool> IsCaseSensitive;
Optional<bool> IsOverlayRelative;
Optional<bool> UseExternalNames;
std::string OverlayDir;
void addEntry(StringRef VirtualPath, StringRef RealPath, bool IsDirectory);
public:
YAMLVFSWriter() = default;
void addFileMapping(StringRef VirtualPath, StringRef RealPath);
void addDirectoryMapping(StringRef VirtualPath, StringRef RealPath);
void setCaseSensitivity(bool CaseSensitive) {
IsCaseSensitive = CaseSensitive;
}
void setUseExternalNames(bool UseExtNames) { UseExternalNames = UseExtNames; }
void setOverlayDir(StringRef OverlayDirectory) {
IsOverlayRelative = true;
OverlayDir.assign(OverlayDirectory.str());
}
const std::vector<YAMLVFSEntry> &getMappings() const { return Mappings; }
void write(llvm::raw_ostream &OS);
};
} }
#endif