blob: b258bc4252328df7ad16cc771d2bb18954b46f7b [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"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000014#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/StringExtras.h"
Ben Langmuir740812b2014-06-24 19:37:16 +000016#include "llvm/ADT/StringSet.h"
Chandler Carruth0d9593d2015-01-14 11:29:14 +000017#include "llvm/ADT/iterator_range.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
Benjamin Kramer268b51a2015-10-05 13:15:33 +000038Status::Status(StringRef Name, UniqueID UID, sys::TimeValue MTime,
39 uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
40 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
Benjamin Kramer268b51a2015-10-05 13:15:33 +000044Status Status::copyWithNewName(const Status &In, StringRef NewName) {
45 return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
46 In.getUser(), In.getGroup(), In.getSize(), In.getType(),
47 In.getPermissions());
48}
49
50Status Status::copyWithNewName(const file_status &In, StringRef NewName) {
51 return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
52 In.getUser(), In.getGroup(), In.getSize(), In.type(),
53 In.permissions());
54}
55
Ben Langmuirc8130a72014-02-20 21:59:23 +000056bool Status::equivalent(const Status &Other) const {
57 return getUniqueID() == Other.getUniqueID();
58}
59bool Status::isDirectory() const {
60 return Type == file_type::directory_file;
61}
62bool Status::isRegularFile() const {
63 return Type == file_type::regular_file;
64}
65bool Status::isOther() const {
66 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
67}
68bool Status::isSymlink() const {
69 return Type == file_type::symlink_file;
70}
71bool Status::isStatusKnown() const {
72 return Type != file_type::status_error;
73}
74bool Status::exists() const {
75 return isStatusKnown() && Type != file_type::file_not_found;
76}
77
78File::~File() {}
79
80FileSystem::~FileSystem() {}
81
Benjamin Kramera8857962014-10-26 22:44:13 +000082ErrorOr<std::unique_ptr<MemoryBuffer>>
83FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
84 bool RequiresNullTerminator, bool IsVolatile) {
85 auto F = openFileForRead(Name);
86 if (!F)
87 return F.getError();
Ben Langmuirc8130a72014-02-20 21:59:23 +000088
Benjamin Kramera8857962014-10-26 22:44:13 +000089 return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +000090}
91
92//===-----------------------------------------------------------------------===/
93// RealFileSystem implementation
94//===-----------------------------------------------------------------------===/
95
Benjamin Kramer3d6220d2014-03-01 17:21:22 +000096namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +000097/// \brief Wrapper around a raw file descriptor.
98class RealFile : public File {
99 int FD;
Ben Langmuird066d4c2014-02-28 21:16:07 +0000100 Status S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000101 friend class RealFileSystem;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000102 RealFile(int FD, StringRef NewName)
103 : FD(FD), S(NewName, {}, {}, {}, {}, {},
104 llvm::sys::fs::file_type::status_error, {}) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000105 assert(FD >= 0 && "Invalid or inactive file descriptor");
106 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000107
Ben Langmuirc8130a72014-02-20 21:59:23 +0000108public:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000109 ~RealFile() override;
Craig Toppera798a9d2014-03-02 09:32:10 +0000110 ErrorOr<Status> status() override;
Benjamin Kramera8857962014-10-26 22:44:13 +0000111 ErrorOr<std::unique_ptr<MemoryBuffer>>
112 getBuffer(const Twine &Name, int64_t FileSize = -1,
113 bool RequiresNullTerminator = true,
114 bool IsVolatile = false) override;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000115 std::error_code close() override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000116};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000117} // end anonymous namespace
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000118RealFile::~RealFile() { close(); }
Ben Langmuirc8130a72014-02-20 21:59:23 +0000119
120ErrorOr<Status> RealFile::status() {
121 assert(FD != -1 && "cannot stat closed file");
Ben Langmuird066d4c2014-02-28 21:16:07 +0000122 if (!S.isStatusKnown()) {
123 file_status RealStatus;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000124 if (std::error_code EC = sys::fs::status(FD, RealStatus))
Ben Langmuird066d4c2014-02-28 21:16:07 +0000125 return EC;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000126 S = Status::copyWithNewName(RealStatus, S.getName());
Ben Langmuird066d4c2014-02-28 21:16:07 +0000127 }
128 return S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000129}
130
Benjamin Kramera8857962014-10-26 22:44:13 +0000131ErrorOr<std::unique_ptr<MemoryBuffer>>
132RealFile::getBuffer(const Twine &Name, int64_t FileSize,
133 bool RequiresNullTerminator, bool IsVolatile) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000134 assert(FD != -1 && "cannot get buffer for closed file");
Benjamin Kramera8857962014-10-26 22:44:13 +0000135 return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
136 IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000137}
138
139// FIXME: This is terrible, we need this for ::close.
140#if !defined(_MSC_VER) && !defined(__MINGW32__)
141#include <unistd.h>
142#include <sys/uio.h>
143#else
144#include <io.h>
145#ifndef S_ISFIFO
146#define S_ISFIFO(x) (0)
147#endif
148#endif
Rafael Espindola8e650d72014-06-12 20:37:59 +0000149std::error_code RealFile::close() {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000150 if (::close(FD))
Rafael Espindola8e650d72014-06-12 20:37:59 +0000151 return std::error_code(errno, std::generic_category());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000152 FD = -1;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000153 return std::error_code();
Ben Langmuirc8130a72014-02-20 21:59:23 +0000154}
155
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000156namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000157/// \brief The file system according to your operating system.
158class RealFileSystem : public FileSystem {
159public:
Craig Toppera798a9d2014-03-02 09:32:10 +0000160 ErrorOr<Status> status(const Twine &Path) override;
Benjamin Kramera8857962014-10-26 22:44:13 +0000161 ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
Ben Langmuir740812b2014-06-24 19:37:16 +0000162 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000163};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000164} // end anonymous namespace
Ben Langmuirc8130a72014-02-20 21:59:23 +0000165
166ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
167 sys::fs::file_status RealStatus;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000168 if (std::error_code EC = sys::fs::status(Path, RealStatus))
Ben Langmuirc8130a72014-02-20 21:59:23 +0000169 return EC;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000170 return Status::copyWithNewName(RealStatus, Path.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000171}
172
Benjamin Kramera8857962014-10-26 22:44:13 +0000173ErrorOr<std::unique_ptr<File>>
174RealFileSystem::openFileForRead(const Twine &Name) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000175 int FD;
Rafael Espindola8e650d72014-06-12 20:37:59 +0000176 if (std::error_code EC = sys::fs::openFileForRead(Name, FD))
Ben Langmuirc8130a72014-02-20 21:59:23 +0000177 return EC;
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000178 return std::unique_ptr<File>(new RealFile(FD, Name.str()));
Ben Langmuirc8130a72014-02-20 21:59:23 +0000179}
180
181IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
182 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
183 return FS;
184}
185
Ben Langmuir740812b2014-06-24 19:37:16 +0000186namespace {
187class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
188 std::string Path;
189 llvm::sys::fs::directory_iterator Iter;
190public:
191 RealFSDirIter(const Twine &_Path, std::error_code &EC)
192 : Path(_Path.str()), Iter(Path, EC) {
193 if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
194 llvm::sys::fs::file_status S;
195 EC = Iter->status(S);
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000196 if (!EC)
197 CurrentEntry = Status::copyWithNewName(S, Iter->path());
Ben Langmuir740812b2014-06-24 19:37:16 +0000198 }
199 }
200
201 std::error_code increment() override {
202 std::error_code EC;
203 Iter.increment(EC);
204 if (EC) {
205 return EC;
206 } else if (Iter == llvm::sys::fs::directory_iterator()) {
207 CurrentEntry = Status();
208 } else {
209 llvm::sys::fs::file_status S;
210 EC = Iter->status(S);
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000211 CurrentEntry = Status::copyWithNewName(S, Iter->path());
Ben Langmuir740812b2014-06-24 19:37:16 +0000212 }
213 return EC;
214 }
215};
Alexander Kornienkoab9db512015-06-22 23:07:51 +0000216}
Ben Langmuir740812b2014-06-24 19:37:16 +0000217
218directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
219 std::error_code &EC) {
220 return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
221}
222
Ben Langmuirc8130a72014-02-20 21:59:23 +0000223//===-----------------------------------------------------------------------===/
224// OverlayFileSystem implementation
225//===-----------------------------------------------------------------------===/
226OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
227 pushOverlay(BaseFS);
228}
229
230void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
231 FSList.push_back(FS);
232}
233
234ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
235 // FIXME: handle symlinks that cross file systems
236 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
237 ErrorOr<Status> Status = (*I)->status(Path);
Rafael Espindola71de0b62014-06-13 17:20:50 +0000238 if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuirc8130a72014-02-20 21:59:23 +0000239 return Status;
240 }
Rafael Espindola71de0b62014-06-13 17:20:50 +0000241 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000242}
243
Benjamin Kramera8857962014-10-26 22:44:13 +0000244ErrorOr<std::unique_ptr<File>>
245OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000246 // FIXME: handle symlinks that cross file systems
247 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
Benjamin Kramera8857962014-10-26 22:44:13 +0000248 auto Result = (*I)->openFileForRead(Path);
249 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
250 return Result;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000251 }
Rafael Espindola71de0b62014-06-13 17:20:50 +0000252 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000253}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000254
Ben Langmuir740812b2014-06-24 19:37:16 +0000255clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
256
257namespace {
258class OverlayFSDirIterImpl : public clang::vfs::detail::DirIterImpl {
259 OverlayFileSystem &Overlays;
260 std::string Path;
261 OverlayFileSystem::iterator CurrentFS;
262 directory_iterator CurrentDirIter;
263 llvm::StringSet<> SeenNames;
264
265 std::error_code incrementFS() {
266 assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
267 ++CurrentFS;
268 for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
269 std::error_code EC;
270 CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
271 if (EC && EC != errc::no_such_file_or_directory)
272 return EC;
273 if (CurrentDirIter != directory_iterator())
274 break; // found
275 }
276 return std::error_code();
277 }
278
279 std::error_code incrementDirIter(bool IsFirstTime) {
280 assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
281 "incrementing past end");
282 std::error_code EC;
283 if (!IsFirstTime)
284 CurrentDirIter.increment(EC);
285 if (!EC && CurrentDirIter == directory_iterator())
286 EC = incrementFS();
287 return EC;
288 }
289
290 std::error_code incrementImpl(bool IsFirstTime) {
291 while (true) {
292 std::error_code EC = incrementDirIter(IsFirstTime);
293 if (EC || CurrentDirIter == directory_iterator()) {
294 CurrentEntry = Status();
295 return EC;
296 }
297 CurrentEntry = *CurrentDirIter;
298 StringRef Name = llvm::sys::path::filename(CurrentEntry.getName());
David Blaikie61b86d42014-11-19 02:56:13 +0000299 if (SeenNames.insert(Name).second)
Ben Langmuir740812b2014-06-24 19:37:16 +0000300 return EC; // name not seen before
301 }
302 llvm_unreachable("returned above");
303 }
304
305public:
306 OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
307 std::error_code &EC)
308 : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
309 CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
310 EC = incrementImpl(true);
311 }
312
313 std::error_code increment() override { return incrementImpl(false); }
314};
315} // end anonymous namespace
316
317directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
318 std::error_code &EC) {
319 return directory_iterator(
320 std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
321}
322
Benjamin Kramera25dcfd2015-10-05 13:55:14 +0000323namespace clang {
324namespace vfs {
325namespace detail {
326
327enum InMemoryNodeKind { IME_File, IME_Directory };
328
329/// The in memory file system is a tree of Nodes. Every node can either be a
330/// file or a directory.
331class InMemoryNode {
332 Status Stat;
333 InMemoryNodeKind Kind;
334
335public:
336 InMemoryNode(Status Stat, InMemoryNodeKind Kind)
337 : Stat(std::move(Stat)), Kind(Kind) {}
338 virtual ~InMemoryNode() {}
339 const Status &getStatus() const { return Stat; }
340 InMemoryNodeKind getKind() const { return Kind; }
341 virtual std::string toString(unsigned Indent) const = 0;
342};
343
344namespace {
345class InMemoryFile : public InMemoryNode {
346 std::unique_ptr<llvm::MemoryBuffer> Buffer;
347
348public:
349 InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
350 : InMemoryNode(std::move(Stat), IME_File), Buffer(std::move(Buffer)) {}
351
352 llvm::MemoryBuffer *getBuffer() { return Buffer.get(); }
353 std::string toString(unsigned Indent) const override {
354 return (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
355 }
356 static bool classof(const InMemoryNode *N) {
357 return N->getKind() == IME_File;
358 }
359};
360
361/// Adapt a InMemoryFile for VFS' File interface.
362class InMemoryFileAdaptor : public File {
363 InMemoryFile &Node;
364
365public:
366 explicit InMemoryFileAdaptor(InMemoryFile &Node) : Node(Node) {}
367
368 llvm::ErrorOr<Status> status() override { return Node.getStatus(); }
369 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
370 getBuffer(const Twine &Name, int64_t FileSize = -1,
371 bool RequiresNullTerminator = true,
372 bool IsVolatile = false) override {
373 llvm::MemoryBuffer *Buf = Node.getBuffer();
374 return llvm::MemoryBuffer::getMemBuffer(
375 Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
376 }
377 std::error_code close() override { return std::error_code(); }
378};
379} // end anonymous namespace
380
381class InMemoryDirectory : public InMemoryNode {
382 std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
383
384public:
385 InMemoryDirectory(Status Stat)
386 : InMemoryNode(std::move(Stat), IME_Directory) {}
387 InMemoryNode *getChild(StringRef Name) {
388 auto I = Entries.find(Name);
389 if (I != Entries.end())
390 return I->second.get();
391 return nullptr;
392 }
393 InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
394 return Entries.insert(make_pair(Name, std::move(Child)))
395 .first->second.get();
396 }
397
398 typedef decltype(Entries)::const_iterator const_iterator;
399 const_iterator begin() const { return Entries.begin(); }
400 const_iterator end() const { return Entries.end(); }
401
402 std::string toString(unsigned Indent) const override {
403 std::string Result =
404 (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
405 for (const auto &Entry : Entries) {
406 Result += Entry.second->toString(Indent + 2);
407 }
408 return Result;
409 }
410 static bool classof(const InMemoryNode *N) {
411 return N->getKind() == IME_Directory;
412 }
413};
414}
415
416InMemoryFileSystem::InMemoryFileSystem()
417 : Root(new detail::InMemoryDirectory(
418 Status("", getNextVirtualUniqueID(), llvm::sys::TimeValue::MinTime(),
419 0, 0, 0, llvm::sys::fs::file_type::directory_file,
420 llvm::sys::fs::perms::all_all))) {}
421
422InMemoryFileSystem::~InMemoryFileSystem() {}
423
424StringRef InMemoryFileSystem::toString() const {
425 return Root->toString(/*Indent=*/0);
426}
427
428void InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
429 std::unique_ptr<llvm::MemoryBuffer> Buffer) {
430 SmallString<128> Path;
431 P.toVector(Path);
432
433 // Fix up relative paths. This just prepends the current working directory.
434 std::error_code EC = sys::fs::make_absolute(Path);
435 assert(!EC);
436 (void)EC;
437
438 detail::InMemoryDirectory *Dir = Root.get();
439 auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
440 while (true) {
441 StringRef Name = *I;
442 detail::InMemoryNode *Node = Dir->getChild(Name);
443 ++I;
444 if (!Node) {
445 if (I == E) {
446 // End of the path, create a new file.
447 // FIXME: expose the status details in the interface.
448 Status Stat(Path, getNextVirtualUniqueID(),
449 llvm::sys::TimeValue(ModificationTime), 0, 0,
450 Buffer->getBufferSize(),
451 llvm::sys::fs::file_type::regular_file,
452 llvm::sys::fs::all_all);
453 Dir->addChild(Name, llvm::make_unique<detail::InMemoryFile>(
454 std::move(Stat), std::move(Buffer)));
455 return;
456 }
457
458 // Create a new directory. Use the path up to here.
459 // FIXME: expose the status details in the interface.
460 Status Stat(
461 StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
462 getNextVirtualUniqueID(), llvm::sys::TimeValue(ModificationTime), 0,
463 0, Buffer->getBufferSize(), llvm::sys::fs::file_type::directory_file,
464 llvm::sys::fs::all_all);
465 Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
466 Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
467 continue;
468 }
469
470 if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node))
471 Dir = NewDir;
472 }
473}
474
475static ErrorOr<detail::InMemoryNode *>
476lookupInMemoryNode(detail::InMemoryDirectory *Dir, const Twine &P) {
477 SmallString<128> Path;
478 P.toVector(Path);
479
480 // Fix up relative paths. This just prepends the current working directory.
481 std::error_code EC = sys::fs::make_absolute(Path);
482 assert(!EC);
483 (void)EC;
484
485 auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
486 while (true) {
487 detail::InMemoryNode *Node = Dir->getChild(*I);
488 ++I;
489 if (!Node)
490 return errc::no_such_file_or_directory;
491
492 // Return the file if it's at the end of the path.
493 if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
494 if (I == E)
495 return File;
496 return errc::no_such_file_or_directory;
497 }
498
499 // Traverse directories.
500 Dir = cast<detail::InMemoryDirectory>(Node);
501 if (I == E)
502 return Dir;
503 }
504}
505
506llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
507 auto Node = lookupInMemoryNode(Root.get(), Path);
508 if (Node)
509 return (*Node)->getStatus();
510 return Node.getError();
511}
512
513llvm::ErrorOr<std::unique_ptr<File>>
514InMemoryFileSystem::openFileForRead(const Twine &Path) {
515 auto Node = lookupInMemoryNode(Root.get(), Path);
516 if (!Node)
517 return Node.getError();
518
519 // When we have a file provide a heap-allocated wrapper for the memory buffer
520 // to match the ownership semantics for File.
521 if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
522 return std::unique_ptr<File>(new detail::InMemoryFileAdaptor(*F));
523
524 // FIXME: errc::not_a_file?
525 return make_error_code(llvm::errc::invalid_argument);
526}
527
528namespace {
529/// Adaptor from InMemoryDir::iterator to directory_iterator.
530class InMemoryDirIterator : public clang::vfs::detail::DirIterImpl {
531 detail::InMemoryDirectory::const_iterator I;
532 detail::InMemoryDirectory::const_iterator E;
533
534public:
535 InMemoryDirIterator() {}
536 explicit InMemoryDirIterator(detail::InMemoryDirectory &Dir)
537 : I(Dir.begin()), E(Dir.end()) {
538 if (I != E)
539 CurrentEntry = I->second->getStatus();
540 }
541
542 std::error_code increment() override {
543 ++I;
544 // When we're at the end, make CurrentEntry invalid and DirIterImpl will do
545 // the rest.
546 CurrentEntry = I != E ? I->second->getStatus() : Status();
547 return std::error_code();
548 }
549};
550} // end anonymous namespace
551
552directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
553 std::error_code &EC) {
554 auto Node = lookupInMemoryNode(Root.get(), Dir);
555 if (!Node) {
556 EC = Node.getError();
557 return directory_iterator(std::make_shared<InMemoryDirIterator>());
558 }
559
560 if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
561 return directory_iterator(std::make_shared<InMemoryDirIterator>(*DirNode));
562
563 EC = make_error_code(llvm::errc::not_a_directory);
564 return directory_iterator(std::make_shared<InMemoryDirIterator>());
565}
566}
567}
568
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000569//===-----------------------------------------------------------------------===/
570// VFSFromYAML implementation
571//===-----------------------------------------------------------------------===/
572
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000573namespace {
574
575enum EntryKind {
576 EK_Directory,
577 EK_File
578};
579
580/// \brief A single file or directory in the VFS.
581class Entry {
582 EntryKind Kind;
583 std::string Name;
584
585public:
586 virtual ~Entry();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000587 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
588 StringRef getName() const { return Name; }
589 EntryKind getKind() const { return Kind; }
590};
591
592class DirectoryEntry : public Entry {
593 std::vector<Entry *> Contents;
594 Status S;
595
596public:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000597 ~DirectoryEntry() override;
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000598 DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
599 : Entry(EK_Directory, Name), Contents(std::move(Contents)),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000600 S(std::move(S)) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000601 Status getStatus() { return S; }
602 typedef std::vector<Entry *>::iterator iterator;
603 iterator contents_begin() { return Contents.begin(); }
604 iterator contents_end() { return Contents.end(); }
605 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
606};
607
608class FileEntry : public Entry {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000609public:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000610 enum NameKind {
611 NK_NotSet,
612 NK_External,
613 NK_Virtual
614 };
615private:
616 std::string ExternalContentsPath;
617 NameKind UseName;
618public:
619 FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
620 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
621 UseName(UseName) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000622 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
Ben Langmuirb59cf672014-02-27 00:25:12 +0000623 /// \brief whether to use the external path as the name for this file.
Ben Langmuird066d4c2014-02-28 21:16:07 +0000624 bool useExternalName(bool GlobalUseExternalName) const {
625 return UseName == NK_NotSet ? GlobalUseExternalName
626 : (UseName == NK_External);
627 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000628 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
629};
630
Ben Langmuir740812b2014-06-24 19:37:16 +0000631class VFSFromYAML;
632
633class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
634 std::string Dir;
635 VFSFromYAML &FS;
636 DirectoryEntry::iterator Current, End;
637public:
638 VFSFromYamlDirIterImpl(const Twine &Path, VFSFromYAML &FS,
639 DirectoryEntry::iterator Begin,
640 DirectoryEntry::iterator End, std::error_code &EC);
641 std::error_code increment() override;
642};
643
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000644/// \brief A virtual file system parsed from a YAML file.
645///
646/// Currently, this class allows creating virtual directories and mapping
647/// virtual file paths to existing external files, available in \c ExternalFS.
648///
649/// The basic structure of the parsed file is:
650/// \verbatim
651/// {
652/// 'version': <version number>,
653/// <optional configuration>
654/// 'roots': [
655/// <directory entries>
656/// ]
657/// }
658/// \endverbatim
659///
660/// All configuration options are optional.
661/// 'case-sensitive': <boolean, default=true>
Ben Langmuirb59cf672014-02-27 00:25:12 +0000662/// 'use-external-names': <boolean, default=true>
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000663///
664/// Virtual directories are represented as
665/// \verbatim
666/// {
667/// 'type': 'directory',
668/// 'name': <string>,
669/// 'contents': [ <file or directory entries> ]
670/// }
671/// \endverbatim
672///
673/// The default attributes for virtual directories are:
674/// \verbatim
675/// MTime = now() when created
676/// Perms = 0777
677/// User = Group = 0
678/// Size = 0
679/// UniqueID = unspecified unique value
680/// \endverbatim
681///
682/// Re-mapped files are represented as
683/// \verbatim
684/// {
685/// 'type': 'file',
686/// 'name': <string>,
Ben Langmuirb59cf672014-02-27 00:25:12 +0000687/// 'use-external-name': <boolean> # Optional
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000688/// 'external-contents': <path to external file>)
689/// }
690/// \endverbatim
691///
692/// and inherit their attributes from the external contents.
693///
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000694/// In both cases, the 'name' field may contain multiple path components (e.g.
695/// /path/to/file). However, any directory that contains more than one child
696/// must be uniquely represented by a directory entry.
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000697class VFSFromYAML : public vfs::FileSystem {
698 std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
699 /// \brief The file system to use for external references.
700 IntrusiveRefCntPtr<FileSystem> ExternalFS;
701
702 /// @name Configuration
703 /// @{
704
705 /// \brief Whether to perform case-sensitive comparisons.
706 ///
707 /// Currently, case-insensitive matching only works correctly with ASCII.
Ben Langmuirb59cf672014-02-27 00:25:12 +0000708 bool CaseSensitive;
709
710 /// \brief Whether to use to use the value of 'external-contents' for the
711 /// names of files. This global value is overridable on a per-file basis.
712 bool UseExternalNames;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000713 /// @}
714
715 friend class VFSFromYAMLParser;
716
717private:
718 VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
Ben Langmuirb59cf672014-02-27 00:25:12 +0000719 : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000720
721 /// \brief Looks up \p Path in \c Roots.
722 ErrorOr<Entry *> lookupPath(const Twine &Path);
723
724 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
725 /// recursing into the contents of \p From if it is a directory.
726 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
727 sys::path::const_iterator End, Entry *From);
728
Ben Langmuir740812b2014-06-24 19:37:16 +0000729 /// \brief Get the status of a given an \c Entry.
730 ErrorOr<Status> status(const Twine &Path, Entry *E);
731
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000732public:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000733 ~VFSFromYAML() override;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000734
735 /// \brief Parses \p Buffer, which is expected to be in YAML format and
736 /// returns a virtual file system representing its contents.
Rafael Espindola04ab21d72014-08-17 22:12:58 +0000737 static VFSFromYAML *create(std::unique_ptr<MemoryBuffer> Buffer,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000738 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000739 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000740 IntrusiveRefCntPtr<FileSystem> ExternalFS);
741
Craig Toppera798a9d2014-03-02 09:32:10 +0000742 ErrorOr<Status> status(const Twine &Path) override;
Benjamin Kramera8857962014-10-26 22:44:13 +0000743 ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
Ben Langmuir740812b2014-06-24 19:37:16 +0000744
745 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
746 ErrorOr<Entry *> E = lookupPath(Dir);
747 if (!E) {
748 EC = E.getError();
749 return directory_iterator();
750 }
751 ErrorOr<Status> S = status(Dir, *E);
752 if (!S) {
753 EC = S.getError();
754 return directory_iterator();
755 }
756 if (!S->isDirectory()) {
757 EC = std::error_code(static_cast<int>(errc::not_a_directory),
758 std::system_category());
759 return directory_iterator();
760 }
761
762 DirectoryEntry *D = cast<DirectoryEntry>(*E);
763 return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
764 *this, D->contents_begin(), D->contents_end(), EC));
765 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000766};
767
768/// \brief A helper class to hold the common YAML parsing state.
769class VFSFromYAMLParser {
770 yaml::Stream &Stream;
771
772 void error(yaml::Node *N, const Twine &Msg) {
773 Stream.printError(N, Msg);
774 }
775
776 // false on error
777 bool parseScalarString(yaml::Node *N, StringRef &Result,
778 SmallVectorImpl<char> &Storage) {
779 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
780 if (!S) {
781 error(N, "expected string");
782 return false;
783 }
784 Result = S->getValue(Storage);
785 return true;
786 }
787
788 // false on error
789 bool parseScalarBool(yaml::Node *N, bool &Result) {
790 SmallString<5> Storage;
791 StringRef Value;
792 if (!parseScalarString(N, Value, Storage))
793 return false;
794
795 if (Value.equals_lower("true") || Value.equals_lower("on") ||
796 Value.equals_lower("yes") || Value == "1") {
797 Result = true;
798 return true;
799 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
800 Value.equals_lower("no") || Value == "0") {
801 Result = false;
802 return true;
803 }
804
805 error(N, "expected boolean value");
806 return false;
807 }
808
809 struct KeyStatus {
810 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
811 bool Required;
812 bool Seen;
813 };
814 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
815
816 // false on error
817 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
818 DenseMap<StringRef, KeyStatus> &Keys) {
819 if (!Keys.count(Key)) {
820 error(KeyNode, "unknown key");
821 return false;
822 }
823 KeyStatus &S = Keys[Key];
824 if (S.Seen) {
825 error(KeyNode, Twine("duplicate key '") + Key + "'");
826 return false;
827 }
828 S.Seen = true;
829 return true;
830 }
831
832 // false on error
833 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
834 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
835 E = Keys.end();
836 I != E; ++I) {
837 if (I->second.Required && !I->second.Seen) {
838 error(Obj, Twine("missing key '") + I->first + "'");
839 return false;
840 }
841 }
842 return true;
843 }
844
845 Entry *parseEntry(yaml::Node *N) {
846 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
847 if (!M) {
848 error(N, "expected mapping node for file or directory entry");
Craig Topperf1186c52014-05-08 06:41:40 +0000849 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000850 }
851
852 KeyStatusPair Fields[] = {
853 KeyStatusPair("name", true),
854 KeyStatusPair("type", true),
855 KeyStatusPair("contents", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000856 KeyStatusPair("external-contents", false),
857 KeyStatusPair("use-external-name", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000858 };
859
860 DenseMap<StringRef, KeyStatus> Keys(
861 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
862
863 bool HasContents = false; // external or otherwise
864 std::vector<Entry *> EntryArrayContents;
865 std::string ExternalContentsPath;
866 std::string Name;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000867 FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000868 EntryKind Kind;
869
870 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
871 ++I) {
872 StringRef Key;
873 // Reuse the buffer for key and value, since we don't look at key after
874 // parsing value.
875 SmallString<256> Buffer;
876 if (!parseScalarString(I->getKey(), Key, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000877 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000878
879 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000880 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000881
882 StringRef Value;
883 if (Key == "name") {
884 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000885 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000886 Name = Value;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000887 } else if (Key == "type") {
888 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000889 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000890 if (Value == "file")
891 Kind = EK_File;
892 else if (Value == "directory")
893 Kind = EK_Directory;
894 else {
895 error(I->getValue(), "unknown value for 'type'");
Craig Topperf1186c52014-05-08 06:41:40 +0000896 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000897 }
898 } else if (Key == "contents") {
899 if (HasContents) {
900 error(I->getKey(),
901 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000902 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000903 }
904 HasContents = true;
905 yaml::SequenceNode *Contents =
906 dyn_cast<yaml::SequenceNode>(I->getValue());
907 if (!Contents) {
908 // FIXME: this is only for directories, what about files?
909 error(I->getValue(), "expected array");
Craig Topperf1186c52014-05-08 06:41:40 +0000910 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000911 }
912
913 for (yaml::SequenceNode::iterator I = Contents->begin(),
914 E = Contents->end();
915 I != E; ++I) {
916 if (Entry *E = parseEntry(&*I))
917 EntryArrayContents.push_back(E);
918 else
Craig Topperf1186c52014-05-08 06:41:40 +0000919 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000920 }
921 } else if (Key == "external-contents") {
922 if (HasContents) {
923 error(I->getKey(),
924 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000925 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000926 }
927 HasContents = true;
928 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000929 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000930 ExternalContentsPath = Value;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000931 } else if (Key == "use-external-name") {
932 bool Val;
933 if (!parseScalarBool(I->getValue(), Val))
Craig Topperf1186c52014-05-08 06:41:40 +0000934 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000935 UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000936 } else {
937 llvm_unreachable("key missing from Keys");
938 }
939 }
940
941 if (Stream.failed())
Craig Topperf1186c52014-05-08 06:41:40 +0000942 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000943
944 // check for missing keys
945 if (!HasContents) {
946 error(N, "missing key 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000947 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000948 }
949 if (!checkMissingKeys(N, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000950 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000951
Ben Langmuirb59cf672014-02-27 00:25:12 +0000952 // check invalid configuration
953 if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
954 error(N, "'use-external-name' is not supported for directories");
Craig Topperf1186c52014-05-08 06:41:40 +0000955 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000956 }
957
Ben Langmuir93853232014-03-05 21:32:20 +0000958 // Remove trailing slash(es), being careful not to remove the root path
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000959 StringRef Trimmed(Name);
Ben Langmuir93853232014-03-05 21:32:20 +0000960 size_t RootPathLen = sys::path::root_path(Trimmed).size();
961 while (Trimmed.size() > RootPathLen &&
962 sys::path::is_separator(Trimmed.back()))
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000963 Trimmed = Trimmed.slice(0, Trimmed.size()-1);
964 // Get the last component
965 StringRef LastComponent = sys::path::filename(Trimmed);
966
Craig Topperf1186c52014-05-08 06:41:40 +0000967 Entry *Result = nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000968 switch (Kind) {
969 case EK_File:
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000970 Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000971 UseExternalName);
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000972 break;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000973 case EK_Directory:
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000974 Result = new DirectoryEntry(
975 LastComponent, std::move(EntryArrayContents),
976 Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
977 file_type::directory_file, sys::fs::all_all));
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000978 break;
979 }
980
981 StringRef Parent = sys::path::parent_path(Trimmed);
982 if (Parent.empty())
983 return Result;
984
985 // if 'name' contains multiple components, create implicit directory entries
986 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
987 E = sys::path::rend(Parent);
988 I != E; ++I) {
Benjamin Kramer268b51a2015-10-05 13:15:33 +0000989 Result = new DirectoryEntry(
990 *I, llvm::makeArrayRef(Result),
991 Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
992 file_type::directory_file, sys::fs::all_all));
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000993 }
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000994 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000995 }
996
997public:
998 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
999
1000 // false on error
1001 bool parse(yaml::Node *Root, VFSFromYAML *FS) {
1002 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
1003 if (!Top) {
1004 error(Root, "expected mapping node");
1005 return false;
1006 }
1007
1008 KeyStatusPair Fields[] = {
1009 KeyStatusPair("version", true),
1010 KeyStatusPair("case-sensitive", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +00001011 KeyStatusPair("use-external-names", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001012 KeyStatusPair("roots", true),
1013 };
1014
1015 DenseMap<StringRef, KeyStatus> Keys(
1016 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
1017
1018 // Parse configuration and 'roots'
1019 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
1020 ++I) {
1021 SmallString<10> KeyBuffer;
1022 StringRef Key;
1023 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
1024 return false;
1025
1026 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
1027 return false;
1028
1029 if (Key == "roots") {
1030 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
1031 if (!Roots) {
1032 error(I->getValue(), "expected array");
1033 return false;
1034 }
1035
1036 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
1037 I != E; ++I) {
1038 if (Entry *E = parseEntry(&*I))
1039 FS->Roots.push_back(E);
1040 else
1041 return false;
1042 }
1043 } else if (Key == "version") {
1044 StringRef VersionString;
1045 SmallString<4> Storage;
1046 if (!parseScalarString(I->getValue(), VersionString, Storage))
1047 return false;
1048 int Version;
1049 if (VersionString.getAsInteger<int>(10, Version)) {
1050 error(I->getValue(), "expected integer");
1051 return false;
1052 }
1053 if (Version < 0) {
1054 error(I->getValue(), "invalid version number");
1055 return false;
1056 }
1057 if (Version != 0) {
1058 error(I->getValue(), "version mismatch, expected 0");
1059 return false;
1060 }
1061 } else if (Key == "case-sensitive") {
1062 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
1063 return false;
Ben Langmuirb59cf672014-02-27 00:25:12 +00001064 } else if (Key == "use-external-names") {
1065 if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
1066 return false;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001067 } else {
1068 llvm_unreachable("key missing from Keys");
1069 }
1070 }
1071
1072 if (Stream.failed())
1073 return false;
1074
1075 if (!checkMissingKeys(Top, Keys))
1076 return false;
1077 return true;
1078 }
1079};
1080} // end of anonymous namespace
1081
1082Entry::~Entry() {}
1083DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
1084
1085VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
1086
Rafael Espindola04ab21d72014-08-17 22:12:58 +00001087VFSFromYAML *VFSFromYAML::create(std::unique_ptr<MemoryBuffer> Buffer,
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001088 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +00001089 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001090 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1091
1092 SourceMgr SM;
Rafael Espindola85d78922014-08-27 19:03:27 +00001093 yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001094
Ben Langmuir97882e72014-02-24 20:56:37 +00001095 SM.setDiagHandler(DiagHandler, DiagContext);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001096 yaml::document_iterator DI = Stream.begin();
1097 yaml::Node *Root = DI->getRoot();
1098 if (DI == Stream.end() || !Root) {
1099 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
Craig Topperf1186c52014-05-08 06:41:40 +00001100 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001101 }
1102
1103 VFSFromYAMLParser P(Stream);
1104
Ahmed Charlesb8984322014-03-07 20:03:18 +00001105 std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001106 if (!P.parse(Root, FS.get()))
Craig Topperf1186c52014-05-08 06:41:40 +00001107 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001108
Ahmed Charles9a16beb2014-03-07 19:33:25 +00001109 return FS.release();
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001110}
1111
1112ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +00001113 SmallString<256> Path;
1114 Path_.toVector(Path);
1115
1116 // Handle relative paths
Rafael Espindola8e650d72014-06-12 20:37:59 +00001117 if (std::error_code EC = sys::fs::make_absolute(Path))
Ben Langmuira6f8ca82014-03-04 22:34:50 +00001118 return EC;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001119
1120 if (Path.empty())
Rafael Espindola71de0b62014-06-13 17:20:50 +00001121 return make_error_code(llvm::errc::invalid_argument);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001122
1123 sys::path::const_iterator Start = sys::path::begin(Path);
1124 sys::path::const_iterator End = sys::path::end(Path);
1125 for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
1126 I != E; ++I) {
1127 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
Rafael Espindola71de0b62014-06-13 17:20:50 +00001128 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001129 return Result;
1130 }
Rafael Espindola71de0b62014-06-13 17:20:50 +00001131 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001132}
1133
1134ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
1135 sys::path::const_iterator End,
1136 Entry *From) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +00001137 if (Start->equals("."))
1138 ++Start;
1139
1140 // FIXME: handle ..
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001141 if (CaseSensitive ? !Start->equals(From->getName())
1142 : !Start->equals_lower(From->getName()))
1143 // failure to match
Rafael Espindola71de0b62014-06-13 17:20:50 +00001144 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001145
1146 ++Start;
1147
1148 if (Start == End) {
1149 // Match!
1150 return From;
1151 }
1152
1153 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
1154 if (!DE)
Rafael Espindola71de0b62014-06-13 17:20:50 +00001155 return make_error_code(llvm::errc::not_a_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001156
1157 for (DirectoryEntry::iterator I = DE->contents_begin(),
1158 E = DE->contents_end();
1159 I != E; ++I) {
1160 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
Rafael Espindola71de0b62014-06-13 17:20:50 +00001161 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001162 return Result;
1163 }
Rafael Espindola71de0b62014-06-13 17:20:50 +00001164 return make_error_code(llvm::errc::no_such_file_or_directory);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001165}
1166
Ben Langmuir740812b2014-06-24 19:37:16 +00001167ErrorOr<Status> VFSFromYAML::status(const Twine &Path, Entry *E) {
1168 assert(E != nullptr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001169 std::string PathStr(Path.str());
Ben Langmuir740812b2014-06-24 19:37:16 +00001170 if (FileEntry *F = dyn_cast<FileEntry>(E)) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001171 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
Ben Langmuirb59cf672014-02-27 00:25:12 +00001172 assert(!S || S->getName() == F->getExternalContentsPath());
Ben Langmuird066d4c2014-02-28 21:16:07 +00001173 if (S && !F->useExternalName(UseExternalNames))
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001174 *S = Status::copyWithNewName(*S, PathStr);
Ben Langmuir5de00f32014-05-23 18:15:47 +00001175 if (S)
1176 S->IsVFSMapped = true;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001177 return S;
1178 } else { // directory
Ben Langmuir740812b2014-06-24 19:37:16 +00001179 DirectoryEntry *DE = cast<DirectoryEntry>(E);
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001180 return Status::copyWithNewName(DE->getStatus(), PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001181 }
1182}
1183
Ben Langmuir740812b2014-06-24 19:37:16 +00001184ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
1185 ErrorOr<Entry *> Result = lookupPath(Path);
1186 if (!Result)
1187 return Result.getError();
1188 return status(Path, *Result);
1189}
1190
Benjamin Kramer5532ef12015-10-05 13:55:09 +00001191namespace {
1192/// Provide a file wrapper that returns the external name when asked.
1193class NamedFileAdaptor : public File {
1194 std::unique_ptr<File> InnerFile;
1195 std::string NewName;
1196
1197public:
1198 NamedFileAdaptor(std::unique_ptr<File> InnerFile, std::string NewName)
1199 : InnerFile(std::move(InnerFile)), NewName(std::move(NewName)) {}
1200
1201 llvm::ErrorOr<Status> status() override {
1202 auto InnerStatus = InnerFile->status();
1203 if (InnerStatus)
1204 return Status::copyWithNewName(*InnerStatus, NewName);
1205 return InnerStatus.getError();
1206 }
1207 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
1208 getBuffer(const Twine &Name, int64_t FileSize = -1,
1209 bool RequiresNullTerminator = true,
1210 bool IsVolatile = false) override {
1211 return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
1212 IsVolatile);
1213 }
1214 std::error_code close() override { return InnerFile->close(); }
1215};
1216} // end anonymous namespace
1217
Benjamin Kramera8857962014-10-26 22:44:13 +00001218ErrorOr<std::unique_ptr<File>> VFSFromYAML::openFileForRead(const Twine &Path) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001219 ErrorOr<Entry *> E = lookupPath(Path);
1220 if (!E)
1221 return E.getError();
1222
1223 FileEntry *F = dyn_cast<FileEntry>(*E);
1224 if (!F) // FIXME: errc::not_a_file?
Rafael Espindola71de0b62014-06-13 17:20:50 +00001225 return make_error_code(llvm::errc::invalid_argument);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001226
Benjamin Kramera8857962014-10-26 22:44:13 +00001227 auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
1228 if (!Result)
1229 return Result;
Ben Langmuird066d4c2014-02-28 21:16:07 +00001230
Benjamin Kramer5532ef12015-10-05 13:55:09 +00001231 if (!F->useExternalName(UseExternalNames))
Benjamin Kramer268b51a2015-10-05 13:15:33 +00001232 return std::unique_ptr<File>(
1233 new NamedFileAdaptor(std::move(*Result), Path.str()));
Ben Langmuird066d4c2014-02-28 21:16:07 +00001234
Benjamin Kramera8857962014-10-26 22:44:13 +00001235 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001236}
1237
1238IntrusiveRefCntPtr<FileSystem>
Rafael Espindola04ab21d72014-08-17 22:12:58 +00001239vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1240 SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001241 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Rafael Espindola04ab21d72014-08-17 22:12:58 +00001242 return VFSFromYAML::create(std::move(Buffer), DiagHandler, DiagContext,
1243 ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001244}
1245
1246UniqueID vfs::getNextVirtualUniqueID() {
Benjamin Kramer4527fb22014-03-02 17:08:31 +00001247 static std::atomic<unsigned> UID;
1248 unsigned ID = ++UID;
Ben Langmuird51ba0b2014-02-21 23:39:37 +00001249 // The following assumes that uint64_t max will never collide with a real
1250 // dev_t value from the OS.
1251 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
1252}
Justin Bogner9c785292014-05-20 21:43:27 +00001253
1254#ifndef NDEBUG
1255static bool pathHasTraversal(StringRef Path) {
1256 using namespace llvm::sys;
1257 for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1258 if (Comp == "." || Comp == "..")
1259 return true;
1260 return false;
1261}
1262#endif
1263
1264void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1265 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1266 assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1267 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1268 Mappings.emplace_back(VirtualPath, RealPath);
1269}
1270
Justin Bogner44fa450342014-05-21 22:46:51 +00001271namespace {
1272class JSONWriter {
1273 llvm::raw_ostream &OS;
1274 SmallVector<StringRef, 16> DirStack;
1275 inline unsigned getDirIndent() { return 4 * DirStack.size(); }
1276 inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1277 bool containedIn(StringRef Parent, StringRef Path);
1278 StringRef containedPart(StringRef Parent, StringRef Path);
1279 void startDirectory(StringRef Path);
1280 void endDirectory();
1281 void writeEntry(StringRef VPath, StringRef RPath);
1282
1283public:
1284 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1285 void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> IsCaseSensitive);
1286};
Alexander Kornienkoab9db512015-06-22 23:07:51 +00001287}
Justin Bogner9c785292014-05-20 21:43:27 +00001288
Justin Bogner44fa450342014-05-21 22:46:51 +00001289bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
Justin Bogner1c078f22014-05-20 22:12:58 +00001290 using namespace llvm::sys;
1291 // Compare each path component.
1292 auto IParent = path::begin(Parent), EParent = path::end(Parent);
1293 for (auto IChild = path::begin(Path), EChild = path::end(Path);
1294 IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
1295 if (*IParent != *IChild)
1296 return false;
1297 }
1298 // Have we exhausted the parent path?
1299 return IParent == EParent;
Justin Bogner9c785292014-05-20 21:43:27 +00001300}
1301
Justin Bogner44fa450342014-05-21 22:46:51 +00001302StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1303 assert(!Parent.empty());
Justin Bogner9c785292014-05-20 21:43:27 +00001304 assert(containedIn(Parent, Path));
Justin Bogner9c785292014-05-20 21:43:27 +00001305 return Path.slice(Parent.size() + 1, StringRef::npos);
1306}
1307
Justin Bogner44fa450342014-05-21 22:46:51 +00001308void JSONWriter::startDirectory(StringRef Path) {
1309 StringRef Name =
1310 DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
1311 DirStack.push_back(Path);
1312 unsigned Indent = getDirIndent();
1313 OS.indent(Indent) << "{\n";
1314 OS.indent(Indent + 2) << "'type': 'directory',\n";
1315 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1316 OS.indent(Indent + 2) << "'contents': [\n";
1317}
1318
1319void JSONWriter::endDirectory() {
1320 unsigned Indent = getDirIndent();
1321 OS.indent(Indent + 2) << "]\n";
1322 OS.indent(Indent) << "}";
1323
1324 DirStack.pop_back();
1325}
1326
1327void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1328 unsigned Indent = getFileIndent();
1329 OS.indent(Indent) << "{\n";
1330 OS.indent(Indent + 2) << "'type': 'file',\n";
1331 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1332 OS.indent(Indent + 2) << "'external-contents': \""
1333 << llvm::yaml::escape(RPath) << "\"\n";
1334 OS.indent(Indent) << "}";
1335}
1336
1337void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1338 Optional<bool> IsCaseSensitive) {
1339 using namespace llvm::sys;
1340
1341 OS << "{\n"
1342 " 'version': 0,\n";
1343 if (IsCaseSensitive.hasValue())
1344 OS << " 'case-sensitive': '"
1345 << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
1346 OS << " 'roots': [\n";
1347
Justin Bogner73466402014-07-15 01:24:35 +00001348 if (!Entries.empty()) {
1349 const YAMLVFSEntry &Entry = Entries.front();
1350 startDirectory(path::parent_path(Entry.VPath));
Justin Bogner44fa450342014-05-21 22:46:51 +00001351 writeEntry(path::filename(Entry.VPath), Entry.RPath);
Justin Bogner44fa450342014-05-21 22:46:51 +00001352
Justin Bogner73466402014-07-15 01:24:35 +00001353 for (const auto &Entry : Entries.slice(1)) {
1354 StringRef Dir = path::parent_path(Entry.VPath);
1355 if (Dir == DirStack.back())
1356 OS << ",\n";
1357 else {
1358 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
1359 OS << "\n";
1360 endDirectory();
1361 }
1362 OS << ",\n";
1363 startDirectory(Dir);
1364 }
1365 writeEntry(path::filename(Entry.VPath), Entry.RPath);
1366 }
1367
1368 while (!DirStack.empty()) {
1369 OS << "\n";
1370 endDirectory();
1371 }
Justin Bogner44fa450342014-05-21 22:46:51 +00001372 OS << "\n";
Justin Bogner44fa450342014-05-21 22:46:51 +00001373 }
1374
Justin Bogner73466402014-07-15 01:24:35 +00001375 OS << " ]\n"
Justin Bogner44fa450342014-05-21 22:46:51 +00001376 << "}\n";
1377}
1378
Justin Bogner9c785292014-05-20 21:43:27 +00001379void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
1380 std::sort(Mappings.begin(), Mappings.end(),
Justin Bogner44fa450342014-05-21 22:46:51 +00001381 [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
Justin Bogner9c785292014-05-20 21:43:27 +00001382 return LHS.VPath < RHS.VPath;
1383 });
1384
Justin Bogner44fa450342014-05-21 22:46:51 +00001385 JSONWriter(OS).write(Mappings, IsCaseSensitive);
Justin Bogner9c785292014-05-20 21:43:27 +00001386}
Ben Langmuir740812b2014-06-24 19:37:16 +00001387
1388VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(const Twine &_Path,
1389 VFSFromYAML &FS,
1390 DirectoryEntry::iterator Begin,
1391 DirectoryEntry::iterator End,
1392 std::error_code &EC)
1393 : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
1394 if (Current != End) {
1395 SmallString<128> PathStr(Dir);
1396 llvm::sys::path::append(PathStr, (*Current)->getName());
Yaron Keren92e1b622015-03-18 10:17:07 +00001397 llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
Ben Langmuir740812b2014-06-24 19:37:16 +00001398 if (S)
1399 CurrentEntry = *S;
1400 else
1401 EC = S.getError();
1402 }
1403}
1404
1405std::error_code VFSFromYamlDirIterImpl::increment() {
1406 assert(Current != End && "cannot iterate past end");
1407 if (++Current != End) {
1408 SmallString<128> PathStr(Dir);
1409 llvm::sys::path::append(PathStr, (*Current)->getName());
Yaron Keren92e1b622015-03-18 10:17:07 +00001410 llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
Ben Langmuir740812b2014-06-24 19:37:16 +00001411 if (!S)
1412 return S.getError();
1413 CurrentEntry = *S;
1414 } else {
1415 CurrentEntry = Status();
1416 }
1417 return std::error_code();
1418}
Ben Langmuir7c9f6c82014-06-25 20:25:40 +00001419
1420vfs::recursive_directory_iterator::recursive_directory_iterator(FileSystem &FS_,
1421 const Twine &Path,
1422 std::error_code &EC)
1423 : FS(&FS_) {
1424 directory_iterator I = FS->dir_begin(Path, EC);
1425 if (!EC && I != directory_iterator()) {
1426 State = std::make_shared<IterState>();
1427 State->push(I);
1428 }
1429}
1430
1431vfs::recursive_directory_iterator &
1432recursive_directory_iterator::increment(std::error_code &EC) {
1433 assert(FS && State && !State->empty() && "incrementing past end");
1434 assert(State->top()->isStatusKnown() && "non-canonical end iterator");
1435 vfs::directory_iterator End;
1436 if (State->top()->isDirectory()) {
1437 vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
1438 if (EC)
1439 return *this;
1440 if (I != End) {
1441 State->push(I);
1442 return *this;
1443 }
1444 }
1445
1446 while (!State->empty() && State->top().increment(EC) == End)
1447 State->pop();
1448
1449 if (State->empty())
1450 State.reset(); // end iterator
1451
1452 return *this;
Rafael Espindola2d2b4202014-07-06 17:43:24 +00001453}