Home | History | Annotate | Download | only in Basic
      1 //===- VirtualFileSystem.h - Virtual File System Layer ----------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 /// \file
     10 /// \brief Defines the virtual file system interface vfs::FileSystem.
     11 //===----------------------------------------------------------------------===//
     12 
     13 #ifndef LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H
     14 #define LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H
     15 
     16 #include "clang/Basic/LLVM.h"
     17 #include "llvm/ADT/IntrusiveRefCntPtr.h"
     18 #include "llvm/ADT/Optional.h"
     19 #include "llvm/Support/ErrorOr.h"
     20 #include "llvm/Support/FileSystem.h"
     21 #include "llvm/Support/SourceMgr.h"
     22 #include "llvm/Support/raw_ostream.h"
     23 
     24 namespace llvm {
     25 class MemoryBuffer;
     26 }
     27 
     28 namespace clang {
     29 namespace vfs {
     30 
     31 /// \brief The result of a \p status operation.
     32 class Status {
     33   std::string Name;
     34   llvm::sys::fs::UniqueID UID;
     35   llvm::sys::TimeValue MTime;
     36   uint32_t User;
     37   uint32_t Group;
     38   uint64_t Size;
     39   llvm::sys::fs::file_type Type;
     40   llvm::sys::fs::perms Perms;
     41 
     42 public:
     43   bool IsVFSMapped; // FIXME: remove when files support multiple names
     44 
     45 public:
     46   Status() : Type(llvm::sys::fs::file_type::status_error) {}
     47   Status(const llvm::sys::fs::file_status &Status);
     48   Status(StringRef Name, llvm::sys::fs::UniqueID UID,
     49          llvm::sys::TimeValue MTime, uint32_t User, uint32_t Group,
     50          uint64_t Size, llvm::sys::fs::file_type Type,
     51          llvm::sys::fs::perms Perms);
     52 
     53   /// Get a copy of a Status with a different name.
     54   static Status copyWithNewName(const Status &In, StringRef NewName);
     55   static Status copyWithNewName(const llvm::sys::fs::file_status &In,
     56                                 StringRef NewName);
     57 
     58   /// \brief Returns the name that should be used for this file or directory.
     59   StringRef getName() const { return Name; }
     60 
     61   /// @name Status interface from llvm::sys::fs
     62   /// @{
     63   llvm::sys::fs::file_type getType() const { return Type; }
     64   llvm::sys::fs::perms getPermissions() const { return Perms; }
     65   llvm::sys::TimeValue getLastModificationTime() const { return MTime; }
     66   llvm::sys::fs::UniqueID getUniqueID() const { return UID; }
     67   uint32_t getUser() const { return User; }
     68   uint32_t getGroup() const { return Group; }
     69   uint64_t getSize() const { return Size; }
     70   /// @}
     71   /// @name Status queries
     72   /// These are static queries in llvm::sys::fs.
     73   /// @{
     74   bool equivalent(const Status &Other) const;
     75   bool isDirectory() const;
     76   bool isRegularFile() const;
     77   bool isOther() const;
     78   bool isSymlink() const;
     79   bool isStatusKnown() const;
     80   bool exists() const;
     81   /// @}
     82 };
     83 
     84 /// \brief Represents an open file.
     85 class File {
     86 public:
     87   /// \brief Destroy the file after closing it (if open).
     88   /// Sub-classes should generally call close() inside their destructors.  We
     89   /// cannot do that from the base class, since close is virtual.
     90   virtual ~File();
     91   /// \brief Get the status of the file.
     92   virtual llvm::ErrorOr<Status> status() = 0;
     93   /// \brief Get the contents of the file as a \p MemoryBuffer.
     94   virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
     95   getBuffer(const Twine &Name, int64_t FileSize = -1,
     96             bool RequiresNullTerminator = true, bool IsVolatile = false) = 0;
     97   /// \brief Closes the file.
     98   virtual std::error_code close() = 0;
     99 };
    100 
    101 namespace detail {
    102 /// \brief An interface for virtual file systems to provide an iterator over the
    103 /// (non-recursive) contents of a directory.
    104 struct DirIterImpl {
    105   virtual ~DirIterImpl();
    106   /// \brief Sets \c CurrentEntry to the next entry in the directory on success,
    107   /// or returns a system-defined \c error_code.
    108   virtual std::error_code increment() = 0;
    109   Status CurrentEntry;
    110 };
    111 } // end namespace detail
    112 
    113 /// \brief An input iterator over the entries in a virtual path, similar to
    114 /// llvm::sys::fs::directory_iterator.
    115 class directory_iterator {
    116   std::shared_ptr<detail::DirIterImpl> Impl; // Input iterator semantics on copy
    117 
    118 public:
    119   directory_iterator(std::shared_ptr<detail::DirIterImpl> I) : Impl(I) {
    120     assert(Impl.get() != nullptr && "requires non-null implementation");
    121     if (!Impl->CurrentEntry.isStatusKnown())
    122       Impl.reset(); // Normalize the end iterator to Impl == nullptr.
    123   }
    124 
    125   /// \brief Construct an 'end' iterator.
    126   directory_iterator() { }
    127 
    128   /// \brief Equivalent to operator++, with an error code.
    129   directory_iterator &increment(std::error_code &EC) {
    130     assert(Impl && "attempting to increment past end");
    131     EC = Impl->increment();
    132     if (EC || !Impl->CurrentEntry.isStatusKnown())
    133       Impl.reset(); // Normalize the end iterator to Impl == nullptr.
    134     return *this;
    135   }
    136 
    137   const Status &operator*() const { return Impl->CurrentEntry; }
    138   const Status *operator->() const { return &Impl->CurrentEntry; }
    139 
    140   bool operator==(const directory_iterator &RHS) const {
    141     if (Impl && RHS.Impl)
    142       return Impl->CurrentEntry.equivalent(RHS.Impl->CurrentEntry);
    143     return !Impl && !RHS.Impl;
    144   }
    145   bool operator!=(const directory_iterator &RHS) const {
    146     return !(*this == RHS);
    147   }
    148 };
    149 
    150 class FileSystem;
    151 
    152 /// \brief An input iterator over the recursive contents of a virtual path,
    153 /// similar to llvm::sys::fs::recursive_directory_iterator.
    154 class recursive_directory_iterator {
    155   typedef std::stack<directory_iterator, std::vector<directory_iterator>>
    156       IterState;
    157 
    158   FileSystem *FS;
    159   std::shared_ptr<IterState> State; // Input iterator semantics on copy.
    160 
    161 public:
    162   recursive_directory_iterator(FileSystem &FS, const Twine &Path,
    163                                std::error_code &EC);
    164   /// \brief Construct an 'end' iterator.
    165   recursive_directory_iterator() { }
    166 
    167   /// \brief Equivalent to operator++, with an error code.
    168   recursive_directory_iterator &increment(std::error_code &EC);
    169 
    170   const Status &operator*() const { return *State->top(); }
    171   const Status *operator->() const { return &*State->top(); }
    172 
    173   bool operator==(const recursive_directory_iterator &Other) const {
    174     return State == Other.State; // identity
    175   }
    176   bool operator!=(const recursive_directory_iterator &RHS) const {
    177     return !(*this == RHS);
    178   }
    179 };
    180 
    181 /// \brief The virtual file system interface.
    182 class FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem> {
    183 public:
    184   virtual ~FileSystem();
    185 
    186   /// \brief Get the status of the entry at \p Path, if one exists.
    187   virtual llvm::ErrorOr<Status> status(const Twine &Path) = 0;
    188   /// \brief Get a \p File object for the file at \p Path, if one exists.
    189   virtual llvm::ErrorOr<std::unique_ptr<File>>
    190   openFileForRead(const Twine &Path) = 0;
    191 
    192   /// This is a convenience method that opens a file, gets its content and then
    193   /// closes the file.
    194   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
    195   getBufferForFile(const Twine &Name, int64_t FileSize = -1,
    196                    bool RequiresNullTerminator = true, bool IsVolatile = false);
    197 
    198   /// \brief Get a directory_iterator for \p Dir.
    199   /// \note The 'end' iterator is directory_iterator().
    200   virtual directory_iterator dir_begin(const Twine &Dir,
    201                                        std::error_code &EC) = 0;
    202 
    203   /// Set the working directory. This will affect all following operations on
    204   /// this file system and may propagate down for nested file systems.
    205   virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) = 0;
    206   /// Get the working directory of this file system.
    207   virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const = 0;
    208 
    209   /// Check whether a file exists. Provided for convenience.
    210   bool exists(const Twine &Path);
    211 
    212   /// Make \a Path an absolute path.
    213   ///
    214   /// Makes \a Path absolute using the current directory if it is not already.
    215   /// An empty \a Path will result in the current directory.
    216   ///
    217   /// /absolute/path   => /absolute/path
    218   /// relative/../path => <current-directory>/relative/../path
    219   ///
    220   /// \param Path A path that is modified to be an absolute path.
    221   /// \returns success if \a path has been made absolute, otherwise a
    222   ///          platform-specific error_code.
    223   std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const;
    224 };
    225 
    226 /// \brief Gets an \p vfs::FileSystem for the 'real' file system, as seen by
    227 /// the operating system.
    228 IntrusiveRefCntPtr<FileSystem> getRealFileSystem();
    229 
    230 /// \brief A file system that allows overlaying one \p AbstractFileSystem on top
    231 /// of another.
    232 ///
    233 /// Consists of a stack of >=1 \p FileSystem objects, which are treated as being
    234 /// one merged file system. When there is a directory that exists in more than
    235 /// one file system, the \p OverlayFileSystem contains a directory containing
    236 /// the union of their contents.  The attributes (permissions, etc.) of the
    237 /// top-most (most recently added) directory are used.  When there is a file
    238 /// that exists in more than one file system, the file in the top-most file
    239 /// system overrides the other(s).
    240 class OverlayFileSystem : public FileSystem {
    241   typedef SmallVector<IntrusiveRefCntPtr<FileSystem>, 1> FileSystemList;
    242   /// \brief The stack of file systems, implemented as a list in order of
    243   /// their addition.
    244   FileSystemList FSList;
    245 
    246 public:
    247   OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> Base);
    248   /// \brief Pushes a file system on top of the stack.
    249   void pushOverlay(IntrusiveRefCntPtr<FileSystem> FS);
    250 
    251   llvm::ErrorOr<Status> status(const Twine &Path) override;
    252   llvm::ErrorOr<std::unique_ptr<File>>
    253   openFileForRead(const Twine &Path) override;
    254   directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
    255   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
    256   std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
    257 
    258   typedef FileSystemList::reverse_iterator iterator;
    259 
    260   /// \brief Get an iterator pointing to the most recently added file system.
    261   iterator overlays_begin() { return FSList.rbegin(); }
    262 
    263   /// \brief Get an iterator pointing one-past the least recently added file
    264   /// system.
    265   iterator overlays_end() { return FSList.rend(); }
    266 };
    267 
    268 namespace detail {
    269 class InMemoryDirectory;
    270 } // end namespace detail
    271 
    272 /// An in-memory file system.
    273 class InMemoryFileSystem : public FileSystem {
    274   std::unique_ptr<detail::InMemoryDirectory> Root;
    275   std::string WorkingDirectory;
    276   bool UseNormalizedPaths = true;
    277 
    278 public:
    279   explicit InMemoryFileSystem(bool UseNormalizedPaths = true);
    280   ~InMemoryFileSystem() override;
    281   /// Add a buffer to the VFS with a path. The VFS owns the buffer.
    282   /// \return true if the file was successfully added, false if the file already
    283   /// exists in the file system with different contents.
    284   bool addFile(const Twine &Path, time_t ModificationTime,
    285                std::unique_ptr<llvm::MemoryBuffer> Buffer);
    286   /// Add a buffer to the VFS with a path. The VFS does not own the buffer.
    287   /// \return true if the file was successfully added, false if the file already
    288   /// exists in the file system with different contents.
    289   bool addFileNoOwn(const Twine &Path, time_t ModificationTime,
    290                     llvm::MemoryBuffer *Buffer);
    291   std::string toString() const;
    292   /// Return true if this file system normalizes . and .. in paths.
    293   bool useNormalizedPaths() const { return UseNormalizedPaths; }
    294 
    295   llvm::ErrorOr<Status> status(const Twine &Path) override;
    296   llvm::ErrorOr<std::unique_ptr<File>>
    297   openFileForRead(const Twine &Path) override;
    298   directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
    299   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
    300     return WorkingDirectory;
    301   }
    302   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
    303     WorkingDirectory = Path.str();
    304     return std::error_code();
    305   }
    306 };
    307 
    308 /// \brief Get a globally unique ID for a virtual file or directory.
    309 llvm::sys::fs::UniqueID getNextVirtualUniqueID();
    310 
    311 /// \brief Gets a \p FileSystem for a virtual file system described in YAML
    312 /// format.
    313 IntrusiveRefCntPtr<FileSystem>
    314 getVFSFromYAML(std::unique_ptr<llvm::MemoryBuffer> Buffer,
    315                llvm::SourceMgr::DiagHandlerTy DiagHandler,
    316                void *DiagContext = nullptr,
    317                IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem());
    318 
    319 struct YAMLVFSEntry {
    320   template <typename T1, typename T2> YAMLVFSEntry(T1 &&VPath, T2 &&RPath)
    321       : VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)) {}
    322   std::string VPath;
    323   std::string RPath;
    324 };
    325 
    326 class YAMLVFSWriter {
    327   std::vector<YAMLVFSEntry> Mappings;
    328   Optional<bool> IsCaseSensitive;
    329 
    330 public:
    331   YAMLVFSWriter() {}
    332   void addFileMapping(StringRef VirtualPath, StringRef RealPath);
    333   void setCaseSensitivity(bool CaseSensitive) {
    334     IsCaseSensitive = CaseSensitive;
    335   }
    336   void write(llvm::raw_ostream &OS);
    337 };
    338 
    339 } // end namespace vfs
    340 } // end namespace clang
    341 #endif
    342