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