106f32e7eSjoerg //===- VirtualFileSystem.h - Virtual File System Layer ----------*- C++ -*-===// 206f32e7eSjoerg // 306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information. 506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606f32e7eSjoerg // 706f32e7eSjoerg //===----------------------------------------------------------------------===// 806f32e7eSjoerg // 906f32e7eSjoerg /// \file 1006f32e7eSjoerg /// Defines the virtual file system interface vfs::FileSystem. 1106f32e7eSjoerg // 1206f32e7eSjoerg //===----------------------------------------------------------------------===// 1306f32e7eSjoerg 1406f32e7eSjoerg #ifndef LLVM_SUPPORT_VIRTUALFILESYSTEM_H 1506f32e7eSjoerg #define LLVM_SUPPORT_VIRTUALFILESYSTEM_H 1606f32e7eSjoerg 1706f32e7eSjoerg #include "llvm/ADT/IntrusiveRefCntPtr.h" 1806f32e7eSjoerg #include "llvm/ADT/None.h" 1906f32e7eSjoerg #include "llvm/ADT/Optional.h" 2006f32e7eSjoerg #include "llvm/ADT/SmallVector.h" 2106f32e7eSjoerg #include "llvm/ADT/StringRef.h" 2206f32e7eSjoerg #include "llvm/Support/Chrono.h" 2306f32e7eSjoerg #include "llvm/Support/ErrorOr.h" 2406f32e7eSjoerg #include "llvm/Support/FileSystem.h" 2506f32e7eSjoerg #include "llvm/Support/Path.h" 2606f32e7eSjoerg #include "llvm/Support/SourceMgr.h" 2706f32e7eSjoerg #include <cassert> 2806f32e7eSjoerg #include <cstdint> 2906f32e7eSjoerg #include <ctime> 3006f32e7eSjoerg #include <memory> 3106f32e7eSjoerg #include <stack> 3206f32e7eSjoerg #include <string> 3306f32e7eSjoerg #include <system_error> 3406f32e7eSjoerg #include <utility> 3506f32e7eSjoerg #include <vector> 3606f32e7eSjoerg 3706f32e7eSjoerg namespace llvm { 3806f32e7eSjoerg 3906f32e7eSjoerg class MemoryBuffer; 40*da58b97aSjoerg class MemoryBufferRef; 41*da58b97aSjoerg class Twine; 4206f32e7eSjoerg 4306f32e7eSjoerg namespace vfs { 4406f32e7eSjoerg 4506f32e7eSjoerg /// The result of a \p status operation. 4606f32e7eSjoerg class Status { 4706f32e7eSjoerg std::string Name; 4806f32e7eSjoerg llvm::sys::fs::UniqueID UID; 4906f32e7eSjoerg llvm::sys::TimePoint<> MTime; 5006f32e7eSjoerg uint32_t User; 5106f32e7eSjoerg uint32_t Group; 5206f32e7eSjoerg uint64_t Size; 5306f32e7eSjoerg llvm::sys::fs::file_type Type = llvm::sys::fs::file_type::status_error; 5406f32e7eSjoerg llvm::sys::fs::perms Perms; 5506f32e7eSjoerg 5606f32e7eSjoerg public: 5706f32e7eSjoerg // FIXME: remove when files support multiple names 5806f32e7eSjoerg bool IsVFSMapped = false; 5906f32e7eSjoerg 6006f32e7eSjoerg Status() = default; 6106f32e7eSjoerg Status(const llvm::sys::fs::file_status &Status); 6206f32e7eSjoerg Status(const Twine &Name, llvm::sys::fs::UniqueID UID, 6306f32e7eSjoerg llvm::sys::TimePoint<> MTime, uint32_t User, uint32_t Group, 6406f32e7eSjoerg uint64_t Size, llvm::sys::fs::file_type Type, 6506f32e7eSjoerg llvm::sys::fs::perms Perms); 6606f32e7eSjoerg 6706f32e7eSjoerg /// Get a copy of a Status with a different name. 6806f32e7eSjoerg static Status copyWithNewName(const Status &In, const Twine &NewName); 6906f32e7eSjoerg static Status copyWithNewName(const llvm::sys::fs::file_status &In, 7006f32e7eSjoerg const Twine &NewName); 7106f32e7eSjoerg 7206f32e7eSjoerg /// Returns the name that should be used for this file or directory. getName()7306f32e7eSjoerg StringRef getName() const { return Name; } 7406f32e7eSjoerg 7506f32e7eSjoerg /// @name Status interface from llvm::sys::fs 7606f32e7eSjoerg /// @{ getType()7706f32e7eSjoerg llvm::sys::fs::file_type getType() const { return Type; } getPermissions()7806f32e7eSjoerg llvm::sys::fs::perms getPermissions() const { return Perms; } getLastModificationTime()7906f32e7eSjoerg llvm::sys::TimePoint<> getLastModificationTime() const { return MTime; } getUniqueID()8006f32e7eSjoerg llvm::sys::fs::UniqueID getUniqueID() const { return UID; } getUser()8106f32e7eSjoerg uint32_t getUser() const { return User; } getGroup()8206f32e7eSjoerg uint32_t getGroup() const { return Group; } getSize()8306f32e7eSjoerg uint64_t getSize() const { return Size; } 8406f32e7eSjoerg /// @} 8506f32e7eSjoerg /// @name Status queries 8606f32e7eSjoerg /// These are static queries in llvm::sys::fs. 8706f32e7eSjoerg /// @{ 8806f32e7eSjoerg bool equivalent(const Status &Other) const; 8906f32e7eSjoerg bool isDirectory() const; 9006f32e7eSjoerg bool isRegularFile() const; 9106f32e7eSjoerg bool isOther() const; 9206f32e7eSjoerg bool isSymlink() const; 9306f32e7eSjoerg bool isStatusKnown() const; 9406f32e7eSjoerg bool exists() const; 9506f32e7eSjoerg /// @} 9606f32e7eSjoerg }; 9706f32e7eSjoerg 9806f32e7eSjoerg /// Represents an open file. 9906f32e7eSjoerg class File { 10006f32e7eSjoerg public: 10106f32e7eSjoerg /// Destroy the file after closing it (if open). 10206f32e7eSjoerg /// Sub-classes should generally call close() inside their destructors. We 10306f32e7eSjoerg /// cannot do that from the base class, since close is virtual. 10406f32e7eSjoerg virtual ~File(); 10506f32e7eSjoerg 10606f32e7eSjoerg /// Get the status of the file. 10706f32e7eSjoerg virtual llvm::ErrorOr<Status> status() = 0; 10806f32e7eSjoerg 10906f32e7eSjoerg /// Get the name of the file getName()11006f32e7eSjoerg virtual llvm::ErrorOr<std::string> getName() { 11106f32e7eSjoerg if (auto Status = status()) 11206f32e7eSjoerg return Status->getName().str(); 11306f32e7eSjoerg else 11406f32e7eSjoerg return Status.getError(); 11506f32e7eSjoerg } 11606f32e7eSjoerg 11706f32e7eSjoerg /// Get the contents of the file as a \p MemoryBuffer. 11806f32e7eSjoerg virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 11906f32e7eSjoerg getBuffer(const Twine &Name, int64_t FileSize = -1, 12006f32e7eSjoerg bool RequiresNullTerminator = true, bool IsVolatile = false) = 0; 12106f32e7eSjoerg 12206f32e7eSjoerg /// Closes the file. 12306f32e7eSjoerg virtual std::error_code close() = 0; 12406f32e7eSjoerg }; 12506f32e7eSjoerg 12606f32e7eSjoerg /// A member of a directory, yielded by a directory_iterator. 12706f32e7eSjoerg /// Only information available on most platforms is included. 12806f32e7eSjoerg class directory_entry { 12906f32e7eSjoerg std::string Path; 130*da58b97aSjoerg llvm::sys::fs::file_type Type = llvm::sys::fs::file_type::type_unknown; 13106f32e7eSjoerg 13206f32e7eSjoerg public: 13306f32e7eSjoerg directory_entry() = default; directory_entry(std::string Path,llvm::sys::fs::file_type Type)13406f32e7eSjoerg directory_entry(std::string Path, llvm::sys::fs::file_type Type) 13506f32e7eSjoerg : Path(std::move(Path)), Type(Type) {} 13606f32e7eSjoerg path()13706f32e7eSjoerg llvm::StringRef path() const { return Path; } type()13806f32e7eSjoerg llvm::sys::fs::file_type type() const { return Type; } 13906f32e7eSjoerg }; 14006f32e7eSjoerg 14106f32e7eSjoerg namespace detail { 14206f32e7eSjoerg 14306f32e7eSjoerg /// An interface for virtual file systems to provide an iterator over the 14406f32e7eSjoerg /// (non-recursive) contents of a directory. 14506f32e7eSjoerg struct DirIterImpl { 14606f32e7eSjoerg virtual ~DirIterImpl(); 14706f32e7eSjoerg 14806f32e7eSjoerg /// Sets \c CurrentEntry to the next entry in the directory on success, 14906f32e7eSjoerg /// to directory_entry() at end, or returns a system-defined \c error_code. 15006f32e7eSjoerg virtual std::error_code increment() = 0; 15106f32e7eSjoerg 15206f32e7eSjoerg directory_entry CurrentEntry; 15306f32e7eSjoerg }; 15406f32e7eSjoerg 15506f32e7eSjoerg } // namespace detail 15606f32e7eSjoerg 15706f32e7eSjoerg /// An input iterator over the entries in a virtual path, similar to 15806f32e7eSjoerg /// llvm::sys::fs::directory_iterator. 15906f32e7eSjoerg class directory_iterator { 16006f32e7eSjoerg std::shared_ptr<detail::DirIterImpl> Impl; // Input iterator semantics on copy 16106f32e7eSjoerg 16206f32e7eSjoerg public: directory_iterator(std::shared_ptr<detail::DirIterImpl> I)16306f32e7eSjoerg directory_iterator(std::shared_ptr<detail::DirIterImpl> I) 16406f32e7eSjoerg : Impl(std::move(I)) { 16506f32e7eSjoerg assert(Impl.get() != nullptr && "requires non-null implementation"); 16606f32e7eSjoerg if (Impl->CurrentEntry.path().empty()) 16706f32e7eSjoerg Impl.reset(); // Normalize the end iterator to Impl == nullptr. 16806f32e7eSjoerg } 16906f32e7eSjoerg 17006f32e7eSjoerg /// Construct an 'end' iterator. 17106f32e7eSjoerg directory_iterator() = default; 17206f32e7eSjoerg 17306f32e7eSjoerg /// Equivalent to operator++, with an error code. increment(std::error_code & EC)17406f32e7eSjoerg directory_iterator &increment(std::error_code &EC) { 17506f32e7eSjoerg assert(Impl && "attempting to increment past end"); 17606f32e7eSjoerg EC = Impl->increment(); 17706f32e7eSjoerg if (Impl->CurrentEntry.path().empty()) 17806f32e7eSjoerg Impl.reset(); // Normalize the end iterator to Impl == nullptr. 17906f32e7eSjoerg return *this; 18006f32e7eSjoerg } 18106f32e7eSjoerg 18206f32e7eSjoerg const directory_entry &operator*() const { return Impl->CurrentEntry; } 18306f32e7eSjoerg const directory_entry *operator->() const { return &Impl->CurrentEntry; } 18406f32e7eSjoerg 18506f32e7eSjoerg bool operator==(const directory_iterator &RHS) const { 18606f32e7eSjoerg if (Impl && RHS.Impl) 18706f32e7eSjoerg return Impl->CurrentEntry.path() == RHS.Impl->CurrentEntry.path(); 18806f32e7eSjoerg return !Impl && !RHS.Impl; 18906f32e7eSjoerg } 19006f32e7eSjoerg bool operator!=(const directory_iterator &RHS) const { 19106f32e7eSjoerg return !(*this == RHS); 19206f32e7eSjoerg } 19306f32e7eSjoerg }; 19406f32e7eSjoerg 19506f32e7eSjoerg class FileSystem; 19606f32e7eSjoerg 19706f32e7eSjoerg namespace detail { 19806f32e7eSjoerg 19906f32e7eSjoerg /// Keeps state for the recursive_directory_iterator. 20006f32e7eSjoerg struct RecDirIterState { 20106f32e7eSjoerg std::stack<directory_iterator, std::vector<directory_iterator>> Stack; 20206f32e7eSjoerg bool HasNoPushRequest = false; 20306f32e7eSjoerg }; 20406f32e7eSjoerg 20506f32e7eSjoerg } // end namespace detail 20606f32e7eSjoerg 20706f32e7eSjoerg /// An input iterator over the recursive contents of a virtual path, 20806f32e7eSjoerg /// similar to llvm::sys::fs::recursive_directory_iterator. 20906f32e7eSjoerg class recursive_directory_iterator { 21006f32e7eSjoerg FileSystem *FS; 21106f32e7eSjoerg std::shared_ptr<detail::RecDirIterState> 21206f32e7eSjoerg State; // Input iterator semantics on copy. 21306f32e7eSjoerg 21406f32e7eSjoerg public: 21506f32e7eSjoerg recursive_directory_iterator(FileSystem &FS, const Twine &Path, 21606f32e7eSjoerg std::error_code &EC); 21706f32e7eSjoerg 21806f32e7eSjoerg /// Construct an 'end' iterator. 21906f32e7eSjoerg recursive_directory_iterator() = default; 22006f32e7eSjoerg 22106f32e7eSjoerg /// Equivalent to operator++, with an error code. 22206f32e7eSjoerg recursive_directory_iterator &increment(std::error_code &EC); 22306f32e7eSjoerg 22406f32e7eSjoerg const directory_entry &operator*() const { return *State->Stack.top(); } 22506f32e7eSjoerg const directory_entry *operator->() const { return &*State->Stack.top(); } 22606f32e7eSjoerg 22706f32e7eSjoerg bool operator==(const recursive_directory_iterator &Other) const { 22806f32e7eSjoerg return State == Other.State; // identity 22906f32e7eSjoerg } 23006f32e7eSjoerg bool operator!=(const recursive_directory_iterator &RHS) const { 23106f32e7eSjoerg return !(*this == RHS); 23206f32e7eSjoerg } 23306f32e7eSjoerg 23406f32e7eSjoerg /// Gets the current level. Starting path is at level 0. level()23506f32e7eSjoerg int level() const { 23606f32e7eSjoerg assert(!State->Stack.empty() && 23706f32e7eSjoerg "Cannot get level without any iteration state"); 23806f32e7eSjoerg return State->Stack.size() - 1; 23906f32e7eSjoerg } 24006f32e7eSjoerg no_push()24106f32e7eSjoerg void no_push() { State->HasNoPushRequest = true; } 24206f32e7eSjoerg }; 24306f32e7eSjoerg 24406f32e7eSjoerg /// The virtual file system interface. 24506f32e7eSjoerg class FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem> { 24606f32e7eSjoerg public: 24706f32e7eSjoerg virtual ~FileSystem(); 24806f32e7eSjoerg 24906f32e7eSjoerg /// Get the status of the entry at \p Path, if one exists. 25006f32e7eSjoerg virtual llvm::ErrorOr<Status> status(const Twine &Path) = 0; 25106f32e7eSjoerg 25206f32e7eSjoerg /// Get a \p File object for the file at \p Path, if one exists. 25306f32e7eSjoerg virtual llvm::ErrorOr<std::unique_ptr<File>> 25406f32e7eSjoerg openFileForRead(const Twine &Path) = 0; 25506f32e7eSjoerg 25606f32e7eSjoerg /// This is a convenience method that opens a file, gets its content and then 25706f32e7eSjoerg /// closes the file. 25806f32e7eSjoerg llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 25906f32e7eSjoerg getBufferForFile(const Twine &Name, int64_t FileSize = -1, 26006f32e7eSjoerg bool RequiresNullTerminator = true, bool IsVolatile = false); 26106f32e7eSjoerg 26206f32e7eSjoerg /// Get a directory_iterator for \p Dir. 26306f32e7eSjoerg /// \note The 'end' iterator is directory_iterator(). 26406f32e7eSjoerg virtual directory_iterator dir_begin(const Twine &Dir, 26506f32e7eSjoerg std::error_code &EC) = 0; 26606f32e7eSjoerg 26706f32e7eSjoerg /// Set the working directory. This will affect all following operations on 26806f32e7eSjoerg /// this file system and may propagate down for nested file systems. 26906f32e7eSjoerg virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) = 0; 27006f32e7eSjoerg 27106f32e7eSjoerg /// Get the working directory of this file system. 27206f32e7eSjoerg virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const = 0; 27306f32e7eSjoerg 27406f32e7eSjoerg /// Gets real path of \p Path e.g. collapse all . and .. patterns, resolve 27506f32e7eSjoerg /// symlinks. For real file system, this uses `llvm::sys::fs::real_path`. 27606f32e7eSjoerg /// This returns errc::operation_not_permitted if not implemented by subclass. 27706f32e7eSjoerg virtual std::error_code getRealPath(const Twine &Path, 27806f32e7eSjoerg SmallVectorImpl<char> &Output) const; 27906f32e7eSjoerg 28006f32e7eSjoerg /// Check whether a file exists. Provided for convenience. 28106f32e7eSjoerg bool exists(const Twine &Path); 28206f32e7eSjoerg 28306f32e7eSjoerg /// Is the file mounted on a local filesystem? 28406f32e7eSjoerg virtual std::error_code isLocal(const Twine &Path, bool &Result); 28506f32e7eSjoerg 28606f32e7eSjoerg /// Make \a Path an absolute path. 28706f32e7eSjoerg /// 28806f32e7eSjoerg /// Makes \a Path absolute using the current directory if it is not already. 28906f32e7eSjoerg /// An empty \a Path will result in the current directory. 29006f32e7eSjoerg /// 29106f32e7eSjoerg /// /absolute/path => /absolute/path 29206f32e7eSjoerg /// relative/../path => <current-directory>/relative/../path 29306f32e7eSjoerg /// 29406f32e7eSjoerg /// \param Path A path that is modified to be an absolute path. 29506f32e7eSjoerg /// \returns success if \a path has been made absolute, otherwise a 29606f32e7eSjoerg /// platform-specific error_code. 297*da58b97aSjoerg virtual std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const; 29806f32e7eSjoerg }; 29906f32e7eSjoerg 30006f32e7eSjoerg /// Gets an \p vfs::FileSystem for the 'real' file system, as seen by 30106f32e7eSjoerg /// the operating system. 30206f32e7eSjoerg /// The working directory is linked to the process's working directory. 30306f32e7eSjoerg /// (This is usually thread-hostile). 30406f32e7eSjoerg IntrusiveRefCntPtr<FileSystem> getRealFileSystem(); 30506f32e7eSjoerg 30606f32e7eSjoerg /// Create an \p vfs::FileSystem for the 'real' file system, as seen by 30706f32e7eSjoerg /// the operating system. 30806f32e7eSjoerg /// It has its own working directory, independent of (but initially equal to) 30906f32e7eSjoerg /// that of the process. 31006f32e7eSjoerg std::unique_ptr<FileSystem> createPhysicalFileSystem(); 31106f32e7eSjoerg 31206f32e7eSjoerg /// A file system that allows overlaying one \p AbstractFileSystem on top 31306f32e7eSjoerg /// of another. 31406f32e7eSjoerg /// 31506f32e7eSjoerg /// Consists of a stack of >=1 \p FileSystem objects, which are treated as being 31606f32e7eSjoerg /// one merged file system. When there is a directory that exists in more than 31706f32e7eSjoerg /// one file system, the \p OverlayFileSystem contains a directory containing 31806f32e7eSjoerg /// the union of their contents. The attributes (permissions, etc.) of the 31906f32e7eSjoerg /// top-most (most recently added) directory are used. When there is a file 32006f32e7eSjoerg /// that exists in more than one file system, the file in the top-most file 32106f32e7eSjoerg /// system overrides the other(s). 32206f32e7eSjoerg class OverlayFileSystem : public FileSystem { 32306f32e7eSjoerg using FileSystemList = SmallVector<IntrusiveRefCntPtr<FileSystem>, 1>; 32406f32e7eSjoerg 32506f32e7eSjoerg /// The stack of file systems, implemented as a list in order of 32606f32e7eSjoerg /// their addition. 32706f32e7eSjoerg FileSystemList FSList; 32806f32e7eSjoerg 32906f32e7eSjoerg public: 33006f32e7eSjoerg OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> Base); 33106f32e7eSjoerg 33206f32e7eSjoerg /// Pushes a file system on top of the stack. 33306f32e7eSjoerg void pushOverlay(IntrusiveRefCntPtr<FileSystem> FS); 33406f32e7eSjoerg 33506f32e7eSjoerg llvm::ErrorOr<Status> status(const Twine &Path) override; 33606f32e7eSjoerg llvm::ErrorOr<std::unique_ptr<File>> 33706f32e7eSjoerg openFileForRead(const Twine &Path) override; 33806f32e7eSjoerg directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; 33906f32e7eSjoerg llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; 34006f32e7eSjoerg std::error_code setCurrentWorkingDirectory(const Twine &Path) override; 34106f32e7eSjoerg std::error_code isLocal(const Twine &Path, bool &Result) override; 34206f32e7eSjoerg std::error_code getRealPath(const Twine &Path, 34306f32e7eSjoerg SmallVectorImpl<char> &Output) const override; 34406f32e7eSjoerg 34506f32e7eSjoerg using iterator = FileSystemList::reverse_iterator; 34606f32e7eSjoerg using const_iterator = FileSystemList::const_reverse_iterator; 34706f32e7eSjoerg using reverse_iterator = FileSystemList::iterator; 34806f32e7eSjoerg using const_reverse_iterator = FileSystemList::const_iterator; 34906f32e7eSjoerg 35006f32e7eSjoerg /// Get an iterator pointing to the most recently added file system. overlays_begin()35106f32e7eSjoerg iterator overlays_begin() { return FSList.rbegin(); } overlays_begin()35206f32e7eSjoerg const_iterator overlays_begin() const { return FSList.rbegin(); } 35306f32e7eSjoerg 35406f32e7eSjoerg /// Get an iterator pointing one-past the least recently added file system. overlays_end()35506f32e7eSjoerg iterator overlays_end() { return FSList.rend(); } overlays_end()35606f32e7eSjoerg const_iterator overlays_end() const { return FSList.rend(); } 35706f32e7eSjoerg 35806f32e7eSjoerg /// Get an iterator pointing to the least recently added file system. overlays_rbegin()35906f32e7eSjoerg reverse_iterator overlays_rbegin() { return FSList.begin(); } overlays_rbegin()36006f32e7eSjoerg const_reverse_iterator overlays_rbegin() const { return FSList.begin(); } 36106f32e7eSjoerg 36206f32e7eSjoerg /// Get an iterator pointing one-past the most recently added file system. overlays_rend()36306f32e7eSjoerg reverse_iterator overlays_rend() { return FSList.end(); } overlays_rend()36406f32e7eSjoerg const_reverse_iterator overlays_rend() const { return FSList.end(); } 36506f32e7eSjoerg }; 36606f32e7eSjoerg 36706f32e7eSjoerg /// By default, this delegates all calls to the underlying file system. This 36806f32e7eSjoerg /// is useful when derived file systems want to override some calls and still 36906f32e7eSjoerg /// proxy other calls. 37006f32e7eSjoerg class ProxyFileSystem : public FileSystem { 37106f32e7eSjoerg public: ProxyFileSystem(IntrusiveRefCntPtr<FileSystem> FS)37206f32e7eSjoerg explicit ProxyFileSystem(IntrusiveRefCntPtr<FileSystem> FS) 37306f32e7eSjoerg : FS(std::move(FS)) {} 37406f32e7eSjoerg status(const Twine & Path)37506f32e7eSjoerg llvm::ErrorOr<Status> status(const Twine &Path) override { 37606f32e7eSjoerg return FS->status(Path); 37706f32e7eSjoerg } 37806f32e7eSjoerg llvm::ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine & Path)37906f32e7eSjoerg openFileForRead(const Twine &Path) override { 38006f32e7eSjoerg return FS->openFileForRead(Path); 38106f32e7eSjoerg } dir_begin(const Twine & Dir,std::error_code & EC)38206f32e7eSjoerg directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override { 38306f32e7eSjoerg return FS->dir_begin(Dir, EC); 38406f32e7eSjoerg } getCurrentWorkingDirectory()38506f32e7eSjoerg llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 38606f32e7eSjoerg return FS->getCurrentWorkingDirectory(); 38706f32e7eSjoerg } setCurrentWorkingDirectory(const Twine & Path)38806f32e7eSjoerg std::error_code setCurrentWorkingDirectory(const Twine &Path) override { 38906f32e7eSjoerg return FS->setCurrentWorkingDirectory(Path); 39006f32e7eSjoerg } getRealPath(const Twine & Path,SmallVectorImpl<char> & Output)39106f32e7eSjoerg std::error_code getRealPath(const Twine &Path, 39206f32e7eSjoerg SmallVectorImpl<char> &Output) const override { 39306f32e7eSjoerg return FS->getRealPath(Path, Output); 39406f32e7eSjoerg } isLocal(const Twine & Path,bool & Result)39506f32e7eSjoerg std::error_code isLocal(const Twine &Path, bool &Result) override { 39606f32e7eSjoerg return FS->isLocal(Path, Result); 39706f32e7eSjoerg } 39806f32e7eSjoerg 39906f32e7eSjoerg protected: getUnderlyingFS()40006f32e7eSjoerg FileSystem &getUnderlyingFS() { return *FS; } 40106f32e7eSjoerg 40206f32e7eSjoerg private: 40306f32e7eSjoerg IntrusiveRefCntPtr<FileSystem> FS; 40406f32e7eSjoerg 40506f32e7eSjoerg virtual void anchor(); 40606f32e7eSjoerg }; 40706f32e7eSjoerg 40806f32e7eSjoerg namespace detail { 40906f32e7eSjoerg 41006f32e7eSjoerg class InMemoryDirectory; 41106f32e7eSjoerg class InMemoryFile; 41206f32e7eSjoerg 41306f32e7eSjoerg } // namespace detail 41406f32e7eSjoerg 41506f32e7eSjoerg /// An in-memory file system. 41606f32e7eSjoerg class InMemoryFileSystem : public FileSystem { 41706f32e7eSjoerg std::unique_ptr<detail::InMemoryDirectory> Root; 41806f32e7eSjoerg std::string WorkingDirectory; 41906f32e7eSjoerg bool UseNormalizedPaths = true; 42006f32e7eSjoerg 42106f32e7eSjoerg /// If HardLinkTarget is non-null, a hardlink is created to the To path which 42206f32e7eSjoerg /// must be a file. If it is null then it adds the file as the public addFile. 42306f32e7eSjoerg bool addFile(const Twine &Path, time_t ModificationTime, 42406f32e7eSjoerg std::unique_ptr<llvm::MemoryBuffer> Buffer, 42506f32e7eSjoerg Optional<uint32_t> User, Optional<uint32_t> Group, 42606f32e7eSjoerg Optional<llvm::sys::fs::file_type> Type, 42706f32e7eSjoerg Optional<llvm::sys::fs::perms> Perms, 42806f32e7eSjoerg const detail::InMemoryFile *HardLinkTarget); 42906f32e7eSjoerg 43006f32e7eSjoerg public: 43106f32e7eSjoerg explicit InMemoryFileSystem(bool UseNormalizedPaths = true); 43206f32e7eSjoerg ~InMemoryFileSystem() override; 43306f32e7eSjoerg 43406f32e7eSjoerg /// Add a file containing a buffer or a directory to the VFS with a 43506f32e7eSjoerg /// path. The VFS owns the buffer. If present, User, Group, Type 43606f32e7eSjoerg /// and Perms apply to the newly-created file or directory. 43706f32e7eSjoerg /// \return true if the file or directory was successfully added, 43806f32e7eSjoerg /// false if the file or directory already exists in the file system with 43906f32e7eSjoerg /// different contents. 44006f32e7eSjoerg bool addFile(const Twine &Path, time_t ModificationTime, 44106f32e7eSjoerg std::unique_ptr<llvm::MemoryBuffer> Buffer, 44206f32e7eSjoerg Optional<uint32_t> User = None, Optional<uint32_t> Group = None, 44306f32e7eSjoerg Optional<llvm::sys::fs::file_type> Type = None, 44406f32e7eSjoerg Optional<llvm::sys::fs::perms> Perms = None); 44506f32e7eSjoerg 44606f32e7eSjoerg /// Add a hard link to a file. 44706f32e7eSjoerg /// Here hard links are not intended to be fully equivalent to the classical 44806f32e7eSjoerg /// filesystem. Both the hard link and the file share the same buffer and 44906f32e7eSjoerg /// status (and thus have the same UniqueID). Because of this there is no way 45006f32e7eSjoerg /// to distinguish between the link and the file after the link has been 45106f32e7eSjoerg /// added. 45206f32e7eSjoerg /// 45306f32e7eSjoerg /// The To path must be an existing file or a hardlink. The From file must not 45406f32e7eSjoerg /// have been added before. The To Path must not be a directory. The From Node 45506f32e7eSjoerg /// is added as a hard link which points to the resolved file of To Node. 45606f32e7eSjoerg /// \return true if the above condition is satisfied and hardlink was 45706f32e7eSjoerg /// successfully created, false otherwise. 45806f32e7eSjoerg bool addHardLink(const Twine &From, const Twine &To); 45906f32e7eSjoerg 46006f32e7eSjoerg /// Add a buffer to the VFS with a path. The VFS does not own the buffer. 46106f32e7eSjoerg /// If present, User, Group, Type and Perms apply to the newly-created file 46206f32e7eSjoerg /// or directory. 46306f32e7eSjoerg /// \return true if the file or directory was successfully added, 46406f32e7eSjoerg /// false if the file or directory already exists in the file system with 46506f32e7eSjoerg /// different contents. 46606f32e7eSjoerg bool addFileNoOwn(const Twine &Path, time_t ModificationTime, 467*da58b97aSjoerg const llvm::MemoryBufferRef &Buffer, 468*da58b97aSjoerg Optional<uint32_t> User = None, 46906f32e7eSjoerg Optional<uint32_t> Group = None, 47006f32e7eSjoerg Optional<llvm::sys::fs::file_type> Type = None, 47106f32e7eSjoerg Optional<llvm::sys::fs::perms> Perms = None); 47206f32e7eSjoerg 47306f32e7eSjoerg std::string toString() const; 47406f32e7eSjoerg 47506f32e7eSjoerg /// Return true if this file system normalizes . and .. in paths. useNormalizedPaths()47606f32e7eSjoerg bool useNormalizedPaths() const { return UseNormalizedPaths; } 47706f32e7eSjoerg 47806f32e7eSjoerg llvm::ErrorOr<Status> status(const Twine &Path) override; 47906f32e7eSjoerg llvm::ErrorOr<std::unique_ptr<File>> 48006f32e7eSjoerg openFileForRead(const Twine &Path) override; 48106f32e7eSjoerg directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; 48206f32e7eSjoerg getCurrentWorkingDirectory()48306f32e7eSjoerg llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 48406f32e7eSjoerg return WorkingDirectory; 48506f32e7eSjoerg } 48606f32e7eSjoerg /// Canonicalizes \p Path by combining with the current working 48706f32e7eSjoerg /// directory and normalizing the path (e.g. remove dots). If the current 48806f32e7eSjoerg /// working directory is not set, this returns errc::operation_not_permitted. 48906f32e7eSjoerg /// 49006f32e7eSjoerg /// This doesn't resolve symlinks as they are not supported in in-memory file 49106f32e7eSjoerg /// system. 49206f32e7eSjoerg std::error_code getRealPath(const Twine &Path, 49306f32e7eSjoerg SmallVectorImpl<char> &Output) const override; 49406f32e7eSjoerg std::error_code isLocal(const Twine &Path, bool &Result) override; 49506f32e7eSjoerg std::error_code setCurrentWorkingDirectory(const Twine &Path) override; 49606f32e7eSjoerg }; 49706f32e7eSjoerg 49806f32e7eSjoerg /// Get a globally unique ID for a virtual file or directory. 49906f32e7eSjoerg llvm::sys::fs::UniqueID getNextVirtualUniqueID(); 50006f32e7eSjoerg 50106f32e7eSjoerg /// Gets a \p FileSystem for a virtual file system described in YAML 50206f32e7eSjoerg /// format. 503*da58b97aSjoerg std::unique_ptr<FileSystem> 50406f32e7eSjoerg getVFSFromYAML(std::unique_ptr<llvm::MemoryBuffer> Buffer, 50506f32e7eSjoerg llvm::SourceMgr::DiagHandlerTy DiagHandler, 50606f32e7eSjoerg StringRef YAMLFilePath, void *DiagContext = nullptr, 50706f32e7eSjoerg IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem()); 50806f32e7eSjoerg 50906f32e7eSjoerg struct YAMLVFSEntry { 51006f32e7eSjoerg template <typename T1, typename T2> 511*da58b97aSjoerg YAMLVFSEntry(T1 &&VPath, T2 &&RPath, bool IsDirectory = false) VPathYAMLVFSEntry512*da58b97aSjoerg : VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)), 513*da58b97aSjoerg IsDirectory(IsDirectory) {} 51406f32e7eSjoerg std::string VPath; 51506f32e7eSjoerg std::string RPath; 516*da58b97aSjoerg bool IsDirectory = false; 51706f32e7eSjoerg }; 51806f32e7eSjoerg 519*da58b97aSjoerg class RedirectingFSDirIterImpl; 52006f32e7eSjoerg class RedirectingFileSystemParser; 52106f32e7eSjoerg 52206f32e7eSjoerg /// A virtual file system parsed from a YAML file. 52306f32e7eSjoerg /// 524*da58b97aSjoerg /// Currently, this class allows creating virtual files and directories. Virtual 525*da58b97aSjoerg /// files map to existing external files in \c ExternalFS, and virtual 526*da58b97aSjoerg /// directories may either map to existing directories in \c ExternalFS or list 527*da58b97aSjoerg /// their contents in the form of other virtual directories and/or files. 52806f32e7eSjoerg /// 52906f32e7eSjoerg /// The basic structure of the parsed file is: 53006f32e7eSjoerg /// \verbatim 53106f32e7eSjoerg /// { 53206f32e7eSjoerg /// 'version': <version number>, 53306f32e7eSjoerg /// <optional configuration> 53406f32e7eSjoerg /// 'roots': [ 53506f32e7eSjoerg /// <directory entries> 53606f32e7eSjoerg /// ] 53706f32e7eSjoerg /// } 53806f32e7eSjoerg /// \endverbatim 53906f32e7eSjoerg /// 54006f32e7eSjoerg /// All configuration options are optional. 541*da58b97aSjoerg /// 'case-sensitive': <boolean, default=(true for Posix, false for Windows)> 54206f32e7eSjoerg /// 'use-external-names': <boolean, default=true> 54306f32e7eSjoerg /// 'overlay-relative': <boolean, default=false> 54406f32e7eSjoerg /// 'fallthrough': <boolean, default=true> 54506f32e7eSjoerg /// 546*da58b97aSjoerg /// Virtual directories that list their contents are represented as 54706f32e7eSjoerg /// \verbatim 54806f32e7eSjoerg /// { 54906f32e7eSjoerg /// 'type': 'directory', 55006f32e7eSjoerg /// 'name': <string>, 55106f32e7eSjoerg /// 'contents': [ <file or directory entries> ] 55206f32e7eSjoerg /// } 55306f32e7eSjoerg /// \endverbatim 55406f32e7eSjoerg /// 555*da58b97aSjoerg /// The default attributes for such virtual directories are: 55606f32e7eSjoerg /// \verbatim 55706f32e7eSjoerg /// MTime = now() when created 55806f32e7eSjoerg /// Perms = 0777 55906f32e7eSjoerg /// User = Group = 0 56006f32e7eSjoerg /// Size = 0 56106f32e7eSjoerg /// UniqueID = unspecified unique value 56206f32e7eSjoerg /// \endverbatim 56306f32e7eSjoerg /// 564*da58b97aSjoerg /// When a path prefix matches such a directory, the next component in the path 565*da58b97aSjoerg /// is matched against the entries in the 'contents' array. 566*da58b97aSjoerg /// 567*da58b97aSjoerg /// Re-mapped directories, on the other hand, are represented as 568*da58b97aSjoerg /// /// \verbatim 569*da58b97aSjoerg /// { 570*da58b97aSjoerg /// 'type': 'directory-remap', 571*da58b97aSjoerg /// 'name': <string>, 572*da58b97aSjoerg /// 'use-external-name': <boolean>, # Optional 573*da58b97aSjoerg /// 'external-contents': <path to external directory> 574*da58b97aSjoerg /// } 575*da58b97aSjoerg /// \endverbatim 576*da58b97aSjoerg /// 577*da58b97aSjoerg /// and inherit their attributes from the external directory. When a path 578*da58b97aSjoerg /// prefix matches such an entry, the unmatched components are appended to the 579*da58b97aSjoerg /// 'external-contents' path, and the resulting path is looked up in the 580*da58b97aSjoerg /// external file system instead. 581*da58b97aSjoerg /// 58206f32e7eSjoerg /// Re-mapped files are represented as 58306f32e7eSjoerg /// \verbatim 58406f32e7eSjoerg /// { 58506f32e7eSjoerg /// 'type': 'file', 58606f32e7eSjoerg /// 'name': <string>, 587*da58b97aSjoerg /// 'use-external-name': <boolean>, # Optional 58806f32e7eSjoerg /// 'external-contents': <path to external file> 58906f32e7eSjoerg /// } 59006f32e7eSjoerg /// \endverbatim 59106f32e7eSjoerg /// 592*da58b97aSjoerg /// Their attributes and file contents are determined by looking up the file at 593*da58b97aSjoerg /// their 'external-contents' path in the external file system. 59406f32e7eSjoerg /// 595*da58b97aSjoerg /// For 'file', 'directory' and 'directory-remap' entries the 'name' field may 596*da58b97aSjoerg /// contain multiple path components (e.g. /path/to/file). However, any 597*da58b97aSjoerg /// directory in such a path that contains more than one child must be uniquely 598*da58b97aSjoerg /// represented by a 'directory' entry. 59906f32e7eSjoerg class RedirectingFileSystem : public vfs::FileSystem { 60006f32e7eSjoerg public: 601*da58b97aSjoerg enum EntryKind { EK_Directory, EK_DirectoryRemap, EK_File }; 602*da58b97aSjoerg enum NameKind { NK_NotSet, NK_External, NK_Virtual }; 60306f32e7eSjoerg 60406f32e7eSjoerg /// A single file or directory in the VFS. 60506f32e7eSjoerg class Entry { 60606f32e7eSjoerg EntryKind Kind; 60706f32e7eSjoerg std::string Name; 60806f32e7eSjoerg 60906f32e7eSjoerg public: Entry(EntryKind K,StringRef Name)61006f32e7eSjoerg Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {} 61106f32e7eSjoerg virtual ~Entry() = default; 61206f32e7eSjoerg getName()61306f32e7eSjoerg StringRef getName() const { return Name; } getKind()61406f32e7eSjoerg EntryKind getKind() const { return Kind; } 61506f32e7eSjoerg }; 61606f32e7eSjoerg 617*da58b97aSjoerg /// A directory in the vfs with explicitly specified contents. 618*da58b97aSjoerg class DirectoryEntry : public Entry { 61906f32e7eSjoerg std::vector<std::unique_ptr<Entry>> Contents; 62006f32e7eSjoerg Status S; 62106f32e7eSjoerg 62206f32e7eSjoerg public: 623*da58b97aSjoerg /// Constructs a directory entry with explicitly specified contents. DirectoryEntry(StringRef Name,std::vector<std::unique_ptr<Entry>> Contents,Status S)624*da58b97aSjoerg DirectoryEntry(StringRef Name, std::vector<std::unique_ptr<Entry>> Contents, 62506f32e7eSjoerg Status S) 62606f32e7eSjoerg : Entry(EK_Directory, Name), Contents(std::move(Contents)), 62706f32e7eSjoerg S(std::move(S)) {} 628*da58b97aSjoerg 629*da58b97aSjoerg /// Constructs an empty directory entry. DirectoryEntry(StringRef Name,Status S)630*da58b97aSjoerg DirectoryEntry(StringRef Name, Status S) 63106f32e7eSjoerg : Entry(EK_Directory, Name), S(std::move(S)) {} 63206f32e7eSjoerg getStatus()63306f32e7eSjoerg Status getStatus() { return S; } 63406f32e7eSjoerg addContent(std::unique_ptr<Entry> Content)63506f32e7eSjoerg void addContent(std::unique_ptr<Entry> Content) { 63606f32e7eSjoerg Contents.push_back(std::move(Content)); 63706f32e7eSjoerg } 63806f32e7eSjoerg getLastContent()63906f32e7eSjoerg Entry *getLastContent() const { return Contents.back().get(); } 64006f32e7eSjoerg 64106f32e7eSjoerg using iterator = decltype(Contents)::iterator; 64206f32e7eSjoerg contents_begin()64306f32e7eSjoerg iterator contents_begin() { return Contents.begin(); } contents_end()64406f32e7eSjoerg iterator contents_end() { return Contents.end(); } 64506f32e7eSjoerg classof(const Entry * E)64606f32e7eSjoerg static bool classof(const Entry *E) { return E->getKind() == EK_Directory; } 64706f32e7eSjoerg }; 64806f32e7eSjoerg 649*da58b97aSjoerg /// A file or directory in the vfs that is mapped to a file or directory in 650*da58b97aSjoerg /// the external filesystem. 651*da58b97aSjoerg class RemapEntry : public Entry { 65206f32e7eSjoerg std::string ExternalContentsPath; 65306f32e7eSjoerg NameKind UseName; 65406f32e7eSjoerg 655*da58b97aSjoerg protected: RemapEntry(EntryKind K,StringRef Name,StringRef ExternalContentsPath,NameKind UseName)656*da58b97aSjoerg RemapEntry(EntryKind K, StringRef Name, StringRef ExternalContentsPath, 65706f32e7eSjoerg NameKind UseName) 658*da58b97aSjoerg : Entry(K, Name), ExternalContentsPath(ExternalContentsPath), 65906f32e7eSjoerg UseName(UseName) {} 66006f32e7eSjoerg 661*da58b97aSjoerg public: getExternalContentsPath()66206f32e7eSjoerg StringRef getExternalContentsPath() const { return ExternalContentsPath; } 66306f32e7eSjoerg 664*da58b97aSjoerg /// Whether to use the external path as the name for this file or directory. useExternalName(bool GlobalUseExternalName)66506f32e7eSjoerg bool useExternalName(bool GlobalUseExternalName) const { 66606f32e7eSjoerg return UseName == NK_NotSet ? GlobalUseExternalName 66706f32e7eSjoerg : (UseName == NK_External); 66806f32e7eSjoerg } 66906f32e7eSjoerg getUseName()67006f32e7eSjoerg NameKind getUseName() const { return UseName; } 67106f32e7eSjoerg classof(const Entry * E)672*da58b97aSjoerg static bool classof(const Entry *E) { 673*da58b97aSjoerg switch (E->getKind()) { 674*da58b97aSjoerg case EK_DirectoryRemap: 675*da58b97aSjoerg LLVM_FALLTHROUGH; 676*da58b97aSjoerg case EK_File: 677*da58b97aSjoerg return true; 678*da58b97aSjoerg case EK_Directory: 679*da58b97aSjoerg return false; 680*da58b97aSjoerg } 681*da58b97aSjoerg llvm_unreachable("invalid entry kind"); 682*da58b97aSjoerg } 683*da58b97aSjoerg }; 684*da58b97aSjoerg 685*da58b97aSjoerg /// A directory in the vfs that maps to a directory in the external file 686*da58b97aSjoerg /// system. 687*da58b97aSjoerg class DirectoryRemapEntry : public RemapEntry { 688*da58b97aSjoerg public: DirectoryRemapEntry(StringRef Name,StringRef ExternalContentsPath,NameKind UseName)689*da58b97aSjoerg DirectoryRemapEntry(StringRef Name, StringRef ExternalContentsPath, 690*da58b97aSjoerg NameKind UseName) 691*da58b97aSjoerg : RemapEntry(EK_DirectoryRemap, Name, ExternalContentsPath, UseName) {} 692*da58b97aSjoerg classof(const Entry * E)693*da58b97aSjoerg static bool classof(const Entry *E) { 694*da58b97aSjoerg return E->getKind() == EK_DirectoryRemap; 695*da58b97aSjoerg } 696*da58b97aSjoerg }; 697*da58b97aSjoerg 698*da58b97aSjoerg /// A file in the vfs that maps to a file in the external file system. 699*da58b97aSjoerg class FileEntry : public RemapEntry { 700*da58b97aSjoerg public: FileEntry(StringRef Name,StringRef ExternalContentsPath,NameKind UseName)701*da58b97aSjoerg FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName) 702*da58b97aSjoerg : RemapEntry(EK_File, Name, ExternalContentsPath, UseName) {} 703*da58b97aSjoerg classof(const Entry * E)70406f32e7eSjoerg static bool classof(const Entry *E) { return E->getKind() == EK_File; } 70506f32e7eSjoerg }; 70606f32e7eSjoerg 707*da58b97aSjoerg /// Represents the result of a path lookup into the RedirectingFileSystem. 708*da58b97aSjoerg struct LookupResult { 709*da58b97aSjoerg /// The entry the looked-up path corresponds to. 710*da58b97aSjoerg Entry *E; 711*da58b97aSjoerg 71206f32e7eSjoerg private: 713*da58b97aSjoerg /// When the found Entry is a DirectoryRemapEntry, stores the path in the 714*da58b97aSjoerg /// external file system that the looked-up path in the virtual file system 715*da58b97aSjoerg // corresponds to. 716*da58b97aSjoerg Optional<std::string> ExternalRedirect; 717*da58b97aSjoerg 718*da58b97aSjoerg public: 719*da58b97aSjoerg LookupResult(Entry *E, sys::path::const_iterator Start, 720*da58b97aSjoerg sys::path::const_iterator End); 721*da58b97aSjoerg 722*da58b97aSjoerg /// If the found Entry maps the the input path to a path in the external 723*da58b97aSjoerg /// file system (i.e. it is a FileEntry or DirectoryRemapEntry), returns 724*da58b97aSjoerg /// that path. getExternalRedirectLookupResult725*da58b97aSjoerg Optional<StringRef> getExternalRedirect() const { 726*da58b97aSjoerg if (isa<DirectoryRemapEntry>(E)) 727*da58b97aSjoerg return StringRef(*ExternalRedirect); 728*da58b97aSjoerg if (auto *FE = dyn_cast<FileEntry>(E)) 729*da58b97aSjoerg return FE->getExternalContentsPath(); 730*da58b97aSjoerg return None; 731*da58b97aSjoerg } 732*da58b97aSjoerg }; 733*da58b97aSjoerg 734*da58b97aSjoerg private: 735*da58b97aSjoerg friend class RedirectingFSDirIterImpl; 73606f32e7eSjoerg friend class RedirectingFileSystemParser; 73706f32e7eSjoerg shouldUseExternalFS()738*da58b97aSjoerg bool shouldUseExternalFS() const { return IsFallthrough; } 739*da58b97aSjoerg 740*da58b97aSjoerg /// Canonicalize path by removing ".", "..", "./", components. This is 741*da58b97aSjoerg /// a VFS request, do not bother about symlinks in the path components 742*da58b97aSjoerg /// but canonicalize in order to perform the correct entry search. 743*da58b97aSjoerg std::error_code makeCanonical(SmallVectorImpl<char> &Path) const; 744*da58b97aSjoerg 745*da58b97aSjoerg /// Whether to fall back to the external file system when an operation fails 746*da58b97aSjoerg /// with the given error code on a path associated with the provided Entry. 747*da58b97aSjoerg bool shouldFallBackToExternalFS(std::error_code EC, Entry *E = nullptr) const; 748*da58b97aSjoerg 749*da58b97aSjoerg // In a RedirectingFileSystem, keys can be specified in Posix or Windows 750*da58b97aSjoerg // style (or even a mixture of both), so this comparison helper allows 751*da58b97aSjoerg // slashes (representing a root) to match backslashes (and vice versa). Note 752*da58b97aSjoerg // that, other than the root, path components should not contain slashes or 753*da58b97aSjoerg // backslashes. pathComponentMatches(llvm::StringRef lhs,llvm::StringRef rhs)754*da58b97aSjoerg bool pathComponentMatches(llvm::StringRef lhs, llvm::StringRef rhs) const { 755*da58b97aSjoerg if ((CaseSensitive ? lhs.equals(rhs) : lhs.equals_lower(rhs))) 756*da58b97aSjoerg return true; 757*da58b97aSjoerg return (lhs == "/" && rhs == "\\") || (lhs == "\\" && rhs == "/"); 75806f32e7eSjoerg } 75906f32e7eSjoerg 76006f32e7eSjoerg /// The root(s) of the virtual file system. 76106f32e7eSjoerg std::vector<std::unique_ptr<Entry>> Roots; 76206f32e7eSjoerg 76306f32e7eSjoerg /// The current working directory of the file system. 76406f32e7eSjoerg std::string WorkingDirectory; 76506f32e7eSjoerg 76606f32e7eSjoerg /// The file system to use for external references. 76706f32e7eSjoerg IntrusiveRefCntPtr<FileSystem> ExternalFS; 76806f32e7eSjoerg 76906f32e7eSjoerg /// If IsRelativeOverlay is set, this represents the directory 77006f32e7eSjoerg /// path that should be prefixed to each 'external-contents' entry 77106f32e7eSjoerg /// when reading from YAML files. 77206f32e7eSjoerg std::string ExternalContentsPrefixDir; 77306f32e7eSjoerg 77406f32e7eSjoerg /// @name Configuration 77506f32e7eSjoerg /// @{ 77606f32e7eSjoerg 77706f32e7eSjoerg /// Whether to perform case-sensitive comparisons. 77806f32e7eSjoerg /// 77906f32e7eSjoerg /// Currently, case-insensitive matching only works correctly with ASCII. 780*da58b97aSjoerg bool CaseSensitive = 781*da58b97aSjoerg #ifdef _WIN32 782*da58b97aSjoerg false; 783*da58b97aSjoerg #else 784*da58b97aSjoerg true; 785*da58b97aSjoerg #endif 78606f32e7eSjoerg 78706f32e7eSjoerg /// IsRelativeOverlay marks whether a ExternalContentsPrefixDir path must 78806f32e7eSjoerg /// be prefixed in every 'external-contents' when reading from YAML files. 78906f32e7eSjoerg bool IsRelativeOverlay = false; 79006f32e7eSjoerg 79106f32e7eSjoerg /// Whether to use to use the value of 'external-contents' for the 79206f32e7eSjoerg /// names of files. This global value is overridable on a per-file basis. 79306f32e7eSjoerg bool UseExternalNames = true; 79406f32e7eSjoerg 79506f32e7eSjoerg /// Whether to attempt a file lookup in external file system after it wasn't 79606f32e7eSjoerg /// found in VFS. 79706f32e7eSjoerg bool IsFallthrough = true; 79806f32e7eSjoerg /// @} 79906f32e7eSjoerg 80006f32e7eSjoerg RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS); 80106f32e7eSjoerg 802*da58b97aSjoerg /// Looks up the path <tt>[Start, End)</tt> in \p From, possibly recursing 803*da58b97aSjoerg /// into the contents of \p From if it is a directory. Returns a LookupResult 804*da58b97aSjoerg /// giving the matched entry and, if that entry is a FileEntry or 805*da58b97aSjoerg /// DirectoryRemapEntry, the path it redirects to in the external file system. 806*da58b97aSjoerg ErrorOr<LookupResult> lookupPathImpl(llvm::sys::path::const_iterator Start, 80706f32e7eSjoerg llvm::sys::path::const_iterator End, 80806f32e7eSjoerg Entry *From) const; 80906f32e7eSjoerg 810*da58b97aSjoerg /// Get the status for a path with the provided \c LookupResult. 811*da58b97aSjoerg ErrorOr<Status> status(const Twine &Path, const LookupResult &Result); 81206f32e7eSjoerg 81306f32e7eSjoerg public: 814*da58b97aSjoerg /// Looks up \p Path in \c Roots and returns a LookupResult giving the 815*da58b97aSjoerg /// matched entry and, if the entry was a FileEntry or DirectoryRemapEntry, 816*da58b97aSjoerg /// the path it redirects to in the external file system. 817*da58b97aSjoerg ErrorOr<LookupResult> lookupPath(StringRef Path) const; 81806f32e7eSjoerg 81906f32e7eSjoerg /// Parses \p Buffer, which is expected to be in YAML format and 82006f32e7eSjoerg /// returns a virtual file system representing its contents. 821*da58b97aSjoerg static std::unique_ptr<RedirectingFileSystem> 82206f32e7eSjoerg create(std::unique_ptr<MemoryBuffer> Buffer, 82306f32e7eSjoerg SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, 82406f32e7eSjoerg void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS); 82506f32e7eSjoerg 826*da58b97aSjoerg /// Redirect each of the remapped files from first to second. 827*da58b97aSjoerg static std::unique_ptr<RedirectingFileSystem> 828*da58b97aSjoerg create(ArrayRef<std::pair<std::string, std::string>> RemappedFiles, 829*da58b97aSjoerg bool UseExternalNames, FileSystem &ExternalFS); 830*da58b97aSjoerg 83106f32e7eSjoerg ErrorOr<Status> status(const Twine &Path) override; 83206f32e7eSjoerg ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override; 83306f32e7eSjoerg 83406f32e7eSjoerg std::error_code getRealPath(const Twine &Path, 83506f32e7eSjoerg SmallVectorImpl<char> &Output) const override; 83606f32e7eSjoerg 83706f32e7eSjoerg llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; 83806f32e7eSjoerg 83906f32e7eSjoerg std::error_code setCurrentWorkingDirectory(const Twine &Path) override; 84006f32e7eSjoerg 84106f32e7eSjoerg std::error_code isLocal(const Twine &Path, bool &Result) override; 84206f32e7eSjoerg 843*da58b97aSjoerg std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const override; 844*da58b97aSjoerg 84506f32e7eSjoerg directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; 84606f32e7eSjoerg 84706f32e7eSjoerg void setExternalContentsPrefixDir(StringRef PrefixDir); 84806f32e7eSjoerg 84906f32e7eSjoerg StringRef getExternalContentsPrefixDir() const; 85006f32e7eSjoerg 851*da58b97aSjoerg void setFallthrough(bool Fallthrough); 852*da58b97aSjoerg 853*da58b97aSjoerg std::vector<llvm::StringRef> getRoots() const; 854*da58b97aSjoerg 85506f32e7eSjoerg void dump(raw_ostream &OS) const; 85606f32e7eSjoerg void dumpEntry(raw_ostream &OS, Entry *E, int NumSpaces = 0) const; 85706f32e7eSjoerg #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 85806f32e7eSjoerg LLVM_DUMP_METHOD void dump() const; 85906f32e7eSjoerg #endif 86006f32e7eSjoerg }; 86106f32e7eSjoerg 86206f32e7eSjoerg /// Collect all pairs of <virtual path, real path> entries from the 86306f32e7eSjoerg /// \p YAMLFilePath. This is used by the module dependency collector to forward 86406f32e7eSjoerg /// the entries into the reproducer output VFS YAML file. 86506f32e7eSjoerg void collectVFSFromYAML( 86606f32e7eSjoerg std::unique_ptr<llvm::MemoryBuffer> Buffer, 86706f32e7eSjoerg llvm::SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, 86806f32e7eSjoerg SmallVectorImpl<YAMLVFSEntry> &CollectedEntries, 86906f32e7eSjoerg void *DiagContext = nullptr, 87006f32e7eSjoerg IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem()); 87106f32e7eSjoerg 87206f32e7eSjoerg class YAMLVFSWriter { 87306f32e7eSjoerg std::vector<YAMLVFSEntry> Mappings; 87406f32e7eSjoerg Optional<bool> IsCaseSensitive; 87506f32e7eSjoerg Optional<bool> IsOverlayRelative; 87606f32e7eSjoerg Optional<bool> UseExternalNames; 87706f32e7eSjoerg std::string OverlayDir; 87806f32e7eSjoerg 879*da58b97aSjoerg void addEntry(StringRef VirtualPath, StringRef RealPath, bool IsDirectory); 880*da58b97aSjoerg 88106f32e7eSjoerg public: 88206f32e7eSjoerg YAMLVFSWriter() = default; 88306f32e7eSjoerg 88406f32e7eSjoerg void addFileMapping(StringRef VirtualPath, StringRef RealPath); 885*da58b97aSjoerg void addDirectoryMapping(StringRef VirtualPath, StringRef RealPath); 88606f32e7eSjoerg setCaseSensitivity(bool CaseSensitive)88706f32e7eSjoerg void setCaseSensitivity(bool CaseSensitive) { 88806f32e7eSjoerg IsCaseSensitive = CaseSensitive; 88906f32e7eSjoerg } 89006f32e7eSjoerg setUseExternalNames(bool UseExtNames)89106f32e7eSjoerg void setUseExternalNames(bool UseExtNames) { UseExternalNames = UseExtNames; } 89206f32e7eSjoerg setOverlayDir(StringRef OverlayDirectory)89306f32e7eSjoerg void setOverlayDir(StringRef OverlayDirectory) { 89406f32e7eSjoerg IsOverlayRelative = true; 89506f32e7eSjoerg OverlayDir.assign(OverlayDirectory.str()); 89606f32e7eSjoerg } 89706f32e7eSjoerg getMappings()89806f32e7eSjoerg const std::vector<YAMLVFSEntry> &getMappings() const { return Mappings; } 89906f32e7eSjoerg 90006f32e7eSjoerg void write(llvm::raw_ostream &OS); 90106f32e7eSjoerg }; 90206f32e7eSjoerg 90306f32e7eSjoerg } // namespace vfs 90406f32e7eSjoerg } // namespace llvm 90506f32e7eSjoerg 90606f32e7eSjoerg #endif // LLVM_SUPPORT_VIRTUALFILESYSTEM_H 907