1 //===- VirtualFileSystem.h - Virtual File System Layer ----------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
11 /// Defines the virtual file system interface vfs::FileSystem.
13 //===----------------------------------------------------------------------===//
15 #ifndef LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H
16 #define LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H
18 #include "clang/Basic/LLVM.h"
19 #include "llvm/ADT/IntrusiveRefCntPtr.h"
20 #include "llvm/ADT/None.h"
21 #include "llvm/ADT/Optional.h"
22 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/ADT/Twine.h"
25 #include "llvm/Support/Chrono.h"
26 #include "llvm/Support/ErrorOr.h"
27 #include "llvm/Support/FileSystem.h"
28 #include "llvm/Support/SourceMgr.h"
35 #include <system_error>
48 /// The result of a \p status operation.
51 llvm::sys::fs::UniqueID UID;
52 llvm::sys::TimePoint<> MTime;
56 llvm::sys::fs::file_type Type = llvm::sys::fs::file_type::status_error;
57 llvm::sys::fs::perms Perms;
60 // FIXME: remove when files support multiple names
61 bool IsVFSMapped = false;
64 Status(const llvm::sys::fs::file_status &Status);
65 Status(StringRef Name, llvm::sys::fs::UniqueID UID,
66 llvm::sys::TimePoint<> MTime, uint32_t User, uint32_t Group,
67 uint64_t Size, llvm::sys::fs::file_type Type,
68 llvm::sys::fs::perms Perms);
70 /// Get a copy of a Status with a different name.
71 static Status copyWithNewName(const Status &In, StringRef NewName);
72 static Status copyWithNewName(const llvm::sys::fs::file_status &In,
75 /// Returns the name that should be used for this file or directory.
76 StringRef getName() const { return Name; }
78 /// @name Status interface from llvm::sys::fs
80 llvm::sys::fs::file_type getType() const { return Type; }
81 llvm::sys::fs::perms getPermissions() const { return Perms; }
82 llvm::sys::TimePoint<> getLastModificationTime() const { return MTime; }
83 llvm::sys::fs::UniqueID getUniqueID() const { return UID; }
84 uint32_t getUser() const { return User; }
85 uint32_t getGroup() const { return Group; }
86 uint64_t getSize() const { return Size; }
88 /// @name Status queries
89 /// These are static queries in llvm::sys::fs.
91 bool equivalent(const Status &Other) const;
92 bool isDirectory() const;
93 bool isRegularFile() const;
95 bool isSymlink() const;
96 bool isStatusKnown() const;
101 /// Represents an open file.
104 /// Destroy the file after closing it (if open).
105 /// Sub-classes should generally call close() inside their destructors. We
106 /// cannot do that from the base class, since close is virtual.
109 /// Get the status of the file.
110 virtual llvm::ErrorOr<Status> status() = 0;
112 /// Get the name of the file
113 virtual llvm::ErrorOr<std::string> getName() {
114 if (auto Status = status())
115 return Status->getName().str();
117 return Status.getError();
120 /// Get the contents of the file as a \p MemoryBuffer.
121 virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
122 getBuffer(const Twine &Name, int64_t FileSize = -1,
123 bool RequiresNullTerminator = true, bool IsVolatile = false) = 0;
126 virtual std::error_code close() = 0;
131 /// An interface for virtual file systems to provide an iterator over the
132 /// (non-recursive) contents of a directory.
134 virtual ~DirIterImpl();
136 /// Sets \c CurrentEntry to the next entry in the directory on success,
137 /// or returns a system-defined \c error_code.
138 virtual std::error_code increment() = 0;
143 } // namespace detail
145 /// An input iterator over the entries in a virtual path, similar to
146 /// llvm::sys::fs::directory_iterator.
147 class directory_iterator {
148 std::shared_ptr<detail::DirIterImpl> Impl; // Input iterator semantics on copy
151 directory_iterator(std::shared_ptr<detail::DirIterImpl> I)
152 : Impl(std::move(I)) {
153 assert(Impl.get() != nullptr && "requires non-null implementation");
154 if (!Impl->CurrentEntry.isStatusKnown())
155 Impl.reset(); // Normalize the end iterator to Impl == nullptr.
158 /// Construct an 'end' iterator.
159 directory_iterator() = default;
161 /// Equivalent to operator++, with an error code.
162 directory_iterator &increment(std::error_code &EC) {
163 assert(Impl && "attempting to increment past end");
164 EC = Impl->increment();
165 if (!Impl->CurrentEntry.isStatusKnown())
166 Impl.reset(); // Normalize the end iterator to Impl == nullptr.
170 const Status &operator*() const { return Impl->CurrentEntry; }
171 const Status *operator->() const { return &Impl->CurrentEntry; }
173 bool operator==(const directory_iterator &RHS) const {
174 if (Impl && RHS.Impl)
175 return Impl->CurrentEntry.equivalent(RHS.Impl->CurrentEntry);
176 return !Impl && !RHS.Impl;
178 bool operator!=(const directory_iterator &RHS) const {
179 return !(*this == RHS);
185 /// An input iterator over the recursive contents of a virtual path,
186 /// similar to llvm::sys::fs::recursive_directory_iterator.
187 class recursive_directory_iterator {
189 std::stack<directory_iterator, std::vector<directory_iterator>>;
192 std::shared_ptr<IterState> State; // Input iterator semantics on copy.
195 recursive_directory_iterator(FileSystem &FS, const Twine &Path,
196 std::error_code &EC);
198 /// Construct an 'end' iterator.
199 recursive_directory_iterator() = default;
201 /// Equivalent to operator++, with an error code.
202 recursive_directory_iterator &increment(std::error_code &EC);
204 const Status &operator*() const { return *State->top(); }
205 const Status *operator->() const { return &*State->top(); }
207 bool operator==(const recursive_directory_iterator &Other) const {
208 return State == Other.State; // identity
210 bool operator!=(const recursive_directory_iterator &RHS) const {
211 return !(*this == RHS);
214 /// Gets the current level. Starting path is at level 0.
216 assert(!State->empty() && "Cannot get level without any iteration state");
217 return State->size()-1;
221 /// The virtual file system interface.
222 class FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem> {
224 virtual ~FileSystem();
226 /// Get the status of the entry at \p Path, if one exists.
227 virtual llvm::ErrorOr<Status> status(const Twine &Path) = 0;
229 /// Get a \p File object for the file at \p Path, if one exists.
230 virtual llvm::ErrorOr<std::unique_ptr<File>>
231 openFileForRead(const Twine &Path) = 0;
233 /// This is a convenience method that opens a file, gets its content and then
235 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
236 getBufferForFile(const Twine &Name, int64_t FileSize = -1,
237 bool RequiresNullTerminator = true, bool IsVolatile = false);
239 /// Get a directory_iterator for \p Dir.
240 /// \note The 'end' iterator is directory_iterator().
241 virtual directory_iterator dir_begin(const Twine &Dir,
242 std::error_code &EC) = 0;
244 /// Set the working directory. This will affect all following operations on
245 /// this file system and may propagate down for nested file systems.
246 virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) = 0;
248 /// Get the working directory of this file system.
249 virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const = 0;
251 /// Gets real path of \p Path e.g. collapse all . and .. patterns, resolve
252 /// symlinks. For real file system, this uses `llvm::sys::fs::real_path`.
253 /// This returns errc::operation_not_permitted if not implemented by subclass.
254 virtual std::error_code getRealPath(const Twine &Path,
255 SmallVectorImpl<char> &Output) const;
257 /// Check whether a file exists. Provided for convenience.
258 bool exists(const Twine &Path);
260 /// Make \a Path an absolute path.
262 /// Makes \a Path absolute using the current directory if it is not already.
263 /// An empty \a Path will result in the current directory.
265 /// /absolute/path => /absolute/path
266 /// relative/../path => <current-directory>/relative/../path
268 /// \param Path A path that is modified to be an absolute path.
269 /// \returns success if \a path has been made absolute, otherwise a
270 /// platform-specific error_code.
271 std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const;
274 /// Gets an \p vfs::FileSystem for the 'real' file system, as seen by
275 /// the operating system.
276 IntrusiveRefCntPtr<FileSystem> getRealFileSystem();
278 /// A file system that allows overlaying one \p AbstractFileSystem on top
281 /// Consists of a stack of >=1 \p FileSystem objects, which are treated as being
282 /// one merged file system. When there is a directory that exists in more than
283 /// one file system, the \p OverlayFileSystem contains a directory containing
284 /// the union of their contents. The attributes (permissions, etc.) of the
285 /// top-most (most recently added) directory are used. When there is a file
286 /// that exists in more than one file system, the file in the top-most file
287 /// system overrides the other(s).
288 class OverlayFileSystem : public FileSystem {
289 using FileSystemList = SmallVector<IntrusiveRefCntPtr<FileSystem>, 1>;
291 /// The stack of file systems, implemented as a list in order of
293 FileSystemList FSList;
296 OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> Base);
298 /// Pushes a file system on top of the stack.
299 void pushOverlay(IntrusiveRefCntPtr<FileSystem> FS);
301 llvm::ErrorOr<Status> status(const Twine &Path) override;
302 llvm::ErrorOr<std::unique_ptr<File>>
303 openFileForRead(const Twine &Path) override;
304 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
305 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
306 std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
307 std::error_code getRealPath(const Twine &Path,
308 SmallVectorImpl<char> &Output) const override;
310 using iterator = FileSystemList::reverse_iterator;
311 using const_iterator = FileSystemList::const_reverse_iterator;
313 /// Get an iterator pointing to the most recently added file system.
314 iterator overlays_begin() { return FSList.rbegin(); }
315 const_iterator overlays_begin() const { return FSList.rbegin(); }
317 /// Get an iterator pointing one-past the least recently added file
319 iterator overlays_end() { return FSList.rend(); }
320 const_iterator overlays_end() const { return FSList.rend(); }
325 class InMemoryDirectory;
327 } // namespace detail
329 /// An in-memory file system.
330 class InMemoryFileSystem : public FileSystem {
331 std::unique_ptr<detail::InMemoryDirectory> Root;
332 std::string WorkingDirectory;
333 bool UseNormalizedPaths = true;
336 explicit InMemoryFileSystem(bool UseNormalizedPaths = true);
337 ~InMemoryFileSystem() override;
339 /// Add a file containing a buffer or a directory to the VFS with a
340 /// path. The VFS owns the buffer. If present, User, Group, Type
341 /// and Perms apply to the newly-created file or directory.
342 /// \return true if the file or directory was successfully added,
343 /// false if the file or directory already exists in the file system with
344 /// different contents.
345 bool addFile(const Twine &Path, time_t ModificationTime,
346 std::unique_ptr<llvm::MemoryBuffer> Buffer,
347 Optional<uint32_t> User = None, Optional<uint32_t> Group = None,
348 Optional<llvm::sys::fs::file_type> Type = None,
349 Optional<llvm::sys::fs::perms> Perms = None);
351 /// Add a buffer to the VFS with a path. The VFS does not own the buffer.
352 /// If present, User, Group, Type and Perms apply to the newly-created file
354 /// \return true if the file or directory was successfully added,
355 /// false if the file or directory already exists in the file system with
356 /// different contents.
357 bool addFileNoOwn(const Twine &Path, time_t ModificationTime,
358 llvm::MemoryBuffer *Buffer,
359 Optional<uint32_t> User = None,
360 Optional<uint32_t> Group = None,
361 Optional<llvm::sys::fs::file_type> Type = None,
362 Optional<llvm::sys::fs::perms> Perms = None);
364 std::string toString() const;
366 /// Return true if this file system normalizes . and .. in paths.
367 bool useNormalizedPaths() const { return UseNormalizedPaths; }
369 llvm::ErrorOr<Status> status(const Twine &Path) override;
370 llvm::ErrorOr<std::unique_ptr<File>>
371 openFileForRead(const Twine &Path) override;
372 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
374 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
375 return WorkingDirectory;
377 /// Canonicalizes \p Path by combining with the current working
378 /// directory and normalizing the path (e.g. remove dots). If the current
379 /// working directory is not set, this returns errc::operation_not_permitted.
381 /// This doesn't resolve symlinks as they are not supported in in-memory file
383 std::error_code getRealPath(const Twine &Path,
384 SmallVectorImpl<char> &Output) const override;
386 std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
389 /// Get a globally unique ID for a virtual file or directory.
390 llvm::sys::fs::UniqueID getNextVirtualUniqueID();
392 /// Gets a \p FileSystem for a virtual file system described in YAML
394 IntrusiveRefCntPtr<FileSystem>
395 getVFSFromYAML(std::unique_ptr<llvm::MemoryBuffer> Buffer,
396 llvm::SourceMgr::DiagHandlerTy DiagHandler,
397 StringRef YAMLFilePath,
398 void *DiagContext = nullptr,
399 IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem());
401 struct YAMLVFSEntry {
402 template <typename T1, typename T2> YAMLVFSEntry(T1 &&VPath, T2 &&RPath)
403 : VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)) {}
408 /// Collect all pairs of <virtual path, real path> entries from the
409 /// \p YAMLFilePath. This is used by the module dependency collector to forward
410 /// the entries into the reproducer output VFS YAML file.
411 void collectVFSFromYAML(
412 std::unique_ptr<llvm::MemoryBuffer> Buffer,
413 llvm::SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath,
414 SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
415 void *DiagContext = nullptr,
416 IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem());
418 class YAMLVFSWriter {
419 std::vector<YAMLVFSEntry> Mappings;
420 Optional<bool> IsCaseSensitive;
421 Optional<bool> IsOverlayRelative;
422 Optional<bool> UseExternalNames;
423 Optional<bool> IgnoreNonExistentContents;
424 std::string OverlayDir;
427 YAMLVFSWriter() = default;
429 void addFileMapping(StringRef VirtualPath, StringRef RealPath);
431 void setCaseSensitivity(bool CaseSensitive) {
432 IsCaseSensitive = CaseSensitive;
435 void setUseExternalNames(bool UseExtNames) {
436 UseExternalNames = UseExtNames;
439 void setIgnoreNonExistentContents(bool IgnoreContents) {
440 IgnoreNonExistentContents = IgnoreContents;
443 void setOverlayDir(StringRef OverlayDirectory) {
444 IsOverlayRelative = true;
445 OverlayDir.assign(OverlayDirectory.str());
448 void write(llvm::raw_ostream &OS);
454 #endif // LLVM_CLANG_BASIC_VIRTUALFILESYSTEM_H