blob: 3d5e1ad24a24ca555497e08f0c4929bf83ef9d79 [file] [log] [blame]
Ben Langmuirc8130a72014-02-20 21:59:23 +00001//===- VirtualFileSystem.cpp - 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// This file implements the VirtualFileSystem interface.
10//===----------------------------------------------------------------------===//
11
12#include "clang/Basic/VirtualFileSystem.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000013#include "llvm/ADT/DenseMap.h"
Justin Bogner9c785292014-05-20 21:43:27 +000014#include "llvm/ADT/iterator_range.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000015#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/StringExtras.h"
Ben Langmuir740812b2014-06-24 19:37:16 +000017#include "llvm/ADT/StringSet.h"
Rafael Espindola71de0b62014-06-13 17:20:50 +000018#include "llvm/Support/Errc.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000019#include "llvm/Support/MemoryBuffer.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000020#include "llvm/Support/Path.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000021#include "llvm/Support/YAMLParser.h"
Benjamin Kramer4527fb22014-03-02 17:08:31 +000022#include <atomic>
Ahmed Charlesdfca6f92014-03-09 11:36:40 +000023#include <memory>
Ben Langmuirc8130a72014-02-20 21:59:23 +000024
25using namespace clang;
26using namespace clang::vfs;
27using namespace llvm;
28using llvm::sys::fs::file_status;
29using llvm::sys::fs::file_type;
30using llvm::sys::fs::perms;
31using llvm::sys::fs::UniqueID;
32
33Status::Status(const file_status &Status)
34 : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
35 User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
Ben Langmuir5de00f32014-05-23 18:15:47 +000036 Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false) {}
Ben Langmuirc8130a72014-02-20 21:59:23 +000037
38Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
39 sys::TimeValue MTime, uint32_t User, uint32_t Group,
40 uint64_t Size, file_type Type, perms Perms)
Ben Langmuirb59cf672014-02-27 00:25:12 +000041 : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
Ben Langmuir5de00f32014-05-23 18:15:47 +000042 Type(Type), Perms(Perms), IsVFSMapped(false) {}
Ben Langmuirc8130a72014-02-20 21:59:23 +000043
44bool Status::equivalent(const Status &Other) const {
45 return getUniqueID() == Other.getUniqueID();
46}
47bool Status::isDirectory() const {
48 return Type == file_type::directory_file;
49}
50bool Status::isRegularFile() const {
51 return Type == file_type::regular_file;
52}
53bool Status::isOther() const {
54 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
55}
56bool Status::isSymlink() const {
57 return Type == file_type::symlink_file;
58}
59bool Status::isStatusKnown() const {
60 return Type != file_type::status_error;
61}
62bool Status::exists() const {
63 return isStatusKnown() && Type != file_type::file_not_found;
64}
65
66File::~File() {}
67
68FileSystem::~FileSystem() {}
69
Rafael Espindola8e650d72014-06-12 20:37:59 +000070std::error_code FileSystem::getBufferForFile(
71 const llvm::Twine &Name, std::unique_ptr<MemoryBuffer> &Result,
72 int64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) {
Ahmed Charlesb8984322014-03-07 20:03:18 +000073 std::unique_ptr<File> F;
Rafael Espindola8e650d72014-06-12 20:37:59 +000074 if (std::error_code EC = openFileForRead(Name, F))
Ben Langmuirc8130a72014-02-20 21:59:23 +000075 return EC;
76
Rafael Espindola8e650d72014-06-12 20:37:59 +000077 std::error_code EC =
78 F->getBuffer(Name, Result, FileSize, RequiresNullTerminator, IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +000079 return EC;
80}
81
82//===-----------------------------------------------------------------------===/
83// RealFileSystem implementation
84//===-----------------------------------------------------------------------===/
85
Benjamin Kramer3d6220d2014-03-01 17:21:22 +000086namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +000087/// \brief Wrapper around a raw file descriptor.
88class RealFile : public File {
89 int FD;
Ben Langmuird066d4c2014-02-28 21:16:07 +000090 Status S;
Ben Langmuirc8130a72014-02-20 21:59:23 +000091 friend class RealFileSystem;
92 RealFile(int FD) : FD(FD) {
93 assert(FD >= 0 && "Invalid or inactive file descriptor");
94 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +000095
Ben Langmuirc8130a72014-02-20 21:59:23 +000096public:
97 ~RealFile();
Craig Toppera798a9d2014-03-02 09:32:10 +000098 ErrorOr<Status> status() override;
Rafael Espindola8e650d72014-06-12 20:37:59 +000099 std::error_code getBuffer(const Twine &Name,
100 std::unique_ptr<MemoryBuffer> &Result,
101 int64_t FileSize = -1,
102 bool RequiresNullTerminator = true,
103 bool IsVolatile = false) override;
104 std::error_code close() override;
Craig Toppera798a9d2014-03-02 09:32:10 +0000105 void setName(StringRef Name) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000106};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000107} // end anonymous namespace
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000108RealFile::~RealFile() { close(); }
Ben Langmuirc8130a72014-02-20 21:59:23 +0000109
110ErrorOr<Status> RealFile::status() {
111 assert(FD != -1 && "cannot stat closed file");
Ben Langmuird066d4c2014-02-28 21:16:07 +0000112 if (!S.isStatusKnown()) {
113 file_status RealStatus;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000114 if (std::error_code EC = sys::fs::status(FD, RealStatus))
Ben Langmuird066d4c2014-02-28 21:16:07 +0000115 return EC;
116 Status NewS(RealStatus);
117 NewS.setName(S.getName());
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000118 S = std::move(NewS);
Ben Langmuird066d4c2014-02-28 21:16:07 +0000119 }
120 return S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000121}
122
Rafael Espindola8e650d72014-06-12 20:37:59 +0000123std::error_code RealFile::getBuffer(const Twine &Name,
124 std::unique_ptr<MemoryBuffer> &Result,
125 int64_t FileSize,
126 bool RequiresNullTerminator,
127 bool IsVolatile) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000128 assert(FD != -1 && "cannot get buffer for closed file");
129 return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +0000130 RequiresNullTerminator, IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000131}
132
133// FIXME: This is terrible, we need this for ::close.
134#if !defined(_MSC_VER) && !defined(__MINGW32__)
135#include <unistd.h>
136#include <sys/uio.h>
137#else
138#include <io.h>
139#ifndef S_ISFIFO
140#define S_ISFIFO(x) (0)
141#endif
142#endif
Rafael Espindola8e650d72014-06-12 20:37:59 +0000143std::error_code RealFile::close() {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000144 if (::close(FD))
Rafael Espindola8e650d72014-06-12 20:37:59 +0000145 return std::error_code(errno, std::generic_category());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000146 FD = -1;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000147 return std::error_code();
Ben Langmuirc8130a72014-02-20 21:59:23 +0000148}
149
Ben Langmuird066d4c2014-02-28 21:16:07 +0000150void RealFile::setName(StringRef Name) {
151 S.setName(Name);
152}
153
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000154namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000155/// \brief The file system according to your operating system.
156class RealFileSystem : public FileSystem {
157public:
Craig Toppera798a9d2014-03-02 09:32:10 +0000158 ErrorOr<Status> status(const Twine &Path) override;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000159 std::error_code openFileForRead(const Twine &Path,
160 std::unique_ptr<File> &Result) override;
Ben Langmuir740812b2014-06-24 19:37:16 +0000161 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000162};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000163} // end anonymous namespace
Ben Langmuirc8130a72014-02-20 21:59:23 +0000164
165ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
166 sys::fs::file_status RealStatus;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000167 if (std::error_code EC = sys::fs::status(Path, RealStatus))
Ben Langmuirc8130a72014-02-20 21:59:23 +0000168 return EC;
169 Status Result(RealStatus);
170 Result.setName(Path.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000171 return Result;
172}
173
Rafael Espindola8e650d72014-06-12 20:37:59 +0000174std::error_code RealFileSystem::openFileForRead(const Twine &Name,
175 std::unique_ptr<File> &Result) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000176 int FD;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000177 if (std::error_code EC = sys::fs::openFileForRead(Name, FD))
Ben Langmuirc8130a72014-02-20 21:59:23 +0000178 return EC;
179 Result.reset(new RealFile(FD));
Ben Langmuird066d4c2014-02-28 21:16:07 +0000180 Result->setName(Name.str());
Rafael Espindola8e650d72014-06-12 20:37:59 +0000181 return std::error_code();
Ben Langmuirc8130a72014-02-20 21:59:23 +0000182}
183
184IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
185 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
186 return FS;
187}
188
Ben Langmuir740812b2014-06-24 19:37:16 +0000189namespace {
190class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
191 std::string Path;
192 llvm::sys::fs::directory_iterator Iter;
193public:
194 RealFSDirIter(const Twine &_Path, std::error_code &EC)
195 : Path(_Path.str()), Iter(Path, EC) {
196 if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
197 llvm::sys::fs::file_status S;
198 EC = Iter->status(S);
199 if (!EC) {
200 CurrentEntry = Status(S);
201 CurrentEntry.setName(Iter->path());
202 }
203 }
204 }
205
206 std::error_code increment() override {
207 std::error_code EC;
208 Iter.increment(EC);
209 if (EC) {
210 return EC;
211 } else if (Iter == llvm::sys::fs::directory_iterator()) {
212 CurrentEntry = Status();
213 } else {
214 llvm::sys::fs::file_status S;
215 EC = Iter->status(S);
216 CurrentEntry = Status(S);
217 CurrentEntry.setName(Iter->path());
218 }
219 return EC;
220 }
221};
222}
223
224directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
225 std::error_code &EC) {
226 return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
227}
228
Ben Langmuirc8130a72014-02-20 21:59:23 +0000229//===-----------------------------------------------------------------------===/
230// OverlayFileSystem implementation
231//===-----------------------------------------------------------------------===/
232OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
233 pushOverlay(BaseFS);
234}
235
236void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
237 FSList.push_back(FS);
238}
239
240ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
241 // FIXME: handle symlinks that cross file systems
242 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
243 ErrorOr<Status> Status = (*I)->status(Path);
Rafael Espindola71de0b62014-06-13 17:20:50 +0000244 if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuirc8130a72014-02-20 21:59:23 +0000245 return Status;
246 }
Rafael Espindola71de0b62014-06-13 17:20:50 +0000247 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000248}
249
Rafael Espindola8e650d72014-06-12 20:37:59 +0000250std::error_code
251OverlayFileSystem::openFileForRead(const llvm::Twine &Path,
252 std::unique_ptr<File> &Result) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000253 // FIXME: handle symlinks that cross file systems
254 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
Rafael Espindola8e650d72014-06-12 20:37:59 +0000255 std::error_code EC = (*I)->openFileForRead(Path, Result);
Rafael Espindola71de0b62014-06-13 17:20:50 +0000256 if (!EC || EC != llvm::errc::no_such_file_or_directory)
Ben Langmuirc8130a72014-02-20 21:59:23 +0000257 return EC;
258 }
Rafael Espindola71de0b62014-06-13 17:20:50 +0000259 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000260}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000261
Ben Langmuir740812b2014-06-24 19:37:16 +0000262clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
263
264namespace {
265class OverlayFSDirIterImpl : public clang::vfs::detail::DirIterImpl {
266 OverlayFileSystem &Overlays;
267 std::string Path;
268 OverlayFileSystem::iterator CurrentFS;
269 directory_iterator CurrentDirIter;
270 llvm::StringSet<> SeenNames;
271
272 std::error_code incrementFS() {
273 assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
274 ++CurrentFS;
275 for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
276 std::error_code EC;
277 CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
278 if (EC && EC != errc::no_such_file_or_directory)
279 return EC;
280 if (CurrentDirIter != directory_iterator())
281 break; // found
282 }
283 return std::error_code();
284 }
285
286 std::error_code incrementDirIter(bool IsFirstTime) {
287 assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
288 "incrementing past end");
289 std::error_code EC;
290 if (!IsFirstTime)
291 CurrentDirIter.increment(EC);
292 if (!EC && CurrentDirIter == directory_iterator())
293 EC = incrementFS();
294 return EC;
295 }
296
297 std::error_code incrementImpl(bool IsFirstTime) {
298 while (true) {
299 std::error_code EC = incrementDirIter(IsFirstTime);
300 if (EC || CurrentDirIter == directory_iterator()) {
301 CurrentEntry = Status();
302 return EC;
303 }
304 CurrentEntry = *CurrentDirIter;
305 StringRef Name = llvm::sys::path::filename(CurrentEntry.getName());
306 if (SeenNames.insert(Name))
307 return EC; // name not seen before
308 }
309 llvm_unreachable("returned above");
310 }
311
312public:
313 OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
314 std::error_code &EC)
315 : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
316 CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
317 EC = incrementImpl(true);
318 }
319
320 std::error_code increment() override { return incrementImpl(false); }
321};
322} // end anonymous namespace
323
324directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
325 std::error_code &EC) {
326 return directory_iterator(
327 std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
328}
329
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000330//===-----------------------------------------------------------------------===/
331// VFSFromYAML implementation
332//===-----------------------------------------------------------------------===/
333
334// Allow DenseMap<StringRef, ...>. This is useful below because we know all the
335// strings are literals and will outlive the map, and there is no reason to
336// store them.
337namespace llvm {
338 template<>
339 struct DenseMapInfo<StringRef> {
340 // This assumes that "" will never be a valid key.
341 static inline StringRef getEmptyKey() { return StringRef(""); }
342 static inline StringRef getTombstoneKey() { return StringRef(); }
343 static unsigned getHashValue(StringRef Val) { return HashString(Val); }
344 static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
345 };
346}
347
348namespace {
349
350enum EntryKind {
351 EK_Directory,
352 EK_File
353};
354
355/// \brief A single file or directory in the VFS.
356class Entry {
357 EntryKind Kind;
358 std::string Name;
359
360public:
361 virtual ~Entry();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000362 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
363 StringRef getName() const { return Name; }
364 EntryKind getKind() const { return Kind; }
365};
366
367class DirectoryEntry : public Entry {
368 std::vector<Entry *> Contents;
369 Status S;
370
371public:
372 virtual ~DirectoryEntry();
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000373 DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
374 : Entry(EK_Directory, Name), Contents(std::move(Contents)),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000375 S(std::move(S)) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000376 Status getStatus() { return S; }
377 typedef std::vector<Entry *>::iterator iterator;
378 iterator contents_begin() { return Contents.begin(); }
379 iterator contents_end() { return Contents.end(); }
380 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
381};
382
383class FileEntry : public Entry {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000384public:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000385 enum NameKind {
386 NK_NotSet,
387 NK_External,
388 NK_Virtual
389 };
390private:
391 std::string ExternalContentsPath;
392 NameKind UseName;
393public:
394 FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
395 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
396 UseName(UseName) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000397 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
Ben Langmuirb59cf672014-02-27 00:25:12 +0000398 /// \brief whether to use the external path as the name for this file.
Ben Langmuird066d4c2014-02-28 21:16:07 +0000399 bool useExternalName(bool GlobalUseExternalName) const {
400 return UseName == NK_NotSet ? GlobalUseExternalName
401 : (UseName == NK_External);
402 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000403 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
404};
405
Ben Langmuir740812b2014-06-24 19:37:16 +0000406class VFSFromYAML;
407
408class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
409 std::string Dir;
410 VFSFromYAML &FS;
411 DirectoryEntry::iterator Current, End;
412public:
413 VFSFromYamlDirIterImpl(const Twine &Path, VFSFromYAML &FS,
414 DirectoryEntry::iterator Begin,
415 DirectoryEntry::iterator End, std::error_code &EC);
416 std::error_code increment() override;
417};
418
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000419/// \brief A virtual file system parsed from a YAML file.
420///
421/// Currently, this class allows creating virtual directories and mapping
422/// virtual file paths to existing external files, available in \c ExternalFS.
423///
424/// The basic structure of the parsed file is:
425/// \verbatim
426/// {
427/// 'version': <version number>,
428/// <optional configuration>
429/// 'roots': [
430/// <directory entries>
431/// ]
432/// }
433/// \endverbatim
434///
435/// All configuration options are optional.
436/// 'case-sensitive': <boolean, default=true>
Ben Langmuirb59cf672014-02-27 00:25:12 +0000437/// 'use-external-names': <boolean, default=true>
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000438///
439/// Virtual directories are represented as
440/// \verbatim
441/// {
442/// 'type': 'directory',
443/// 'name': <string>,
444/// 'contents': [ <file or directory entries> ]
445/// }
446/// \endverbatim
447///
448/// The default attributes for virtual directories are:
449/// \verbatim
450/// MTime = now() when created
451/// Perms = 0777
452/// User = Group = 0
453/// Size = 0
454/// UniqueID = unspecified unique value
455/// \endverbatim
456///
457/// Re-mapped files are represented as
458/// \verbatim
459/// {
460/// 'type': 'file',
461/// 'name': <string>,
Ben Langmuirb59cf672014-02-27 00:25:12 +0000462/// 'use-external-name': <boolean> # Optional
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000463/// 'external-contents': <path to external file>)
464/// }
465/// \endverbatim
466///
467/// and inherit their attributes from the external contents.
468///
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000469/// In both cases, the 'name' field may contain multiple path components (e.g.
470/// /path/to/file). However, any directory that contains more than one child
471/// must be uniquely represented by a directory entry.
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000472class VFSFromYAML : public vfs::FileSystem {
473 std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
474 /// \brief The file system to use for external references.
475 IntrusiveRefCntPtr<FileSystem> ExternalFS;
476
477 /// @name Configuration
478 /// @{
479
480 /// \brief Whether to perform case-sensitive comparisons.
481 ///
482 /// Currently, case-insensitive matching only works correctly with ASCII.
Ben Langmuirb59cf672014-02-27 00:25:12 +0000483 bool CaseSensitive;
484
485 /// \brief Whether to use to use the value of 'external-contents' for the
486 /// names of files. This global value is overridable on a per-file basis.
487 bool UseExternalNames;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000488 /// @}
489
490 friend class VFSFromYAMLParser;
491
492private:
493 VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
Ben Langmuirb59cf672014-02-27 00:25:12 +0000494 : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000495
496 /// \brief Looks up \p Path in \c Roots.
497 ErrorOr<Entry *> lookupPath(const Twine &Path);
498
499 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
500 /// recursing into the contents of \p From if it is a directory.
501 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
502 sys::path::const_iterator End, Entry *From);
503
Ben Langmuir740812b2014-06-24 19:37:16 +0000504 /// \brief Get the status of a given an \c Entry.
505 ErrorOr<Status> status(const Twine &Path, Entry *E);
506
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000507public:
508 ~VFSFromYAML();
509
510 /// \brief Parses \p Buffer, which is expected to be in YAML format and
511 /// returns a virtual file system representing its contents.
512 ///
513 /// Takes ownership of \p Buffer.
514 static VFSFromYAML *create(MemoryBuffer *Buffer,
515 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000516 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000517 IntrusiveRefCntPtr<FileSystem> ExternalFS);
518
Craig Toppera798a9d2014-03-02 09:32:10 +0000519 ErrorOr<Status> status(const Twine &Path) override;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000520 std::error_code openFileForRead(const Twine &Path,
521 std::unique_ptr<File> &Result) override;
Ben Langmuir740812b2014-06-24 19:37:16 +0000522
523 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
524 ErrorOr<Entry *> E = lookupPath(Dir);
525 if (!E) {
526 EC = E.getError();
527 return directory_iterator();
528 }
529 ErrorOr<Status> S = status(Dir, *E);
530 if (!S) {
531 EC = S.getError();
532 return directory_iterator();
533 }
534 if (!S->isDirectory()) {
535 EC = std::error_code(static_cast<int>(errc::not_a_directory),
536 std::system_category());
537 return directory_iterator();
538 }
539
540 DirectoryEntry *D = cast<DirectoryEntry>(*E);
541 return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
542 *this, D->contents_begin(), D->contents_end(), EC));
543 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000544};
545
546/// \brief A helper class to hold the common YAML parsing state.
547class VFSFromYAMLParser {
548 yaml::Stream &Stream;
549
550 void error(yaml::Node *N, const Twine &Msg) {
551 Stream.printError(N, Msg);
552 }
553
554 // false on error
555 bool parseScalarString(yaml::Node *N, StringRef &Result,
556 SmallVectorImpl<char> &Storage) {
557 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
558 if (!S) {
559 error(N, "expected string");
560 return false;
561 }
562 Result = S->getValue(Storage);
563 return true;
564 }
565
566 // false on error
567 bool parseScalarBool(yaml::Node *N, bool &Result) {
568 SmallString<5> Storage;
569 StringRef Value;
570 if (!parseScalarString(N, Value, Storage))
571 return false;
572
573 if (Value.equals_lower("true") || Value.equals_lower("on") ||
574 Value.equals_lower("yes") || Value == "1") {
575 Result = true;
576 return true;
577 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
578 Value.equals_lower("no") || Value == "0") {
579 Result = false;
580 return true;
581 }
582
583 error(N, "expected boolean value");
584 return false;
585 }
586
587 struct KeyStatus {
588 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
589 bool Required;
590 bool Seen;
591 };
592 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
593
594 // false on error
595 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
596 DenseMap<StringRef, KeyStatus> &Keys) {
597 if (!Keys.count(Key)) {
598 error(KeyNode, "unknown key");
599 return false;
600 }
601 KeyStatus &S = Keys[Key];
602 if (S.Seen) {
603 error(KeyNode, Twine("duplicate key '") + Key + "'");
604 return false;
605 }
606 S.Seen = true;
607 return true;
608 }
609
610 // false on error
611 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
612 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
613 E = Keys.end();
614 I != E; ++I) {
615 if (I->second.Required && !I->second.Seen) {
616 error(Obj, Twine("missing key '") + I->first + "'");
617 return false;
618 }
619 }
620 return true;
621 }
622
623 Entry *parseEntry(yaml::Node *N) {
624 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
625 if (!M) {
626 error(N, "expected mapping node for file or directory entry");
Craig Topperf1186c52014-05-08 06:41:40 +0000627 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000628 }
629
630 KeyStatusPair Fields[] = {
631 KeyStatusPair("name", true),
632 KeyStatusPair("type", true),
633 KeyStatusPair("contents", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000634 KeyStatusPair("external-contents", false),
635 KeyStatusPair("use-external-name", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000636 };
637
638 DenseMap<StringRef, KeyStatus> Keys(
639 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
640
641 bool HasContents = false; // external or otherwise
642 std::vector<Entry *> EntryArrayContents;
643 std::string ExternalContentsPath;
644 std::string Name;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000645 FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000646 EntryKind Kind;
647
648 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
649 ++I) {
650 StringRef Key;
651 // Reuse the buffer for key and value, since we don't look at key after
652 // parsing value.
653 SmallString<256> Buffer;
654 if (!parseScalarString(I->getKey(), Key, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000655 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000656
657 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000658 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000659
660 StringRef Value;
661 if (Key == "name") {
662 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000663 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000664 Name = Value;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000665 } else if (Key == "type") {
666 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000667 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000668 if (Value == "file")
669 Kind = EK_File;
670 else if (Value == "directory")
671 Kind = EK_Directory;
672 else {
673 error(I->getValue(), "unknown value for 'type'");
Craig Topperf1186c52014-05-08 06:41:40 +0000674 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000675 }
676 } else if (Key == "contents") {
677 if (HasContents) {
678 error(I->getKey(),
679 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000680 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000681 }
682 HasContents = true;
683 yaml::SequenceNode *Contents =
684 dyn_cast<yaml::SequenceNode>(I->getValue());
685 if (!Contents) {
686 // FIXME: this is only for directories, what about files?
687 error(I->getValue(), "expected array");
Craig Topperf1186c52014-05-08 06:41:40 +0000688 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000689 }
690
691 for (yaml::SequenceNode::iterator I = Contents->begin(),
692 E = Contents->end();
693 I != E; ++I) {
694 if (Entry *E = parseEntry(&*I))
695 EntryArrayContents.push_back(E);
696 else
Craig Topperf1186c52014-05-08 06:41:40 +0000697 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000698 }
699 } else if (Key == "external-contents") {
700 if (HasContents) {
701 error(I->getKey(),
702 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000703 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000704 }
705 HasContents = true;
706 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000707 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000708 ExternalContentsPath = Value;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000709 } else if (Key == "use-external-name") {
710 bool Val;
711 if (!parseScalarBool(I->getValue(), Val))
Craig Topperf1186c52014-05-08 06:41:40 +0000712 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000713 UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000714 } else {
715 llvm_unreachable("key missing from Keys");
716 }
717 }
718
719 if (Stream.failed())
Craig Topperf1186c52014-05-08 06:41:40 +0000720 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000721
722 // check for missing keys
723 if (!HasContents) {
724 error(N, "missing key 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000725 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000726 }
727 if (!checkMissingKeys(N, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000728 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000729
Ben Langmuirb59cf672014-02-27 00:25:12 +0000730 // check invalid configuration
731 if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
732 error(N, "'use-external-name' is not supported for directories");
Craig Topperf1186c52014-05-08 06:41:40 +0000733 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000734 }
735
Ben Langmuir93853232014-03-05 21:32:20 +0000736 // Remove trailing slash(es), being careful not to remove the root path
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000737 StringRef Trimmed(Name);
Ben Langmuir93853232014-03-05 21:32:20 +0000738 size_t RootPathLen = sys::path::root_path(Trimmed).size();
739 while (Trimmed.size() > RootPathLen &&
740 sys::path::is_separator(Trimmed.back()))
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000741 Trimmed = Trimmed.slice(0, Trimmed.size()-1);
742 // Get the last component
743 StringRef LastComponent = sys::path::filename(Trimmed);
744
Craig Topperf1186c52014-05-08 06:41:40 +0000745 Entry *Result = nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000746 switch (Kind) {
747 case EK_File:
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000748 Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000749 UseExternalName);
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000750 break;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000751 case EK_Directory:
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000752 Result = new DirectoryEntry(LastComponent, std::move(EntryArrayContents),
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000753 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
754 0, file_type::directory_file, sys::fs::all_all));
755 break;
756 }
757
758 StringRef Parent = sys::path::parent_path(Trimmed);
759 if (Parent.empty())
760 return Result;
761
762 // if 'name' contains multiple components, create implicit directory entries
763 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
764 E = sys::path::rend(Parent);
765 I != E; ++I) {
Benjamin Kramer3f755aa2014-03-10 17:55:02 +0000766 Result = new DirectoryEntry(*I, llvm::makeArrayRef(Result),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000767 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
768 0, file_type::directory_file, sys::fs::all_all));
769 }
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000770 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000771 }
772
773public:
774 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
775
776 // false on error
777 bool parse(yaml::Node *Root, VFSFromYAML *FS) {
778 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
779 if (!Top) {
780 error(Root, "expected mapping node");
781 return false;
782 }
783
784 KeyStatusPair Fields[] = {
785 KeyStatusPair("version", true),
786 KeyStatusPair("case-sensitive", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000787 KeyStatusPair("use-external-names", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000788 KeyStatusPair("roots", true),
789 };
790
791 DenseMap<StringRef, KeyStatus> Keys(
792 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
793
794 // Parse configuration and 'roots'
795 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
796 ++I) {
797 SmallString<10> KeyBuffer;
798 StringRef Key;
799 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
800 return false;
801
802 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
803 return false;
804
805 if (Key == "roots") {
806 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
807 if (!Roots) {
808 error(I->getValue(), "expected array");
809 return false;
810 }
811
812 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
813 I != E; ++I) {
814 if (Entry *E = parseEntry(&*I))
815 FS->Roots.push_back(E);
816 else
817 return false;
818 }
819 } else if (Key == "version") {
820 StringRef VersionString;
821 SmallString<4> Storage;
822 if (!parseScalarString(I->getValue(), VersionString, Storage))
823 return false;
824 int Version;
825 if (VersionString.getAsInteger<int>(10, Version)) {
826 error(I->getValue(), "expected integer");
827 return false;
828 }
829 if (Version < 0) {
830 error(I->getValue(), "invalid version number");
831 return false;
832 }
833 if (Version != 0) {
834 error(I->getValue(), "version mismatch, expected 0");
835 return false;
836 }
837 } else if (Key == "case-sensitive") {
838 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
839 return false;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000840 } else if (Key == "use-external-names") {
841 if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
842 return false;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000843 } else {
844 llvm_unreachable("key missing from Keys");
845 }
846 }
847
848 if (Stream.failed())
849 return false;
850
851 if (!checkMissingKeys(Top, Keys))
852 return false;
853 return true;
854 }
855};
856} // end of anonymous namespace
857
858Entry::~Entry() {}
859DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
860
861VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
862
863VFSFromYAML *VFSFromYAML::create(MemoryBuffer *Buffer,
864 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000865 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000866 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
867
868 SourceMgr SM;
869 yaml::Stream Stream(Buffer, SM);
870
Ben Langmuir97882e72014-02-24 20:56:37 +0000871 SM.setDiagHandler(DiagHandler, DiagContext);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000872 yaml::document_iterator DI = Stream.begin();
873 yaml::Node *Root = DI->getRoot();
874 if (DI == Stream.end() || !Root) {
875 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
Craig Topperf1186c52014-05-08 06:41:40 +0000876 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000877 }
878
879 VFSFromYAMLParser P(Stream);
880
Ahmed Charlesb8984322014-03-07 20:03:18 +0000881 std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000882 if (!P.parse(Root, FS.get()))
Craig Topperf1186c52014-05-08 06:41:40 +0000883 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000884
Ahmed Charles9a16beb2014-03-07 19:33:25 +0000885 return FS.release();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000886}
887
888ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +0000889 SmallString<256> Path;
890 Path_.toVector(Path);
891
892 // Handle relative paths
Rafael Espindola8e650d72014-06-12 20:37:59 +0000893 if (std::error_code EC = sys::fs::make_absolute(Path))
Ben Langmuira6f8ca82014-03-04 22:34:50 +0000894 return EC;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000895
896 if (Path.empty())
Rafael Espindola71de0b62014-06-13 17:20:50 +0000897 return make_error_code(llvm::errc::invalid_argument);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000898
899 sys::path::const_iterator Start = sys::path::begin(Path);
900 sys::path::const_iterator End = sys::path::end(Path);
901 for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
902 I != E; ++I) {
903 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
Rafael Espindola71de0b62014-06-13 17:20:50 +0000904 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000905 return Result;
906 }
Rafael Espindola71de0b62014-06-13 17:20:50 +0000907 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000908}
909
910ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
911 sys::path::const_iterator End,
912 Entry *From) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +0000913 if (Start->equals("."))
914 ++Start;
915
916 // FIXME: handle ..
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000917 if (CaseSensitive ? !Start->equals(From->getName())
918 : !Start->equals_lower(From->getName()))
919 // failure to match
Rafael Espindola71de0b62014-06-13 17:20:50 +0000920 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000921
922 ++Start;
923
924 if (Start == End) {
925 // Match!
926 return From;
927 }
928
929 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
930 if (!DE)
Rafael Espindola71de0b62014-06-13 17:20:50 +0000931 return make_error_code(llvm::errc::not_a_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000932
933 for (DirectoryEntry::iterator I = DE->contents_begin(),
934 E = DE->contents_end();
935 I != E; ++I) {
936 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
Rafael Espindola71de0b62014-06-13 17:20:50 +0000937 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000938 return Result;
939 }
Rafael Espindola71de0b62014-06-13 17:20:50 +0000940 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000941}
942
Ben Langmuir740812b2014-06-24 19:37:16 +0000943ErrorOr<Status> VFSFromYAML::status(const Twine &Path, Entry *E) {
944 assert(E != nullptr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000945 std::string PathStr(Path.str());
Ben Langmuir740812b2014-06-24 19:37:16 +0000946 if (FileEntry *F = dyn_cast<FileEntry>(E)) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000947 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
Ben Langmuirb59cf672014-02-27 00:25:12 +0000948 assert(!S || S->getName() == F->getExternalContentsPath());
Ben Langmuird066d4c2014-02-28 21:16:07 +0000949 if (S && !F->useExternalName(UseExternalNames))
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000950 S->setName(PathStr);
Ben Langmuir5de00f32014-05-23 18:15:47 +0000951 if (S)
952 S->IsVFSMapped = true;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000953 return S;
954 } else { // directory
Ben Langmuir740812b2014-06-24 19:37:16 +0000955 DirectoryEntry *DE = cast<DirectoryEntry>(E);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000956 Status S = DE->getStatus();
957 S.setName(PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000958 return S;
959 }
960}
961
Ben Langmuir740812b2014-06-24 19:37:16 +0000962ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
963 ErrorOr<Entry *> Result = lookupPath(Path);
964 if (!Result)
965 return Result.getError();
966 return status(Path, *Result);
967}
968
Rafael Espindola8e650d72014-06-12 20:37:59 +0000969std::error_code
970VFSFromYAML::openFileForRead(const Twine &Path,
971 std::unique_ptr<vfs::File> &Result) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000972 ErrorOr<Entry *> E = lookupPath(Path);
973 if (!E)
974 return E.getError();
975
976 FileEntry *F = dyn_cast<FileEntry>(*E);
977 if (!F) // FIXME: errc::not_a_file?
Rafael Espindola71de0b62014-06-13 17:20:50 +0000978 return make_error_code(llvm::errc::invalid_argument);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000979
Rafael Espindola8e650d72014-06-12 20:37:59 +0000980 if (std::error_code EC =
981 ExternalFS->openFileForRead(F->getExternalContentsPath(), Result))
Ben Langmuird066d4c2014-02-28 21:16:07 +0000982 return EC;
983
984 if (!F->useExternalName(UseExternalNames))
985 Result->setName(Path.str());
986
Rafael Espindola8e650d72014-06-12 20:37:59 +0000987 return std::error_code();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000988}
989
990IntrusiveRefCntPtr<FileSystem>
991vfs::getVFSFromYAML(MemoryBuffer *Buffer, SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000992 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000993 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Ben Langmuir97882e72014-02-24 20:56:37 +0000994 return VFSFromYAML::create(Buffer, DiagHandler, DiagContext, ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000995}
996
997UniqueID vfs::getNextVirtualUniqueID() {
Benjamin Kramer4527fb22014-03-02 17:08:31 +0000998 static std::atomic<unsigned> UID;
999 unsigned ID = ++UID;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001000 // The following assumes that uint64_t max will never collide with a real
1001 // dev_t value from the OS.
1002 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
1003}
Justin Bogner9c785292014-05-20 21:43:27 +00001004
1005#ifndef NDEBUG
1006static bool pathHasTraversal(StringRef Path) {
1007 using namespace llvm::sys;
1008 for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1009 if (Comp == "." || Comp == "..")
1010 return true;
1011 return false;
1012}
1013#endif
1014
1015void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1016 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1017 assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1018 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1019 Mappings.emplace_back(VirtualPath, RealPath);
1020}
1021
Justin Bogner44fa450342014-05-21 22:46:51 +00001022namespace {
1023class JSONWriter {
1024 llvm::raw_ostream &OS;
1025 SmallVector<StringRef, 16> DirStack;
1026 inline unsigned getDirIndent() { return 4 * DirStack.size(); }
1027 inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1028 bool containedIn(StringRef Parent, StringRef Path);
1029 StringRef containedPart(StringRef Parent, StringRef Path);
1030 void startDirectory(StringRef Path);
1031 void endDirectory();
1032 void writeEntry(StringRef VPath, StringRef RPath);
1033
1034public:
1035 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1036 void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> IsCaseSensitive);
1037};
Justin Bogner9c785292014-05-20 21:43:27 +00001038}
1039
Justin Bogner44fa450342014-05-21 22:46:51 +00001040bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
Justin Bogner1c078f22014-05-20 22:12:58 +00001041 using namespace llvm::sys;
1042 // Compare each path component.
1043 auto IParent = path::begin(Parent), EParent = path::end(Parent);
1044 for (auto IChild = path::begin(Path), EChild = path::end(Path);
1045 IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
1046 if (*IParent != *IChild)
1047 return false;
1048 }
1049 // Have we exhausted the parent path?
1050 return IParent == EParent;
Justin Bogner9c785292014-05-20 21:43:27 +00001051}
1052
Justin Bogner44fa450342014-05-21 22:46:51 +00001053StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1054 assert(!Parent.empty());
Justin Bogner9c785292014-05-20 21:43:27 +00001055 assert(containedIn(Parent, Path));
Justin Bogner9c785292014-05-20 21:43:27 +00001056 return Path.slice(Parent.size() + 1, StringRef::npos);
1057}
1058
Justin Bogner44fa450342014-05-21 22:46:51 +00001059void JSONWriter::startDirectory(StringRef Path) {
1060 StringRef Name =
1061 DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
1062 DirStack.push_back(Path);
1063 unsigned Indent = getDirIndent();
1064 OS.indent(Indent) << "{\n";
1065 OS.indent(Indent + 2) << "'type': 'directory',\n";
1066 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1067 OS.indent(Indent + 2) << "'contents': [\n";
1068}
1069
1070void JSONWriter::endDirectory() {
1071 unsigned Indent = getDirIndent();
1072 OS.indent(Indent + 2) << "]\n";
1073 OS.indent(Indent) << "}";
1074
1075 DirStack.pop_back();
1076}
1077
1078void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1079 unsigned Indent = getFileIndent();
1080 OS.indent(Indent) << "{\n";
1081 OS.indent(Indent + 2) << "'type': 'file',\n";
1082 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1083 OS.indent(Indent + 2) << "'external-contents': \""
1084 << llvm::yaml::escape(RPath) << "\"\n";
1085 OS.indent(Indent) << "}";
1086}
1087
1088void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1089 Optional<bool> IsCaseSensitive) {
1090 using namespace llvm::sys;
1091
1092 OS << "{\n"
1093 " 'version': 0,\n";
1094 if (IsCaseSensitive.hasValue())
1095 OS << " 'case-sensitive': '"
1096 << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
1097 OS << " 'roots': [\n";
1098
1099 if (Entries.empty())
1100 return;
1101
1102 const YAMLVFSEntry &Entry = Entries.front();
1103 startDirectory(path::parent_path(Entry.VPath));
1104 writeEntry(path::filename(Entry.VPath), Entry.RPath);
1105
1106 for (const auto &Entry : Entries.slice(1)) {
1107 StringRef Dir = path::parent_path(Entry.VPath);
1108 if (Dir == DirStack.back())
1109 OS << ",\n";
1110 else {
1111 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
1112 OS << "\n";
1113 endDirectory();
1114 }
1115 OS << ",\n";
1116 startDirectory(Dir);
1117 }
1118 writeEntry(path::filename(Entry.VPath), Entry.RPath);
1119 }
1120
1121 while (!DirStack.empty()) {
1122 OS << "\n";
1123 endDirectory();
1124 }
1125
1126 OS << "\n"
1127 << " ]\n"
1128 << "}\n";
1129}
1130
Justin Bogner9c785292014-05-20 21:43:27 +00001131void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
1132 std::sort(Mappings.begin(), Mappings.end(),
Justin Bogner44fa450342014-05-21 22:46:51 +00001133 [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
Justin Bogner9c785292014-05-20 21:43:27 +00001134 return LHS.VPath < RHS.VPath;
1135 });
1136
Justin Bogner44fa450342014-05-21 22:46:51 +00001137 JSONWriter(OS).write(Mappings, IsCaseSensitive);
Justin Bogner9c785292014-05-20 21:43:27 +00001138}
Ben Langmuir740812b2014-06-24 19:37:16 +00001139
1140VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(const Twine &_Path,
1141 VFSFromYAML &FS,
1142 DirectoryEntry::iterator Begin,
1143 DirectoryEntry::iterator End,
1144 std::error_code &EC)
1145 : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
1146 if (Current != End) {
1147 SmallString<128> PathStr(Dir);
1148 llvm::sys::path::append(PathStr, (*Current)->getName());
1149 llvm::ErrorOr<vfs::Status> S = FS.status(PathStr.str());
1150 if (S)
1151 CurrentEntry = *S;
1152 else
1153 EC = S.getError();
1154 }
1155}
1156
1157std::error_code VFSFromYamlDirIterImpl::increment() {
1158 assert(Current != End && "cannot iterate past end");
1159 if (++Current != End) {
1160 SmallString<128> PathStr(Dir);
1161 llvm::sys::path::append(PathStr, (*Current)->getName());
1162 llvm::ErrorOr<vfs::Status> S = FS.status(PathStr.str());
1163 if (!S)
1164 return S.getError();
1165 CurrentEntry = *S;
1166 } else {
1167 CurrentEntry = Status();
1168 }
1169 return std::error_code();
1170}
Ben Langmuir7c9f6c82014-06-25 20:25:40 +00001171
1172vfs::recursive_directory_iterator::recursive_directory_iterator(FileSystem &FS_,
1173 const Twine &Path,
1174 std::error_code &EC)
1175 : FS(&FS_) {
1176 directory_iterator I = FS->dir_begin(Path, EC);
1177 if (!EC && I != directory_iterator()) {
1178 State = std::make_shared<IterState>();
1179 State->push(I);
1180 }
1181}
1182
1183vfs::recursive_directory_iterator &
1184recursive_directory_iterator::increment(std::error_code &EC) {
1185 assert(FS && State && !State->empty() && "incrementing past end");
1186 assert(State->top()->isStatusKnown() && "non-canonical end iterator");
1187 vfs::directory_iterator End;
1188 if (State->top()->isDirectory()) {
1189 vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
1190 if (EC)
1191 return *this;
1192 if (I != End) {
1193 State->push(I);
1194 return *this;
1195 }
1196 }
1197
1198 while (!State->empty() && State->top().increment(EC) == End)
1199 State->pop();
1200
1201 if (State->empty())
1202 State.reset(); // end iterator
1203
1204 return *this;
1205}