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