VirtualFileSystem.h revision 343171
1343171Sdim//===- VirtualFileSystem.h - Virtual File System Layer ----------*- C++ -*-===// 2343171Sdim// 3343171Sdim// The LLVM Compiler Infrastructure 4343171Sdim// 5343171Sdim// This file is distributed under the University of Illinois Open Source 6343171Sdim// License. See LICENSE.TXT for details. 7343171Sdim// 8343171Sdim//===----------------------------------------------------------------------===// 9343171Sdim// 10343171Sdim/// \file 11343171Sdim/// Defines the virtual file system interface vfs::FileSystem. 12343171Sdim// 13343171Sdim//===----------------------------------------------------------------------===// 14343171Sdim 15343171Sdim#ifndef LLVM_SUPPORT_VIRTUALFILESYSTEM_H 16343171Sdim#define LLVM_SUPPORT_VIRTUALFILESYSTEM_H 17343171Sdim 18343171Sdim#include "llvm/ADT/IntrusiveRefCntPtr.h" 19343171Sdim#include "llvm/ADT/None.h" 20343171Sdim#include "llvm/ADT/Optional.h" 21343171Sdim#include "llvm/ADT/SmallVector.h" 22343171Sdim#include "llvm/ADT/StringRef.h" 23343171Sdim#include "llvm/ADT/Twine.h" 24343171Sdim#include "llvm/Support/Chrono.h" 25343171Sdim#include "llvm/Support/ErrorOr.h" 26343171Sdim#include "llvm/Support/FileSystem.h" 27343171Sdim#include "llvm/Support/Path.h" 28343171Sdim#include "llvm/Support/SourceMgr.h" 29343171Sdim#include <cassert> 30343171Sdim#include <cstdint> 31343171Sdim#include <ctime> 32343171Sdim#include <memory> 33343171Sdim#include <stack> 34343171Sdim#include <string> 35343171Sdim#include <system_error> 36343171Sdim#include <utility> 37343171Sdim#include <vector> 38343171Sdim 39343171Sdimnamespace llvm { 40343171Sdim 41343171Sdimclass MemoryBuffer; 42343171Sdim 43343171Sdimnamespace vfs { 44343171Sdim 45343171Sdim/// The result of a \p status operation. 46343171Sdimclass Status { 47343171Sdim std::string Name; 48343171Sdim llvm::sys::fs::UniqueID UID; 49343171Sdim llvm::sys::TimePoint<> MTime; 50343171Sdim uint32_t User; 51343171Sdim uint32_t Group; 52343171Sdim uint64_t Size; 53343171Sdim llvm::sys::fs::file_type Type = llvm::sys::fs::file_type::status_error; 54343171Sdim llvm::sys::fs::perms Perms; 55343171Sdim 56343171Sdimpublic: 57343171Sdim // FIXME: remove when files support multiple names 58343171Sdim bool IsVFSMapped = false; 59343171Sdim 60343171Sdim Status() = default; 61343171Sdim Status(const llvm::sys::fs::file_status &Status); 62343171Sdim Status(StringRef Name, llvm::sys::fs::UniqueID UID, 63343171Sdim llvm::sys::TimePoint<> MTime, uint32_t User, uint32_t Group, 64343171Sdim uint64_t Size, llvm::sys::fs::file_type Type, 65343171Sdim llvm::sys::fs::perms Perms); 66343171Sdim 67343171Sdim /// Get a copy of a Status with a different name. 68343171Sdim static Status copyWithNewName(const Status &In, StringRef NewName); 69343171Sdim static Status copyWithNewName(const llvm::sys::fs::file_status &In, 70343171Sdim StringRef NewName); 71343171Sdim 72343171Sdim /// Returns the name that should be used for this file or directory. 73343171Sdim StringRef getName() const { return Name; } 74343171Sdim 75343171Sdim /// @name Status interface from llvm::sys::fs 76343171Sdim /// @{ 77343171Sdim llvm::sys::fs::file_type getType() const { return Type; } 78343171Sdim llvm::sys::fs::perms getPermissions() const { return Perms; } 79343171Sdim llvm::sys::TimePoint<> getLastModificationTime() const { return MTime; } 80343171Sdim llvm::sys::fs::UniqueID getUniqueID() const { return UID; } 81343171Sdim uint32_t getUser() const { return User; } 82343171Sdim uint32_t getGroup() const { return Group; } 83343171Sdim uint64_t getSize() const { return Size; } 84343171Sdim /// @} 85343171Sdim /// @name Status queries 86343171Sdim /// These are static queries in llvm::sys::fs. 87343171Sdim /// @{ 88343171Sdim bool equivalent(const Status &Other) const; 89343171Sdim bool isDirectory() const; 90343171Sdim bool isRegularFile() const; 91343171Sdim bool isOther() const; 92343171Sdim bool isSymlink() const; 93343171Sdim bool isStatusKnown() const; 94343171Sdim bool exists() const; 95343171Sdim /// @} 96343171Sdim}; 97343171Sdim 98343171Sdim/// Represents an open file. 99343171Sdimclass File { 100343171Sdimpublic: 101343171Sdim /// Destroy the file after closing it (if open). 102343171Sdim /// Sub-classes should generally call close() inside their destructors. We 103343171Sdim /// cannot do that from the base class, since close is virtual. 104343171Sdim virtual ~File(); 105343171Sdim 106343171Sdim /// Get the status of the file. 107343171Sdim virtual llvm::ErrorOr<Status> status() = 0; 108343171Sdim 109343171Sdim /// Get the name of the file 110343171Sdim virtual llvm::ErrorOr<std::string> getName() { 111343171Sdim if (auto Status = status()) 112343171Sdim return Status->getName().str(); 113343171Sdim else 114343171Sdim return Status.getError(); 115343171Sdim } 116343171Sdim 117343171Sdim /// Get the contents of the file as a \p MemoryBuffer. 118343171Sdim virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 119343171Sdim getBuffer(const Twine &Name, int64_t FileSize = -1, 120343171Sdim bool RequiresNullTerminator = true, bool IsVolatile = false) = 0; 121343171Sdim 122343171Sdim /// Closes the file. 123343171Sdim virtual std::error_code close() = 0; 124343171Sdim}; 125343171Sdim 126343171Sdim/// A member of a directory, yielded by a directory_iterator. 127343171Sdim/// Only information available on most platforms is included. 128343171Sdimclass directory_entry { 129343171Sdim std::string Path; 130343171Sdim llvm::sys::fs::file_type Type; 131343171Sdim 132343171Sdimpublic: 133343171Sdim directory_entry() = default; 134343171Sdim directory_entry(std::string Path, llvm::sys::fs::file_type Type) 135343171Sdim : Path(std::move(Path)), Type(Type) {} 136343171Sdim 137343171Sdim llvm::StringRef path() const { return Path; } 138343171Sdim llvm::sys::fs::file_type type() const { return Type; } 139343171Sdim}; 140343171Sdim 141343171Sdimnamespace detail { 142343171Sdim 143343171Sdim/// An interface for virtual file systems to provide an iterator over the 144343171Sdim/// (non-recursive) contents of a directory. 145343171Sdimstruct DirIterImpl { 146343171Sdim virtual ~DirIterImpl(); 147343171Sdim 148343171Sdim /// Sets \c CurrentEntry to the next entry in the directory on success, 149343171Sdim /// to directory_entry() at end, or returns a system-defined \c error_code. 150343171Sdim virtual std::error_code increment() = 0; 151343171Sdim 152343171Sdim directory_entry CurrentEntry; 153343171Sdim}; 154343171Sdim 155343171Sdim} // namespace detail 156343171Sdim 157343171Sdim/// An input iterator over the entries in a virtual path, similar to 158343171Sdim/// llvm::sys::fs::directory_iterator. 159343171Sdimclass directory_iterator { 160343171Sdim std::shared_ptr<detail::DirIterImpl> Impl; // Input iterator semantics on copy 161343171Sdim 162343171Sdimpublic: 163343171Sdim directory_iterator(std::shared_ptr<detail::DirIterImpl> I) 164343171Sdim : Impl(std::move(I)) { 165343171Sdim assert(Impl.get() != nullptr && "requires non-null implementation"); 166343171Sdim if (Impl->CurrentEntry.path().empty()) 167343171Sdim Impl.reset(); // Normalize the end iterator to Impl == nullptr. 168343171Sdim } 169343171Sdim 170343171Sdim /// Construct an 'end' iterator. 171343171Sdim directory_iterator() = default; 172343171Sdim 173343171Sdim /// Equivalent to operator++, with an error code. 174343171Sdim directory_iterator &increment(std::error_code &EC) { 175343171Sdim assert(Impl && "attempting to increment past end"); 176343171Sdim EC = Impl->increment(); 177343171Sdim if (Impl->CurrentEntry.path().empty()) 178343171Sdim Impl.reset(); // Normalize the end iterator to Impl == nullptr. 179343171Sdim return *this; 180343171Sdim } 181343171Sdim 182343171Sdim const directory_entry &operator*() const { return Impl->CurrentEntry; } 183343171Sdim const directory_entry *operator->() const { return &Impl->CurrentEntry; } 184343171Sdim 185343171Sdim bool operator==(const directory_iterator &RHS) const { 186343171Sdim if (Impl && RHS.Impl) 187343171Sdim return Impl->CurrentEntry.path() == RHS.Impl->CurrentEntry.path(); 188343171Sdim return !Impl && !RHS.Impl; 189343171Sdim } 190343171Sdim bool operator!=(const directory_iterator &RHS) const { 191343171Sdim return !(*this == RHS); 192343171Sdim } 193343171Sdim}; 194343171Sdim 195343171Sdimclass FileSystem; 196343171Sdim 197343171Sdimnamespace detail { 198343171Sdim 199343171Sdim/// Keeps state for the recursive_directory_iterator. 200343171Sdimstruct RecDirIterState { 201343171Sdim std::stack<directory_iterator, std::vector<directory_iterator>> Stack; 202343171Sdim bool HasNoPushRequest = false; 203343171Sdim}; 204343171Sdim 205343171Sdim} // end namespace detail 206343171Sdim 207343171Sdim/// An input iterator over the recursive contents of a virtual path, 208343171Sdim/// similar to llvm::sys::fs::recursive_directory_iterator. 209343171Sdimclass recursive_directory_iterator { 210343171Sdim FileSystem *FS; 211343171Sdim std::shared_ptr<detail::RecDirIterState> 212343171Sdim State; // Input iterator semantics on copy. 213343171Sdim 214343171Sdimpublic: 215343171Sdim recursive_directory_iterator(FileSystem &FS, const Twine &Path, 216343171Sdim std::error_code &EC); 217343171Sdim 218343171Sdim /// Construct an 'end' iterator. 219343171Sdim recursive_directory_iterator() = default; 220343171Sdim 221343171Sdim /// Equivalent to operator++, with an error code. 222343171Sdim recursive_directory_iterator &increment(std::error_code &EC); 223343171Sdim 224343171Sdim const directory_entry &operator*() const { return *State->Stack.top(); } 225343171Sdim const directory_entry *operator->() const { return &*State->Stack.top(); } 226343171Sdim 227343171Sdim bool operator==(const recursive_directory_iterator &Other) const { 228343171Sdim return State == Other.State; // identity 229343171Sdim } 230343171Sdim bool operator!=(const recursive_directory_iterator &RHS) const { 231343171Sdim return !(*this == RHS); 232343171Sdim } 233343171Sdim 234343171Sdim /// Gets the current level. Starting path is at level 0. 235343171Sdim int level() const { 236343171Sdim assert(!State->Stack.empty() && 237343171Sdim "Cannot get level without any iteration state"); 238343171Sdim return State->Stack.size() - 1; 239343171Sdim } 240343171Sdim 241343171Sdim void no_push() { State->HasNoPushRequest = true; } 242343171Sdim}; 243343171Sdim 244343171Sdim/// The virtual file system interface. 245343171Sdimclass FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem> { 246343171Sdimpublic: 247343171Sdim virtual ~FileSystem(); 248343171Sdim 249343171Sdim /// Get the status of the entry at \p Path, if one exists. 250343171Sdim virtual llvm::ErrorOr<Status> status(const Twine &Path) = 0; 251343171Sdim 252343171Sdim /// Get a \p File object for the file at \p Path, if one exists. 253343171Sdim virtual llvm::ErrorOr<std::unique_ptr<File>> 254343171Sdim openFileForRead(const Twine &Path) = 0; 255343171Sdim 256343171Sdim /// This is a convenience method that opens a file, gets its content and then 257343171Sdim /// closes the file. 258343171Sdim llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 259343171Sdim getBufferForFile(const Twine &Name, int64_t FileSize = -1, 260343171Sdim bool RequiresNullTerminator = true, bool IsVolatile = false); 261343171Sdim 262343171Sdim /// Get a directory_iterator for \p Dir. 263343171Sdim /// \note The 'end' iterator is directory_iterator(). 264343171Sdim virtual directory_iterator dir_begin(const Twine &Dir, 265343171Sdim std::error_code &EC) = 0; 266343171Sdim 267343171Sdim /// Set the working directory. This will affect all following operations on 268343171Sdim /// this file system and may propagate down for nested file systems. 269343171Sdim virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) = 0; 270343171Sdim 271343171Sdim /// Get the working directory of this file system. 272343171Sdim virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const = 0; 273343171Sdim 274343171Sdim /// Gets real path of \p Path e.g. collapse all . and .. patterns, resolve 275343171Sdim /// symlinks. For real file system, this uses `llvm::sys::fs::real_path`. 276343171Sdim /// This returns errc::operation_not_permitted if not implemented by subclass. 277343171Sdim virtual std::error_code getRealPath(const Twine &Path, 278343171Sdim SmallVectorImpl<char> &Output) const; 279343171Sdim 280343171Sdim /// Check whether a file exists. Provided for convenience. 281343171Sdim bool exists(const Twine &Path); 282343171Sdim 283343171Sdim /// Is the file mounted on a local filesystem? 284343171Sdim virtual std::error_code isLocal(const Twine &Path, bool &Result); 285343171Sdim 286343171Sdim /// Make \a Path an absolute path. 287343171Sdim /// 288343171Sdim /// Makes \a Path absolute using the current directory if it is not already. 289343171Sdim /// An empty \a Path will result in the current directory. 290343171Sdim /// 291343171Sdim /// /absolute/path => /absolute/path 292343171Sdim /// relative/../path => <current-directory>/relative/../path 293343171Sdim /// 294343171Sdim /// \param Path A path that is modified to be an absolute path. 295343171Sdim /// \returns success if \a path has been made absolute, otherwise a 296343171Sdim /// platform-specific error_code. 297343171Sdim std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const; 298343171Sdim}; 299343171Sdim 300343171Sdim/// Gets an \p vfs::FileSystem for the 'real' file system, as seen by 301343171Sdim/// the operating system. 302343171SdimIntrusiveRefCntPtr<FileSystem> getRealFileSystem(); 303343171Sdim 304343171Sdim/// A file system that allows overlaying one \p AbstractFileSystem on top 305343171Sdim/// of another. 306343171Sdim/// 307343171Sdim/// Consists of a stack of >=1 \p FileSystem objects, which are treated as being 308343171Sdim/// one merged file system. When there is a directory that exists in more than 309343171Sdim/// one file system, the \p OverlayFileSystem contains a directory containing 310343171Sdim/// the union of their contents. The attributes (permissions, etc.) of the 311343171Sdim/// top-most (most recently added) directory are used. When there is a file 312343171Sdim/// that exists in more than one file system, the file in the top-most file 313343171Sdim/// system overrides the other(s). 314343171Sdimclass OverlayFileSystem : public FileSystem { 315343171Sdim using FileSystemList = SmallVector<IntrusiveRefCntPtr<FileSystem>, 1>; 316343171Sdim 317343171Sdim /// The stack of file systems, implemented as a list in order of 318343171Sdim /// their addition. 319343171Sdim FileSystemList FSList; 320343171Sdim 321343171Sdimpublic: 322343171Sdim OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> Base); 323343171Sdim 324343171Sdim /// Pushes a file system on top of the stack. 325343171Sdim void pushOverlay(IntrusiveRefCntPtr<FileSystem> FS); 326343171Sdim 327343171Sdim llvm::ErrorOr<Status> status(const Twine &Path) override; 328343171Sdim llvm::ErrorOr<std::unique_ptr<File>> 329343171Sdim openFileForRead(const Twine &Path) override; 330343171Sdim directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; 331343171Sdim llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; 332343171Sdim std::error_code setCurrentWorkingDirectory(const Twine &Path) override; 333343171Sdim std::error_code isLocal(const Twine &Path, bool &Result) override; 334343171Sdim std::error_code getRealPath(const Twine &Path, 335343171Sdim SmallVectorImpl<char> &Output) const override; 336343171Sdim 337343171Sdim using iterator = FileSystemList::reverse_iterator; 338343171Sdim using const_iterator = FileSystemList::const_reverse_iterator; 339343171Sdim 340343171Sdim /// Get an iterator pointing to the most recently added file system. 341343171Sdim iterator overlays_begin() { return FSList.rbegin(); } 342343171Sdim const_iterator overlays_begin() const { return FSList.rbegin(); } 343343171Sdim 344343171Sdim /// Get an iterator pointing one-past the least recently added file 345343171Sdim /// system. 346343171Sdim iterator overlays_end() { return FSList.rend(); } 347343171Sdim const_iterator overlays_end() const { return FSList.rend(); } 348343171Sdim}; 349343171Sdim 350343171Sdim/// By default, this delegates all calls to the underlying file system. This 351343171Sdim/// is useful when derived file systems want to override some calls and still 352343171Sdim/// proxy other calls. 353343171Sdimclass ProxyFileSystem : public FileSystem { 354343171Sdimpublic: 355343171Sdim explicit ProxyFileSystem(IntrusiveRefCntPtr<FileSystem> FS) 356343171Sdim : FS(std::move(FS)) {} 357343171Sdim 358343171Sdim llvm::ErrorOr<Status> status(const Twine &Path) override { 359343171Sdim return FS->status(Path); 360343171Sdim } 361343171Sdim llvm::ErrorOr<std::unique_ptr<File>> 362343171Sdim openFileForRead(const Twine &Path) override { 363343171Sdim return FS->openFileForRead(Path); 364343171Sdim } 365343171Sdim directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override { 366343171Sdim return FS->dir_begin(Dir, EC); 367343171Sdim } 368343171Sdim llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 369343171Sdim return FS->getCurrentWorkingDirectory(); 370343171Sdim } 371343171Sdim std::error_code setCurrentWorkingDirectory(const Twine &Path) override { 372343171Sdim return FS->setCurrentWorkingDirectory(Path); 373343171Sdim } 374343171Sdim std::error_code getRealPath(const Twine &Path, 375343171Sdim SmallVectorImpl<char> &Output) const override { 376343171Sdim return FS->getRealPath(Path, Output); 377343171Sdim } 378343171Sdim std::error_code isLocal(const Twine &Path, bool &Result) override { 379343171Sdim return FS->isLocal(Path, Result); 380343171Sdim } 381343171Sdim 382343171Sdimprotected: 383343171Sdim FileSystem &getUnderlyingFS() { return *FS; } 384343171Sdim 385343171Sdimprivate: 386343171Sdim IntrusiveRefCntPtr<FileSystem> FS; 387343171Sdim 388343171Sdim virtual void anchor(); 389343171Sdim}; 390343171Sdim 391343171Sdimnamespace detail { 392343171Sdim 393343171Sdimclass InMemoryDirectory; 394343171Sdimclass InMemoryFile; 395343171Sdim 396343171Sdim} // namespace detail 397343171Sdim 398343171Sdim/// An in-memory file system. 399343171Sdimclass InMemoryFileSystem : public FileSystem { 400343171Sdim std::unique_ptr<detail::InMemoryDirectory> Root; 401343171Sdim std::string WorkingDirectory; 402343171Sdim bool UseNormalizedPaths = true; 403343171Sdim 404343171Sdim /// If HardLinkTarget is non-null, a hardlink is created to the To path which 405343171Sdim /// must be a file. If it is null then it adds the file as the public addFile. 406343171Sdim bool addFile(const Twine &Path, time_t ModificationTime, 407343171Sdim std::unique_ptr<llvm::MemoryBuffer> Buffer, 408343171Sdim Optional<uint32_t> User, Optional<uint32_t> Group, 409343171Sdim Optional<llvm::sys::fs::file_type> Type, 410343171Sdim Optional<llvm::sys::fs::perms> Perms, 411343171Sdim const detail::InMemoryFile *HardLinkTarget); 412343171Sdim 413343171Sdimpublic: 414343171Sdim explicit InMemoryFileSystem(bool UseNormalizedPaths = true); 415343171Sdim ~InMemoryFileSystem() override; 416343171Sdim 417343171Sdim /// Add a file containing a buffer or a directory to the VFS with a 418343171Sdim /// path. The VFS owns the buffer. If present, User, Group, Type 419343171Sdim /// and Perms apply to the newly-created file or directory. 420343171Sdim /// \return true if the file or directory was successfully added, 421343171Sdim /// false if the file or directory already exists in the file system with 422343171Sdim /// different contents. 423343171Sdim bool addFile(const Twine &Path, time_t ModificationTime, 424343171Sdim std::unique_ptr<llvm::MemoryBuffer> Buffer, 425343171Sdim Optional<uint32_t> User = None, Optional<uint32_t> Group = None, 426343171Sdim Optional<llvm::sys::fs::file_type> Type = None, 427343171Sdim Optional<llvm::sys::fs::perms> Perms = None); 428343171Sdim 429343171Sdim /// Add a hard link to a file. 430343171Sdim /// Here hard links are not intended to be fully equivalent to the classical 431343171Sdim /// filesystem. Both the hard link and the file share the same buffer and 432343171Sdim /// status (and thus have the same UniqueID). Because of this there is no way 433343171Sdim /// to distinguish between the link and the file after the link has been 434343171Sdim /// added. 435343171Sdim /// 436343171Sdim /// The To path must be an existing file or a hardlink. The From file must not 437343171Sdim /// have been added before. The To Path must not be a directory. The From Node 438343171Sdim /// is added as a hard link which points to the resolved file of To Node. 439343171Sdim /// \return true if the above condition is satisfied and hardlink was 440343171Sdim /// successfully created, false otherwise. 441343171Sdim bool addHardLink(const Twine &From, const Twine &To); 442343171Sdim 443343171Sdim /// Add a buffer to the VFS with a path. The VFS does not own the buffer. 444343171Sdim /// If present, User, Group, Type and Perms apply to the newly-created file 445343171Sdim /// or directory. 446343171Sdim /// \return true if the file or directory was successfully added, 447343171Sdim /// false if the file or directory already exists in the file system with 448343171Sdim /// different contents. 449343171Sdim bool addFileNoOwn(const Twine &Path, time_t ModificationTime, 450343171Sdim llvm::MemoryBuffer *Buffer, Optional<uint32_t> User = None, 451343171Sdim Optional<uint32_t> Group = None, 452343171Sdim Optional<llvm::sys::fs::file_type> Type = None, 453343171Sdim Optional<llvm::sys::fs::perms> Perms = None); 454343171Sdim 455343171Sdim std::string toString() const; 456343171Sdim 457343171Sdim /// Return true if this file system normalizes . and .. in paths. 458343171Sdim bool useNormalizedPaths() const { return UseNormalizedPaths; } 459343171Sdim 460343171Sdim llvm::ErrorOr<Status> status(const Twine &Path) override; 461343171Sdim llvm::ErrorOr<std::unique_ptr<File>> 462343171Sdim openFileForRead(const Twine &Path) override; 463343171Sdim directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; 464343171Sdim 465343171Sdim llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 466343171Sdim return WorkingDirectory; 467343171Sdim } 468343171Sdim /// Canonicalizes \p Path by combining with the current working 469343171Sdim /// directory and normalizing the path (e.g. remove dots). If the current 470343171Sdim /// working directory is not set, this returns errc::operation_not_permitted. 471343171Sdim /// 472343171Sdim /// This doesn't resolve symlinks as they are not supported in in-memory file 473343171Sdim /// system. 474343171Sdim std::error_code getRealPath(const Twine &Path, 475343171Sdim SmallVectorImpl<char> &Output) const override; 476343171Sdim std::error_code isLocal(const Twine &Path, bool &Result) override; 477343171Sdim std::error_code setCurrentWorkingDirectory(const Twine &Path) override; 478343171Sdim}; 479343171Sdim 480343171Sdim/// Get a globally unique ID for a virtual file or directory. 481343171Sdimllvm::sys::fs::UniqueID getNextVirtualUniqueID(); 482343171Sdim 483343171Sdim/// Gets a \p FileSystem for a virtual file system described in YAML 484343171Sdim/// format. 485343171SdimIntrusiveRefCntPtr<FileSystem> 486343171SdimgetVFSFromYAML(std::unique_ptr<llvm::MemoryBuffer> Buffer, 487343171Sdim llvm::SourceMgr::DiagHandlerTy DiagHandler, 488343171Sdim StringRef YAMLFilePath, void *DiagContext = nullptr, 489343171Sdim IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem()); 490343171Sdim 491343171Sdimstruct YAMLVFSEntry { 492343171Sdim template <typename T1, typename T2> 493343171Sdim YAMLVFSEntry(T1 &&VPath, T2 &&RPath) 494343171Sdim : VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)) {} 495343171Sdim std::string VPath; 496343171Sdim std::string RPath; 497343171Sdim}; 498343171Sdim 499343171Sdimclass VFSFromYamlDirIterImpl; 500343171Sdimclass RedirectingFileSystemParser; 501343171Sdim 502343171Sdim/// A virtual file system parsed from a YAML file. 503343171Sdim/// 504343171Sdim/// Currently, this class allows creating virtual directories and mapping 505343171Sdim/// virtual file paths to existing external files, available in \c ExternalFS. 506343171Sdim/// 507343171Sdim/// The basic structure of the parsed file is: 508343171Sdim/// \verbatim 509343171Sdim/// { 510343171Sdim/// 'version': <version number>, 511343171Sdim/// <optional configuration> 512343171Sdim/// 'roots': [ 513343171Sdim/// <directory entries> 514343171Sdim/// ] 515343171Sdim/// } 516343171Sdim/// \endverbatim 517343171Sdim/// 518343171Sdim/// All configuration options are optional. 519343171Sdim/// 'case-sensitive': <boolean, default=true> 520343171Sdim/// 'use-external-names': <boolean, default=true> 521343171Sdim/// 'overlay-relative': <boolean, default=false> 522343171Sdim/// 'fallthrough': <boolean, default=true> 523343171Sdim/// 524343171Sdim/// Virtual directories are represented as 525343171Sdim/// \verbatim 526343171Sdim/// { 527343171Sdim/// 'type': 'directory', 528343171Sdim/// 'name': <string>, 529343171Sdim/// 'contents': [ <file or directory entries> ] 530343171Sdim/// } 531343171Sdim/// \endverbatim 532343171Sdim/// 533343171Sdim/// The default attributes for virtual directories are: 534343171Sdim/// \verbatim 535343171Sdim/// MTime = now() when created 536343171Sdim/// Perms = 0777 537343171Sdim/// User = Group = 0 538343171Sdim/// Size = 0 539343171Sdim/// UniqueID = unspecified unique value 540343171Sdim/// \endverbatim 541343171Sdim/// 542343171Sdim/// Re-mapped files are represented as 543343171Sdim/// \verbatim 544343171Sdim/// { 545343171Sdim/// 'type': 'file', 546343171Sdim/// 'name': <string>, 547343171Sdim/// 'use-external-name': <boolean> # Optional 548343171Sdim/// 'external-contents': <path to external file> 549343171Sdim/// } 550343171Sdim/// \endverbatim 551343171Sdim/// 552343171Sdim/// and inherit their attributes from the external contents. 553343171Sdim/// 554343171Sdim/// In both cases, the 'name' field may contain multiple path components (e.g. 555343171Sdim/// /path/to/file). However, any directory that contains more than one child 556343171Sdim/// must be uniquely represented by a directory entry. 557343171Sdimclass RedirectingFileSystem : public vfs::FileSystem { 558343171Sdimpublic: 559343171Sdim enum EntryKind { EK_Directory, EK_File }; 560343171Sdim 561343171Sdim /// A single file or directory in the VFS. 562343171Sdim class Entry { 563343171Sdim EntryKind Kind; 564343171Sdim std::string Name; 565343171Sdim 566343171Sdim public: 567343171Sdim Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {} 568343171Sdim virtual ~Entry() = default; 569343171Sdim 570343171Sdim StringRef getName() const { return Name; } 571343171Sdim EntryKind getKind() const { return Kind; } 572343171Sdim }; 573343171Sdim 574343171Sdim class RedirectingDirectoryEntry : public Entry { 575343171Sdim std::vector<std::unique_ptr<Entry>> Contents; 576343171Sdim Status S; 577343171Sdim 578343171Sdim public: 579343171Sdim RedirectingDirectoryEntry(StringRef Name, 580343171Sdim std::vector<std::unique_ptr<Entry>> Contents, 581343171Sdim Status S) 582343171Sdim : Entry(EK_Directory, Name), Contents(std::move(Contents)), 583343171Sdim S(std::move(S)) {} 584343171Sdim RedirectingDirectoryEntry(StringRef Name, Status S) 585343171Sdim : Entry(EK_Directory, Name), S(std::move(S)) {} 586343171Sdim 587343171Sdim Status getStatus() { return S; } 588343171Sdim 589343171Sdim void addContent(std::unique_ptr<Entry> Content) { 590343171Sdim Contents.push_back(std::move(Content)); 591343171Sdim } 592343171Sdim 593343171Sdim Entry *getLastContent() const { return Contents.back().get(); } 594343171Sdim 595343171Sdim using iterator = decltype(Contents)::iterator; 596343171Sdim 597343171Sdim iterator contents_begin() { return Contents.begin(); } 598343171Sdim iterator contents_end() { return Contents.end(); } 599343171Sdim 600343171Sdim static bool classof(const Entry *E) { return E->getKind() == EK_Directory; } 601343171Sdim }; 602343171Sdim 603343171Sdim class RedirectingFileEntry : public Entry { 604343171Sdim public: 605343171Sdim enum NameKind { NK_NotSet, NK_External, NK_Virtual }; 606343171Sdim 607343171Sdim private: 608343171Sdim std::string ExternalContentsPath; 609343171Sdim NameKind UseName; 610343171Sdim 611343171Sdim public: 612343171Sdim RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath, 613343171Sdim NameKind UseName) 614343171Sdim : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath), 615343171Sdim UseName(UseName) {} 616343171Sdim 617343171Sdim StringRef getExternalContentsPath() const { return ExternalContentsPath; } 618343171Sdim 619343171Sdim /// whether to use the external path as the name for this file. 620343171Sdim bool useExternalName(bool GlobalUseExternalName) const { 621343171Sdim return UseName == NK_NotSet ? GlobalUseExternalName 622343171Sdim : (UseName == NK_External); 623343171Sdim } 624343171Sdim 625343171Sdim NameKind getUseName() const { return UseName; } 626343171Sdim 627343171Sdim static bool classof(const Entry *E) { return E->getKind() == EK_File; } 628343171Sdim }; 629343171Sdim 630343171Sdimprivate: 631343171Sdim friend class VFSFromYamlDirIterImpl; 632343171Sdim friend class RedirectingFileSystemParser; 633343171Sdim 634343171Sdim /// The root(s) of the virtual file system. 635343171Sdim std::vector<std::unique_ptr<Entry>> Roots; 636343171Sdim 637343171Sdim /// The file system to use for external references. 638343171Sdim IntrusiveRefCntPtr<FileSystem> ExternalFS; 639343171Sdim 640343171Sdim /// If IsRelativeOverlay is set, this represents the directory 641343171Sdim /// path that should be prefixed to each 'external-contents' entry 642343171Sdim /// when reading from YAML files. 643343171Sdim std::string ExternalContentsPrefixDir; 644343171Sdim 645343171Sdim /// @name Configuration 646343171Sdim /// @{ 647343171Sdim 648343171Sdim /// Whether to perform case-sensitive comparisons. 649343171Sdim /// 650343171Sdim /// Currently, case-insensitive matching only works correctly with ASCII. 651343171Sdim bool CaseSensitive = true; 652343171Sdim 653343171Sdim /// IsRelativeOverlay marks whether a ExternalContentsPrefixDir path must 654343171Sdim /// be prefixed in every 'external-contents' when reading from YAML files. 655343171Sdim bool IsRelativeOverlay = false; 656343171Sdim 657343171Sdim /// Whether to use to use the value of 'external-contents' for the 658343171Sdim /// names of files. This global value is overridable on a per-file basis. 659343171Sdim bool UseExternalNames = true; 660343171Sdim 661343171Sdim /// Whether to attempt a file lookup in external file system after it wasn't 662343171Sdim /// found in VFS. 663343171Sdim bool IsFallthrough = true; 664343171Sdim /// @} 665343171Sdim 666343171Sdim /// Virtual file paths and external files could be canonicalized without "..", 667343171Sdim /// "." and "./" in their paths. FIXME: some unittests currently fail on 668343171Sdim /// win32 when using remove_dots and remove_leading_dotslash on paths. 669343171Sdim bool UseCanonicalizedPaths = 670343171Sdim#ifdef _WIN32 671343171Sdim false; 672343171Sdim#else 673343171Sdim true; 674343171Sdim#endif 675343171Sdim 676343171Sdim RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS) 677343171Sdim : ExternalFS(std::move(ExternalFS)) {} 678343171Sdim 679343171Sdim /// Looks up the path <tt>[Start, End)</tt> in \p From, possibly 680343171Sdim /// recursing into the contents of \p From if it is a directory. 681343171Sdim ErrorOr<Entry *> lookupPath(llvm::sys::path::const_iterator Start, 682343171Sdim llvm::sys::path::const_iterator End, 683343171Sdim Entry *From) const; 684343171Sdim 685343171Sdim /// Get the status of a given an \c Entry. 686343171Sdim ErrorOr<Status> status(const Twine &Path, Entry *E); 687343171Sdim 688343171Sdimpublic: 689343171Sdim /// Looks up \p Path in \c Roots. 690343171Sdim ErrorOr<Entry *> lookupPath(const Twine &Path) const; 691343171Sdim 692343171Sdim /// Parses \p Buffer, which is expected to be in YAML format and 693343171Sdim /// returns a virtual file system representing its contents. 694343171Sdim static RedirectingFileSystem * 695343171Sdim create(std::unique_ptr<MemoryBuffer> Buffer, 696343171Sdim SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, 697343171Sdim void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS); 698343171Sdim 699343171Sdim ErrorOr<Status> status(const Twine &Path) override; 700343171Sdim ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override; 701343171Sdim 702343171Sdim std::error_code getRealPath(const Twine &Path, 703343171Sdim SmallVectorImpl<char> &Output) const override; 704343171Sdim 705343171Sdim llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; 706343171Sdim 707343171Sdim std::error_code setCurrentWorkingDirectory(const Twine &Path) override; 708343171Sdim 709343171Sdim std::error_code isLocal(const Twine &Path, bool &Result) override; 710343171Sdim 711343171Sdim directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; 712343171Sdim 713343171Sdim void setExternalContentsPrefixDir(StringRef PrefixDir); 714343171Sdim 715343171Sdim StringRef getExternalContentsPrefixDir() const; 716343171Sdim 717343171Sdim#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 718343171Sdim LLVM_DUMP_METHOD void dump() const; 719343171Sdim LLVM_DUMP_METHOD void dumpEntry(Entry *E, int NumSpaces = 0) const; 720343171Sdim#endif 721343171Sdim}; 722343171Sdim 723343171Sdim/// Collect all pairs of <virtual path, real path> entries from the 724343171Sdim/// \p YAMLFilePath. This is used by the module dependency collector to forward 725343171Sdim/// the entries into the reproducer output VFS YAML file. 726343171Sdimvoid collectVFSFromYAML( 727343171Sdim std::unique_ptr<llvm::MemoryBuffer> Buffer, 728343171Sdim llvm::SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, 729343171Sdim SmallVectorImpl<YAMLVFSEntry> &CollectedEntries, 730343171Sdim void *DiagContext = nullptr, 731343171Sdim IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem()); 732343171Sdim 733343171Sdimclass YAMLVFSWriter { 734343171Sdim std::vector<YAMLVFSEntry> Mappings; 735343171Sdim Optional<bool> IsCaseSensitive; 736343171Sdim Optional<bool> IsOverlayRelative; 737343171Sdim Optional<bool> UseExternalNames; 738343171Sdim std::string OverlayDir; 739343171Sdim 740343171Sdimpublic: 741343171Sdim YAMLVFSWriter() = default; 742343171Sdim 743343171Sdim void addFileMapping(StringRef VirtualPath, StringRef RealPath); 744343171Sdim 745343171Sdim void setCaseSensitivity(bool CaseSensitive) { 746343171Sdim IsCaseSensitive = CaseSensitive; 747343171Sdim } 748343171Sdim 749343171Sdim void setUseExternalNames(bool UseExtNames) { UseExternalNames = UseExtNames; } 750343171Sdim 751343171Sdim void setOverlayDir(StringRef OverlayDirectory) { 752343171Sdim IsOverlayRelative = true; 753343171Sdim OverlayDir.assign(OverlayDirectory.str()); 754343171Sdim } 755343171Sdim 756343171Sdim const std::vector<YAMLVFSEntry> &getMappings() const { return Mappings; } 757343171Sdim 758343171Sdim void write(llvm::raw_ostream &OS); 759343171Sdim}; 760343171Sdim 761343171Sdim} // namespace vfs 762343171Sdim} // namespace llvm 763343171Sdim 764343171Sdim#endif // LLVM_SUPPORT_VIRTUALFILESYSTEM_H 765