]> granicus.if.org Git - clang/blob - lib/Basic/VirtualFileSystem.cpp
e1c4d71a21c03bf4d12ba69378ac7a6b7680f2e4
[clang] / lib / Basic / VirtualFileSystem.cpp
1 //===- 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"
13 #include "clang/Basic/FileManager.h"
14 #include "llvm/ADT/DenseMap.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/ADT/StringSet.h"
18 #include "llvm/ADT/iterator_range.h"
19 #include "llvm/Support/Errc.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/Process.h"
23 #include "llvm/Support/YAMLParser.h"
24 #include "llvm/Config/llvm-config.h"
25 #include <atomic>
26 #include <memory>
27
28 // For chdir.
29 #ifdef LLVM_ON_WIN32
30 #  include <direct.h>
31 #else
32 #  include <unistd.h>
33 #endif
34
35 using namespace clang;
36 using namespace clang::vfs;
37 using namespace llvm;
38 using llvm::sys::fs::file_status;
39 using llvm::sys::fs::file_type;
40 using llvm::sys::fs::perms;
41 using llvm::sys::fs::UniqueID;
42
43 Status::Status(const file_status &Status)
44     : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
45       User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
46       Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false)  {}
47
48 Status::Status(StringRef Name, UniqueID UID, sys::TimeValue MTime,
49                uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
50                perms Perms)
51     : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
52       Type(Type), Perms(Perms), IsVFSMapped(false) {}
53
54 Status Status::copyWithNewName(const Status &In, StringRef NewName) {
55   return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
56                 In.getUser(), In.getGroup(), In.getSize(), In.getType(),
57                 In.getPermissions());
58 }
59
60 Status Status::copyWithNewName(const file_status &In, StringRef NewName) {
61   return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
62                 In.getUser(), In.getGroup(), In.getSize(), In.type(),
63                 In.permissions());
64 }
65
66 bool Status::equivalent(const Status &Other) const {
67   return getUniqueID() == Other.getUniqueID();
68 }
69 bool Status::isDirectory() const {
70   return Type == file_type::directory_file;
71 }
72 bool Status::isRegularFile() const {
73   return Type == file_type::regular_file;
74 }
75 bool Status::isOther() const {
76   return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
77 }
78 bool Status::isSymlink() const {
79   return Type == file_type::symlink_file;
80 }
81 bool Status::isStatusKnown() const {
82   return Type != file_type::status_error;
83 }
84 bool Status::exists() const {
85   return isStatusKnown() && Type != file_type::file_not_found;
86 }
87
88 File::~File() {}
89
90 FileSystem::~FileSystem() {}
91
92 ErrorOr<std::unique_ptr<MemoryBuffer>>
93 FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
94                              bool RequiresNullTerminator, bool IsVolatile) {
95   auto F = openFileForRead(Name);
96   if (!F)
97     return F.getError();
98
99   return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
100 }
101
102 std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
103   auto WorkingDir = getCurrentWorkingDirectory();
104   if (!WorkingDir)
105     return WorkingDir.getError();
106
107   return llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
108 }
109
110 bool FileSystem::exists(const Twine &Path) {
111   auto Status = status(Path);
112   return Status && Status->exists();
113 }
114
115 #ifndef NDEBUG
116 static bool isTraversalComponent(StringRef Component) {
117   return Component.equals("..") || Component.equals(".");
118 }
119
120 static bool pathHasTraversal(StringRef Path) {
121   using namespace llvm::sys;
122   for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
123     if (isTraversalComponent(Comp))
124       return true;
125   return false;
126 }
127 #endif
128
129 //===-----------------------------------------------------------------------===/
130 // RealFileSystem implementation
131 //===-----------------------------------------------------------------------===/
132
133 namespace {
134 /// \brief Wrapper around a raw file descriptor.
135 class RealFile : public File {
136   int FD;
137   Status S;
138   friend class RealFileSystem;
139   RealFile(int FD, StringRef NewName)
140       : FD(FD), S(NewName, {}, {}, {}, {}, {},
141                   llvm::sys::fs::file_type::status_error, {}) {
142     assert(FD >= 0 && "Invalid or inactive file descriptor");
143   }
144
145 public:
146   ~RealFile() override;
147   ErrorOr<Status> status() override;
148   ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
149                                                    int64_t FileSize,
150                                                    bool RequiresNullTerminator,
151                                                    bool IsVolatile) override;
152   std::error_code close() override;
153 };
154 } // end anonymous namespace
155 RealFile::~RealFile() { close(); }
156
157 ErrorOr<Status> RealFile::status() {
158   assert(FD != -1 && "cannot stat closed file");
159   if (!S.isStatusKnown()) {
160     file_status RealStatus;
161     if (std::error_code EC = sys::fs::status(FD, RealStatus))
162       return EC;
163     S = Status::copyWithNewName(RealStatus, S.getName());
164   }
165   return S;
166 }
167
168 ErrorOr<std::unique_ptr<MemoryBuffer>>
169 RealFile::getBuffer(const Twine &Name, int64_t FileSize,
170                     bool RequiresNullTerminator, bool IsVolatile) {
171   assert(FD != -1 && "cannot get buffer for closed file");
172   return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
173                                    IsVolatile);
174 }
175
176 std::error_code RealFile::close() {
177   std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD);
178   FD = -1;
179   return EC;
180 }
181
182 namespace {
183 /// \brief The file system according to your operating system.
184 class RealFileSystem : public FileSystem {
185 public:
186   ErrorOr<Status> status(const Twine &Path) override;
187   ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
188   directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
189
190   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
191   std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
192 };
193 } // end anonymous namespace
194
195 ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
196   sys::fs::file_status RealStatus;
197   if (std::error_code EC = sys::fs::status(Path, RealStatus))
198     return EC;
199   return Status::copyWithNewName(RealStatus, Path.str());
200 }
201
202 ErrorOr<std::unique_ptr<File>>
203 RealFileSystem::openFileForRead(const Twine &Name) {
204   int FD;
205   if (std::error_code EC = sys::fs::openFileForRead(Name, FD))
206     return EC;
207   return std::unique_ptr<File>(new RealFile(FD, Name.str()));
208 }
209
210 llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
211   SmallString<256> Dir;
212   if (std::error_code EC = llvm::sys::fs::current_path(Dir))
213     return EC;
214   return Dir.str().str();
215 }
216
217 std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
218   // FIXME: chdir is thread hostile; on the other hand, creating the same
219   // behavior as chdir is complex: chdir resolves the path once, thus
220   // guaranteeing that all subsequent relative path operations work
221   // on the same path the original chdir resulted in. This makes a
222   // difference for example on network filesystems, where symlinks might be
223   // switched during runtime of the tool. Fixing this depends on having a
224   // file system abstraction that allows openat() style interactions.
225   SmallString<256> Storage;
226   StringRef Dir = Path.toNullTerminatedStringRef(Storage);
227   if (int Err = ::chdir(Dir.data()))
228     return std::error_code(Err, std::generic_category());
229   return std::error_code();
230 }
231
232 IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
233   static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
234   return FS;
235 }
236
237 namespace {
238 class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
239   std::string Path;
240   llvm::sys::fs::directory_iterator Iter;
241 public:
242   RealFSDirIter(const Twine &_Path, std::error_code &EC)
243       : Path(_Path.str()), Iter(Path, EC) {
244     if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
245       llvm::sys::fs::file_status S;
246       EC = Iter->status(S);
247       if (!EC)
248         CurrentEntry = Status::copyWithNewName(S, Iter->path());
249     }
250   }
251
252   std::error_code increment() override {
253     std::error_code EC;
254     Iter.increment(EC);
255     if (EC) {
256       return EC;
257     } else if (Iter == llvm::sys::fs::directory_iterator()) {
258       CurrentEntry = Status();
259     } else {
260       llvm::sys::fs::file_status S;
261       EC = Iter->status(S);
262       CurrentEntry = Status::copyWithNewName(S, Iter->path());
263     }
264     return EC;
265   }
266 };
267 }
268
269 directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
270                                              std::error_code &EC) {
271   return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
272 }
273
274 //===-----------------------------------------------------------------------===/
275 // OverlayFileSystem implementation
276 //===-----------------------------------------------------------------------===/
277 OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
278   FSList.push_back(BaseFS);
279 }
280
281 void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
282   FSList.push_back(FS);
283   // Synchronize added file systems by duplicating the working directory from
284   // the first one in the list.
285   FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
286 }
287
288 ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
289   // FIXME: handle symlinks that cross file systems
290   for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
291     ErrorOr<Status> Status = (*I)->status(Path);
292     if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
293       return Status;
294   }
295   return make_error_code(llvm::errc::no_such_file_or_directory);
296 }
297
298 ErrorOr<std::unique_ptr<File>>
299 OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
300   // FIXME: handle symlinks that cross file systems
301   for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
302     auto Result = (*I)->openFileForRead(Path);
303     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
304       return Result;
305   }
306   return make_error_code(llvm::errc::no_such_file_or_directory);
307 }
308
309 llvm::ErrorOr<std::string>
310 OverlayFileSystem::getCurrentWorkingDirectory() const {
311   // All file systems are synchronized, just take the first working directory.
312   return FSList.front()->getCurrentWorkingDirectory();
313 }
314 std::error_code
315 OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
316   for (auto &FS : FSList)
317     if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
318       return EC;
319   return std::error_code();
320 }
321
322 clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
323
324 namespace {
325 class OverlayFSDirIterImpl : public clang::vfs::detail::DirIterImpl {
326   OverlayFileSystem &Overlays;
327   std::string Path;
328   OverlayFileSystem::iterator CurrentFS;
329   directory_iterator CurrentDirIter;
330   llvm::StringSet<> SeenNames;
331
332   std::error_code incrementFS() {
333     assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
334     ++CurrentFS;
335     for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
336       std::error_code EC;
337       CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
338       if (EC && EC != errc::no_such_file_or_directory)
339         return EC;
340       if (CurrentDirIter != directory_iterator())
341         break; // found
342     }
343     return std::error_code();
344   }
345
346   std::error_code incrementDirIter(bool IsFirstTime) {
347     assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
348            "incrementing past end");
349     std::error_code EC;
350     if (!IsFirstTime)
351       CurrentDirIter.increment(EC);
352     if (!EC && CurrentDirIter == directory_iterator())
353       EC = incrementFS();
354     return EC;
355   }
356
357   std::error_code incrementImpl(bool IsFirstTime) {
358     while (true) {
359       std::error_code EC = incrementDirIter(IsFirstTime);
360       if (EC || CurrentDirIter == directory_iterator()) {
361         CurrentEntry = Status();
362         return EC;
363       }
364       CurrentEntry = *CurrentDirIter;
365       StringRef Name = llvm::sys::path::filename(CurrentEntry.getName());
366       if (SeenNames.insert(Name).second)
367         return EC; // name not seen before
368     }
369     llvm_unreachable("returned above");
370   }
371
372 public:
373   OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
374                        std::error_code &EC)
375       : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
376     CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
377     EC = incrementImpl(true);
378   }
379
380   std::error_code increment() override { return incrementImpl(false); }
381 };
382 } // end anonymous namespace
383
384 directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
385                                                 std::error_code &EC) {
386   return directory_iterator(
387       std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
388 }
389
390 namespace clang {
391 namespace vfs {
392 namespace detail {
393
394 enum InMemoryNodeKind { IME_File, IME_Directory };
395
396 /// The in memory file system is a tree of Nodes. Every node can either be a
397 /// file or a directory.
398 class InMemoryNode {
399   Status Stat;
400   InMemoryNodeKind Kind;
401
402 public:
403   InMemoryNode(Status Stat, InMemoryNodeKind Kind)
404       : Stat(std::move(Stat)), Kind(Kind) {}
405   virtual ~InMemoryNode() {}
406   const Status &getStatus() const { return Stat; }
407   InMemoryNodeKind getKind() const { return Kind; }
408   virtual std::string toString(unsigned Indent) const = 0;
409 };
410
411 namespace {
412 class InMemoryFile : public InMemoryNode {
413   std::unique_ptr<llvm::MemoryBuffer> Buffer;
414
415 public:
416   InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
417       : InMemoryNode(std::move(Stat), IME_File), Buffer(std::move(Buffer)) {}
418
419   llvm::MemoryBuffer *getBuffer() { return Buffer.get(); }
420   std::string toString(unsigned Indent) const override {
421     return (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
422   }
423   static bool classof(const InMemoryNode *N) {
424     return N->getKind() == IME_File;
425   }
426 };
427
428 /// Adapt a InMemoryFile for VFS' File interface.
429 class InMemoryFileAdaptor : public File {
430   InMemoryFile &Node;
431
432 public:
433   explicit InMemoryFileAdaptor(InMemoryFile &Node) : Node(Node) {}
434
435   llvm::ErrorOr<Status> status() override { return Node.getStatus(); }
436   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
437   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
438             bool IsVolatile) override {
439     llvm::MemoryBuffer *Buf = Node.getBuffer();
440     return llvm::MemoryBuffer::getMemBuffer(
441         Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
442   }
443   std::error_code close() override { return std::error_code(); }
444 };
445 } // end anonymous namespace
446
447 class InMemoryDirectory : public InMemoryNode {
448   std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
449
450 public:
451   InMemoryDirectory(Status Stat)
452       : InMemoryNode(std::move(Stat), IME_Directory) {}
453   InMemoryNode *getChild(StringRef Name) {
454     auto I = Entries.find(Name);
455     if (I != Entries.end())
456       return I->second.get();
457     return nullptr;
458   }
459   InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
460     return Entries.insert(make_pair(Name, std::move(Child)))
461         .first->second.get();
462   }
463
464   typedef decltype(Entries)::const_iterator const_iterator;
465   const_iterator begin() const { return Entries.begin(); }
466   const_iterator end() const { return Entries.end(); }
467
468   std::string toString(unsigned Indent) const override {
469     std::string Result =
470         (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
471     for (const auto &Entry : Entries) {
472       Result += Entry.second->toString(Indent + 2);
473     }
474     return Result;
475   }
476   static bool classof(const InMemoryNode *N) {
477     return N->getKind() == IME_Directory;
478   }
479 };
480 }
481
482 InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
483     : Root(new detail::InMemoryDirectory(
484           Status("", getNextVirtualUniqueID(), llvm::sys::TimeValue::MinTime(),
485                  0, 0, 0, llvm::sys::fs::file_type::directory_file,
486                  llvm::sys::fs::perms::all_all))),
487       UseNormalizedPaths(UseNormalizedPaths) {}
488
489 InMemoryFileSystem::~InMemoryFileSystem() {}
490
491 std::string InMemoryFileSystem::toString() const {
492   return Root->toString(/*Indent=*/0);
493 }
494
495 bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
496                                  std::unique_ptr<llvm::MemoryBuffer> Buffer) {
497   SmallString<128> Path;
498   P.toVector(Path);
499
500   // Fix up relative paths. This just prepends the current working directory.
501   std::error_code EC = makeAbsolute(Path);
502   assert(!EC);
503   (void)EC;
504
505   if (useNormalizedPaths())
506     llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
507
508   if (Path.empty())
509     return false;
510
511   detail::InMemoryDirectory *Dir = Root.get();
512   auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
513   while (true) {
514     StringRef Name = *I;
515     detail::InMemoryNode *Node = Dir->getChild(Name);
516     ++I;
517     if (!Node) {
518       if (I == E) {
519         // End of the path, create a new file.
520         // FIXME: expose the status details in the interface.
521         Status Stat(P.str(), getNextVirtualUniqueID(),
522                     llvm::sys::TimeValue(ModificationTime, 0), 0, 0,
523                     Buffer->getBufferSize(),
524                     llvm::sys::fs::file_type::regular_file,
525                     llvm::sys::fs::all_all);
526         Dir->addChild(Name, llvm::make_unique<detail::InMemoryFile>(
527                                 std::move(Stat), std::move(Buffer)));
528         return true;
529       }
530
531       // Create a new directory. Use the path up to here.
532       // FIXME: expose the status details in the interface.
533       Status Stat(
534           StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
535           getNextVirtualUniqueID(), llvm::sys::TimeValue(ModificationTime, 0),
536           0, 0, Buffer->getBufferSize(),
537           llvm::sys::fs::file_type::directory_file, llvm::sys::fs::all_all);
538       Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
539           Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
540       continue;
541     }
542
543     if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) {
544       Dir = NewDir;
545     } else {
546       assert(isa<detail::InMemoryFile>(Node) &&
547              "Must be either file or directory!");
548
549       // Trying to insert a directory in place of a file.
550       if (I != E)
551         return false;
552
553       // Return false only if the new file is different from the existing one.
554       return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
555              Buffer->getBuffer();
556     }
557   }
558 }
559
560 bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
561                                       llvm::MemoryBuffer *Buffer) {
562   return addFile(P, ModificationTime,
563                  llvm::MemoryBuffer::getMemBuffer(
564                      Buffer->getBuffer(), Buffer->getBufferIdentifier()));
565 }
566
567 static ErrorOr<detail::InMemoryNode *>
568 lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir,
569                    const Twine &P) {
570   SmallString<128> Path;
571   P.toVector(Path);
572
573   // Fix up relative paths. This just prepends the current working directory.
574   std::error_code EC = FS.makeAbsolute(Path);
575   assert(!EC);
576   (void)EC;
577
578   if (FS.useNormalizedPaths())
579     llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
580
581   if (Path.empty())
582     return Dir;
583
584   auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
585   while (true) {
586     detail::InMemoryNode *Node = Dir->getChild(*I);
587     ++I;
588     if (!Node)
589       return errc::no_such_file_or_directory;
590
591     // Return the file if it's at the end of the path.
592     if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
593       if (I == E)
594         return File;
595       return errc::no_such_file_or_directory;
596     }
597
598     // Traverse directories.
599     Dir = cast<detail::InMemoryDirectory>(Node);
600     if (I == E)
601       return Dir;
602   }
603 }
604
605 llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
606   auto Node = lookupInMemoryNode(*this, Root.get(), Path);
607   if (Node)
608     return (*Node)->getStatus();
609   return Node.getError();
610 }
611
612 llvm::ErrorOr<std::unique_ptr<File>>
613 InMemoryFileSystem::openFileForRead(const Twine &Path) {
614   auto Node = lookupInMemoryNode(*this, Root.get(), Path);
615   if (!Node)
616     return Node.getError();
617
618   // When we have a file provide a heap-allocated wrapper for the memory buffer
619   // to match the ownership semantics for File.
620   if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
621     return std::unique_ptr<File>(new detail::InMemoryFileAdaptor(*F));
622
623   // FIXME: errc::not_a_file?
624   return make_error_code(llvm::errc::invalid_argument);
625 }
626
627 namespace {
628 /// Adaptor from InMemoryDir::iterator to directory_iterator.
629 class InMemoryDirIterator : public clang::vfs::detail::DirIterImpl {
630   detail::InMemoryDirectory::const_iterator I;
631   detail::InMemoryDirectory::const_iterator E;
632
633 public:
634   InMemoryDirIterator() {}
635   explicit InMemoryDirIterator(detail::InMemoryDirectory &Dir)
636       : I(Dir.begin()), E(Dir.end()) {
637     if (I != E)
638       CurrentEntry = I->second->getStatus();
639   }
640
641   std::error_code increment() override {
642     ++I;
643     // When we're at the end, make CurrentEntry invalid and DirIterImpl will do
644     // the rest.
645     CurrentEntry = I != E ? I->second->getStatus() : Status();
646     return std::error_code();
647   }
648 };
649 } // end anonymous namespace
650
651 directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
652                                                  std::error_code &EC) {
653   auto Node = lookupInMemoryNode(*this, Root.get(), Dir);
654   if (!Node) {
655     EC = Node.getError();
656     return directory_iterator(std::make_shared<InMemoryDirIterator>());
657   }
658
659   if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
660     return directory_iterator(std::make_shared<InMemoryDirIterator>(*DirNode));
661
662   EC = make_error_code(llvm::errc::not_a_directory);
663   return directory_iterator(std::make_shared<InMemoryDirIterator>());
664 }
665
666 std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) {
667   SmallString<128> Path;
668   P.toVector(Path);
669
670   // Fix up relative paths. This just prepends the current working directory.
671   std::error_code EC = makeAbsolute(Path);
672   assert(!EC);
673   (void)EC;
674
675   if (useNormalizedPaths())
676     llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
677
678   if (!Path.empty())
679     WorkingDirectory = Path.str();
680   return std::error_code();
681 }
682 }
683 }
684
685 //===-----------------------------------------------------------------------===/
686 // RedirectingFileSystem implementation
687 //===-----------------------------------------------------------------------===/
688
689 namespace {
690
691 enum EntryKind {
692   EK_Directory,
693   EK_File
694 };
695
696 /// \brief A single file or directory in the VFS.
697 class Entry {
698   EntryKind Kind;
699   std::string Name;
700
701 public:
702   virtual ~Entry();
703   Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
704   StringRef getName() const { return Name; }
705   EntryKind getKind() const { return Kind; }
706 };
707
708 class RedirectingDirectoryEntry : public Entry {
709   std::vector<std::unique_ptr<Entry>> Contents;
710   Status S;
711
712 public:
713   RedirectingDirectoryEntry(StringRef Name,
714                             std::vector<std::unique_ptr<Entry>> Contents,
715                             Status S)
716       : Entry(EK_Directory, Name), Contents(std::move(Contents)),
717         S(std::move(S)) {}
718   Status getStatus() { return S; }
719   typedef decltype(Contents)::iterator iterator;
720   iterator contents_begin() { return Contents.begin(); }
721   iterator contents_end() { return Contents.end(); }
722   static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
723 };
724
725 class RedirectingFileEntry : public Entry {
726 public:
727   enum NameKind {
728     NK_NotSet,
729     NK_External,
730     NK_Virtual
731   };
732 private:
733   std::string ExternalContentsPath;
734   NameKind UseName;
735 public:
736   RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath,
737                        NameKind UseName)
738       : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
739         UseName(UseName) {}
740   StringRef getExternalContentsPath() const { return ExternalContentsPath; }
741   /// \brief whether to use the external path as the name for this file.
742   bool useExternalName(bool GlobalUseExternalName) const {
743     return UseName == NK_NotSet ? GlobalUseExternalName
744                                 : (UseName == NK_External);
745   }
746   static bool classof(const Entry *E) { return E->getKind() == EK_File; }
747 };
748
749 class RedirectingFileSystem;
750
751 class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
752   std::string Dir;
753   RedirectingFileSystem &FS;
754   RedirectingDirectoryEntry::iterator Current, End;
755
756 public:
757   VFSFromYamlDirIterImpl(const Twine &Path, RedirectingFileSystem &FS,
758                          RedirectingDirectoryEntry::iterator Begin,
759                          RedirectingDirectoryEntry::iterator End,
760                          std::error_code &EC);
761   std::error_code increment() override;
762 };
763
764 /// \brief A virtual file system parsed from a YAML file.
765 ///
766 /// Currently, this class allows creating virtual directories and mapping
767 /// virtual file paths to existing external files, available in \c ExternalFS.
768 ///
769 /// The basic structure of the parsed file is:
770 /// \verbatim
771 /// {
772 ///   'version': <version number>,
773 ///   <optional configuration>
774 ///   'roots': [
775 ///              <directory entries>
776 ///            ]
777 /// }
778 /// \endverbatim
779 ///
780 /// All configuration options are optional.
781 ///   'case-sensitive': <boolean, default=true>
782 ///   'use-external-names': <boolean, default=true>
783 ///
784 /// Virtual directories are represented as
785 /// \verbatim
786 /// {
787 ///   'type': 'directory',
788 ///   'name': <string>,
789 ///   'contents': [ <file or directory entries> ]
790 /// }
791 /// \endverbatim
792 ///
793 /// The default attributes for virtual directories are:
794 /// \verbatim
795 /// MTime = now() when created
796 /// Perms = 0777
797 /// User = Group = 0
798 /// Size = 0
799 /// UniqueID = unspecified unique value
800 /// \endverbatim
801 ///
802 /// Re-mapped files are represented as
803 /// \verbatim
804 /// {
805 ///   'type': 'file',
806 ///   'name': <string>,
807 ///   'use-external-name': <boolean> # Optional
808 ///   'external-contents': <path to external file>)
809 /// }
810 /// \endverbatim
811 ///
812 /// and inherit their attributes from the external contents.
813 ///
814 /// In both cases, the 'name' field may contain multiple path components (e.g.
815 /// /path/to/file). However, any directory that contains more than one child
816 /// must be uniquely represented by a directory entry.
817 class RedirectingFileSystem : public vfs::FileSystem {
818   /// The root(s) of the virtual file system.
819   std::vector<std::unique_ptr<Entry>> Roots;
820   /// \brief The file system to use for external references.
821   IntrusiveRefCntPtr<FileSystem> ExternalFS;
822
823   /// @name Configuration
824   /// @{
825
826   /// \brief Whether to perform case-sensitive comparisons.
827   ///
828   /// Currently, case-insensitive matching only works correctly with ASCII.
829   bool CaseSensitive;
830
831   /// \brief Whether to use to use the value of 'external-contents' for the
832   /// names of files.  This global value is overridable on a per-file basis.
833   bool UseExternalNames;
834   /// @}
835
836   /// Virtual file paths and external files could be canonicalized without "..",
837   /// "." and "./" in their paths. FIXME: some unittests currently fail on
838   /// win32 when using remove_dots and remove_leading_dotslash on paths.
839   bool UseCanonicalizedPaths =
840 #ifdef LLVM_ON_WIN32
841       false;
842 #else
843       true;
844 #endif
845
846   friend class RedirectingFileSystemParser;
847
848 private:
849   RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)
850       : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
851
852   /// \brief Looks up \p Path in \c Roots.
853   ErrorOr<Entry *> lookupPath(const Twine &Path);
854
855   /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
856   /// recursing into the contents of \p From if it is a directory.
857   ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
858                               sys::path::const_iterator End, Entry *From);
859
860   /// \brief Get the status of a given an \c Entry.
861   ErrorOr<Status> status(const Twine &Path, Entry *E);
862
863 public:
864   /// \brief Parses \p Buffer, which is expected to be in YAML format and
865   /// returns a virtual file system representing its contents.
866   static RedirectingFileSystem *
867   create(std::unique_ptr<MemoryBuffer> Buffer,
868          SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
869          IntrusiveRefCntPtr<FileSystem> ExternalFS);
870
871   ErrorOr<Status> status(const Twine &Path) override;
872   ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
873
874   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
875     return ExternalFS->getCurrentWorkingDirectory();
876   }
877   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
878     return ExternalFS->setCurrentWorkingDirectory(Path);
879   }
880
881   directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
882     ErrorOr<Entry *> E = lookupPath(Dir);
883     if (!E) {
884       EC = E.getError();
885       return directory_iterator();
886     }
887     ErrorOr<Status> S = status(Dir, *E);
888     if (!S) {
889       EC = S.getError();
890       return directory_iterator();
891     }
892     if (!S->isDirectory()) {
893       EC = std::error_code(static_cast<int>(errc::not_a_directory),
894                            std::system_category());
895       return directory_iterator();
896     }
897
898     auto *D = cast<RedirectingDirectoryEntry>(*E);
899     return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
900         *this, D->contents_begin(), D->contents_end(), EC));
901   }
902 };
903
904 /// \brief A helper class to hold the common YAML parsing state.
905 class RedirectingFileSystemParser {
906   yaml::Stream &Stream;
907
908   void error(yaml::Node *N, const Twine &Msg) {
909     Stream.printError(N, Msg);
910   }
911
912   // false on error
913   bool parseScalarString(yaml::Node *N, StringRef &Result,
914                          SmallVectorImpl<char> &Storage) {
915     yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
916     if (!S) {
917       error(N, "expected string");
918       return false;
919     }
920     Result = S->getValue(Storage);
921     return true;
922   }
923
924   // false on error
925   bool parseScalarBool(yaml::Node *N, bool &Result) {
926     SmallString<5> Storage;
927     StringRef Value;
928     if (!parseScalarString(N, Value, Storage))
929       return false;
930
931     if (Value.equals_lower("true") || Value.equals_lower("on") ||
932         Value.equals_lower("yes") || Value == "1") {
933       Result = true;
934       return true;
935     } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
936                Value.equals_lower("no") || Value == "0") {
937       Result = false;
938       return true;
939     }
940
941     error(N, "expected boolean value");
942     return false;
943   }
944
945   struct KeyStatus {
946     KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
947     bool Required;
948     bool Seen;
949   };
950   typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
951
952   // false on error
953   bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
954                                   DenseMap<StringRef, KeyStatus> &Keys) {
955     if (!Keys.count(Key)) {
956       error(KeyNode, "unknown key");
957       return false;
958     }
959     KeyStatus &S = Keys[Key];
960     if (S.Seen) {
961       error(KeyNode, Twine("duplicate key '") + Key + "'");
962       return false;
963     }
964     S.Seen = true;
965     return true;
966   }
967
968   // false on error
969   bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
970     for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
971          E = Keys.end();
972          I != E; ++I) {
973       if (I->second.Required && !I->second.Seen) {
974         error(Obj, Twine("missing key '") + I->first + "'");
975         return false;
976       }
977     }
978     return true;
979   }
980
981   std::unique_ptr<Entry> parseEntry(yaml::Node *N, RedirectingFileSystem *FS) {
982     yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
983     if (!M) {
984       error(N, "expected mapping node for file or directory entry");
985       return nullptr;
986     }
987
988     KeyStatusPair Fields[] = {
989       KeyStatusPair("name", true),
990       KeyStatusPair("type", true),
991       KeyStatusPair("contents", false),
992       KeyStatusPair("external-contents", false),
993       KeyStatusPair("use-external-name", false),
994     };
995
996     DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
997
998     bool HasContents = false; // external or otherwise
999     std::vector<std::unique_ptr<Entry>> EntryArrayContents;
1000     std::string ExternalContentsPath;
1001     std::string Name;
1002     auto UseExternalName = RedirectingFileEntry::NK_NotSet;
1003     EntryKind Kind;
1004
1005     for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
1006          ++I) {
1007       StringRef Key;
1008       // Reuse the buffer for key and value, since we don't look at key after
1009       // parsing value.
1010       SmallString<256> Buffer;
1011       if (!parseScalarString(I->getKey(), Key, Buffer))
1012         return nullptr;
1013
1014       if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
1015         return nullptr;
1016
1017       StringRef Value;
1018       if (Key == "name") {
1019         if (!parseScalarString(I->getValue(), Value, Buffer))
1020           return nullptr;
1021
1022         if (FS->UseCanonicalizedPaths) {
1023           SmallString<256> Path(Value);
1024           // Guarantee that old YAML files containing paths with ".." and "."
1025           // are properly canonicalized before read into the VFS.
1026           Path = sys::path::remove_leading_dotslash(Path);
1027           sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1028           Name = Path.str();
1029         } else {
1030           Name = Value;
1031         }
1032       } else if (Key == "type") {
1033         if (!parseScalarString(I->getValue(), Value, Buffer))
1034           return nullptr;
1035         if (Value == "file")
1036           Kind = EK_File;
1037         else if (Value == "directory")
1038           Kind = EK_Directory;
1039         else {
1040           error(I->getValue(), "unknown value for 'type'");
1041           return nullptr;
1042         }
1043       } else if (Key == "contents") {
1044         if (HasContents) {
1045           error(I->getKey(),
1046                 "entry already has 'contents' or 'external-contents'");
1047           return nullptr;
1048         }
1049         HasContents = true;
1050         yaml::SequenceNode *Contents =
1051             dyn_cast<yaml::SequenceNode>(I->getValue());
1052         if (!Contents) {
1053           // FIXME: this is only for directories, what about files?
1054           error(I->getValue(), "expected array");
1055           return nullptr;
1056         }
1057
1058         for (yaml::SequenceNode::iterator I = Contents->begin(),
1059                                           E = Contents->end();
1060              I != E; ++I) {
1061           if (std::unique_ptr<Entry> E = parseEntry(&*I, FS))
1062             EntryArrayContents.push_back(std::move(E));
1063           else
1064             return nullptr;
1065         }
1066       } else if (Key == "external-contents") {
1067         if (HasContents) {
1068           error(I->getKey(),
1069                 "entry already has 'contents' or 'external-contents'");
1070           return nullptr;
1071         }
1072         HasContents = true;
1073         if (!parseScalarString(I->getValue(), Value, Buffer))
1074           return nullptr;
1075         if (FS->UseCanonicalizedPaths) {
1076           SmallString<256> Path(Value);
1077           // Guarantee that old YAML files containing paths with ".." and "."
1078           // are properly canonicalized before read into the VFS.
1079           Path = sys::path::remove_leading_dotslash(Path);
1080           sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1081           ExternalContentsPath = Path.str();
1082         } else {
1083           ExternalContentsPath = Value;
1084         }
1085       } else if (Key == "use-external-name") {
1086         bool Val;
1087         if (!parseScalarBool(I->getValue(), Val))
1088           return nullptr;
1089         UseExternalName = Val ? RedirectingFileEntry::NK_External
1090                               : RedirectingFileEntry::NK_Virtual;
1091       } else {
1092         llvm_unreachable("key missing from Keys");
1093       }
1094     }
1095
1096     if (Stream.failed())
1097       return nullptr;
1098
1099     // check for missing keys
1100     if (!HasContents) {
1101       error(N, "missing key 'contents' or 'external-contents'");
1102       return nullptr;
1103     }
1104     if (!checkMissingKeys(N, Keys))
1105       return nullptr;
1106
1107     // check invalid configuration
1108     if (Kind == EK_Directory &&
1109         UseExternalName != RedirectingFileEntry::NK_NotSet) {
1110       error(N, "'use-external-name' is not supported for directories");
1111       return nullptr;
1112     }
1113
1114     // Remove trailing slash(es), being careful not to remove the root path
1115     StringRef Trimmed(Name);
1116     size_t RootPathLen = sys::path::root_path(Trimmed).size();
1117     while (Trimmed.size() > RootPathLen &&
1118            sys::path::is_separator(Trimmed.back()))
1119       Trimmed = Trimmed.slice(0, Trimmed.size()-1);
1120     // Get the last component
1121     StringRef LastComponent = sys::path::filename(Trimmed);
1122
1123     std::unique_ptr<Entry> Result;
1124     switch (Kind) {
1125     case EK_File:
1126       Result = llvm::make_unique<RedirectingFileEntry>(
1127           LastComponent, std::move(ExternalContentsPath), UseExternalName);
1128       break;
1129     case EK_Directory:
1130       Result = llvm::make_unique<RedirectingDirectoryEntry>(
1131           LastComponent, std::move(EntryArrayContents),
1132           Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
1133                  file_type::directory_file, sys::fs::all_all));
1134       break;
1135     }
1136
1137     StringRef Parent = sys::path::parent_path(Trimmed);
1138     if (Parent.empty())
1139       return Result;
1140
1141     // if 'name' contains multiple components, create implicit directory entries
1142     for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
1143                                      E = sys::path::rend(Parent);
1144          I != E; ++I) {
1145       std::vector<std::unique_ptr<Entry>> Entries;
1146       Entries.push_back(std::move(Result));
1147       Result = llvm::make_unique<RedirectingDirectoryEntry>(
1148           *I, std::move(Entries),
1149           Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
1150                  file_type::directory_file, sys::fs::all_all));
1151     }
1152     return Result;
1153   }
1154
1155 public:
1156   RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
1157
1158   // false on error
1159   bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
1160     yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
1161     if (!Top) {
1162       error(Root, "expected mapping node");
1163       return false;
1164     }
1165
1166     KeyStatusPair Fields[] = {
1167       KeyStatusPair("version", true),
1168       KeyStatusPair("case-sensitive", false),
1169       KeyStatusPair("use-external-names", false),
1170       KeyStatusPair("roots", true),
1171     };
1172
1173     DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1174
1175     // Parse configuration and 'roots'
1176     for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
1177          ++I) {
1178       SmallString<10> KeyBuffer;
1179       StringRef Key;
1180       if (!parseScalarString(I->getKey(), Key, KeyBuffer))
1181         return false;
1182
1183       if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
1184         return false;
1185
1186       if (Key == "roots") {
1187         yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
1188         if (!Roots) {
1189           error(I->getValue(), "expected array");
1190           return false;
1191         }
1192
1193         for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
1194              I != E; ++I) {
1195           if (std::unique_ptr<Entry> E = parseEntry(&*I, FS))
1196             FS->Roots.push_back(std::move(E));
1197           else
1198             return false;
1199         }
1200       } else if (Key == "version") {
1201         StringRef VersionString;
1202         SmallString<4> Storage;
1203         if (!parseScalarString(I->getValue(), VersionString, Storage))
1204           return false;
1205         int Version;
1206         if (VersionString.getAsInteger<int>(10, Version)) {
1207           error(I->getValue(), "expected integer");
1208           return false;
1209         }
1210         if (Version < 0) {
1211           error(I->getValue(), "invalid version number");
1212           return false;
1213         }
1214         if (Version != 0) {
1215           error(I->getValue(), "version mismatch, expected 0");
1216           return false;
1217         }
1218       } else if (Key == "case-sensitive") {
1219         if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
1220           return false;
1221       } else if (Key == "use-external-names") {
1222         if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
1223           return false;
1224       } else {
1225         llvm_unreachable("key missing from Keys");
1226       }
1227     }
1228
1229     if (Stream.failed())
1230       return false;
1231
1232     if (!checkMissingKeys(Top, Keys))
1233       return false;
1234     return true;
1235   }
1236 };
1237 } // end of anonymous namespace
1238
1239 Entry::~Entry() = default;
1240
1241 RedirectingFileSystem *RedirectingFileSystem::create(
1242     std::unique_ptr<MemoryBuffer> Buffer, SourceMgr::DiagHandlerTy DiagHandler,
1243     void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1244
1245   SourceMgr SM;
1246   yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
1247
1248   SM.setDiagHandler(DiagHandler, DiagContext);
1249   yaml::document_iterator DI = Stream.begin();
1250   yaml::Node *Root = DI->getRoot();
1251   if (DI == Stream.end() || !Root) {
1252     SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
1253     return nullptr;
1254   }
1255
1256   RedirectingFileSystemParser P(Stream);
1257
1258   std::unique_ptr<RedirectingFileSystem> FS(
1259       new RedirectingFileSystem(ExternalFS));
1260   if (!P.parse(Root, FS.get()))
1261     return nullptr;
1262
1263   return FS.release();
1264 }
1265
1266 ErrorOr<Entry *> RedirectingFileSystem::lookupPath(const Twine &Path_) {
1267   SmallString<256> Path;
1268   Path_.toVector(Path);
1269
1270   // Handle relative paths
1271   if (std::error_code EC = makeAbsolute(Path))
1272     return EC;
1273
1274   // Canonicalize path by removing ".", "..", "./", etc components. This is
1275   // a VFS request, do bot bother about symlinks in the path components
1276   // but canonicalize in order to perform the correct entry search.
1277   if (UseCanonicalizedPaths) {
1278     Path = sys::path::remove_leading_dotslash(Path);
1279     sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1280   }
1281
1282   if (Path.empty())
1283     return make_error_code(llvm::errc::invalid_argument);
1284
1285   sys::path::const_iterator Start = sys::path::begin(Path);
1286   sys::path::const_iterator End = sys::path::end(Path);
1287   for (const std::unique_ptr<Entry> &Root : Roots) {
1288     ErrorOr<Entry *> Result = lookupPath(Start, End, Root.get());
1289     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
1290       return Result;
1291   }
1292   return make_error_code(llvm::errc::no_such_file_or_directory);
1293 }
1294
1295 ErrorOr<Entry *>
1296 RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
1297                                   sys::path::const_iterator End, Entry *From) {
1298 #ifndef LLVM_ON_WIN32
1299   assert(!isTraversalComponent(*Start) &&
1300          !isTraversalComponent(From->getName()) &&
1301          "Paths should not contain traversal components");
1302 #else
1303   // FIXME: this is here to support windows, remove it once canonicalized
1304   // paths become globally default.
1305   if (Start->equals("."))
1306     ++Start;
1307 #endif
1308
1309   if (CaseSensitive ? !Start->equals(From->getName())
1310                     : !Start->equals_lower(From->getName()))
1311     // failure to match
1312     return make_error_code(llvm::errc::no_such_file_or_directory);
1313
1314   ++Start;
1315
1316   if (Start == End) {
1317     // Match!
1318     return From;
1319   }
1320
1321   auto *DE = dyn_cast<RedirectingDirectoryEntry>(From);
1322   if (!DE)
1323     return make_error_code(llvm::errc::not_a_directory);
1324
1325   for (const std::unique_ptr<Entry> &DirEntry :
1326        llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1327     ErrorOr<Entry *> Result = lookupPath(Start, End, DirEntry.get());
1328     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
1329       return Result;
1330   }
1331   return make_error_code(llvm::errc::no_such_file_or_directory);
1332 }
1333
1334 static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
1335                                       Status ExternalStatus) {
1336   Status S = ExternalStatus;
1337   if (!UseExternalNames)
1338     S = Status::copyWithNewName(S, Path.str());
1339   S.IsVFSMapped = true;
1340   return S;
1341 }
1342
1343 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) {
1344   assert(E != nullptr);
1345   if (auto *F = dyn_cast<RedirectingFileEntry>(E)) {
1346     ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
1347     assert(!S || S->getName() == F->getExternalContentsPath());
1348     if (S)
1349       return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
1350                                      *S);
1351     return S;
1352   } else { // directory
1353     auto *DE = cast<RedirectingDirectoryEntry>(E);
1354     return Status::copyWithNewName(DE->getStatus(), Path.str());
1355   }
1356 }
1357
1358 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
1359   ErrorOr<Entry *> Result = lookupPath(Path);
1360   if (!Result)
1361     return Result.getError();
1362   return status(Path, *Result);
1363 }
1364
1365 namespace {
1366 /// Provide a file wrapper with an overriden status.
1367 class FileWithFixedStatus : public File {
1368   std::unique_ptr<File> InnerFile;
1369   Status S;
1370
1371 public:
1372   FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
1373       : InnerFile(std::move(InnerFile)), S(S) {}
1374
1375   ErrorOr<Status> status() override { return S; }
1376   ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
1377   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
1378             bool IsVolatile) override {
1379     return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
1380                                 IsVolatile);
1381   }
1382   std::error_code close() override { return InnerFile->close(); }
1383 };
1384 } // end anonymous namespace
1385
1386 ErrorOr<std::unique_ptr<File>>
1387 RedirectingFileSystem::openFileForRead(const Twine &Path) {
1388   ErrorOr<Entry *> E = lookupPath(Path);
1389   if (!E)
1390     return E.getError();
1391
1392   auto *F = dyn_cast<RedirectingFileEntry>(*E);
1393   if (!F) // FIXME: errc::not_a_file?
1394     return make_error_code(llvm::errc::invalid_argument);
1395
1396   auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
1397   if (!Result)
1398     return Result;
1399
1400   auto ExternalStatus = (*Result)->status();
1401   if (!ExternalStatus)
1402     return ExternalStatus.getError();
1403
1404   // FIXME: Update the status with the name and VFSMapped.
1405   Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
1406                                      *ExternalStatus);
1407   return std::unique_ptr<File>(
1408       llvm::make_unique<FileWithFixedStatus>(std::move(*Result), S));
1409 }
1410
1411 IntrusiveRefCntPtr<FileSystem>
1412 vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1413                     SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
1414                     IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1415   return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
1416                                        DiagContext, ExternalFS);
1417 }
1418
1419 UniqueID vfs::getNextVirtualUniqueID() {
1420   static std::atomic<unsigned> UID;
1421   unsigned ID = ++UID;
1422   // The following assumes that uint64_t max will never collide with a real
1423   // dev_t value from the OS.
1424   return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
1425 }
1426
1427 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1428   assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1429   assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1430   assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1431   Mappings.emplace_back(VirtualPath, RealPath);
1432 }
1433
1434 namespace {
1435 class JSONWriter {
1436   llvm::raw_ostream &OS;
1437   SmallVector<StringRef, 16> DirStack;
1438   inline unsigned getDirIndent() { return 4 * DirStack.size(); }
1439   inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1440   bool containedIn(StringRef Parent, StringRef Path);
1441   StringRef containedPart(StringRef Parent, StringRef Path);
1442   void startDirectory(StringRef Path);
1443   void endDirectory();
1444   void writeEntry(StringRef VPath, StringRef RPath);
1445
1446 public:
1447   JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1448   void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> IsCaseSensitive);
1449 };
1450 }
1451
1452 bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
1453   using namespace llvm::sys;
1454   // Compare each path component.
1455   auto IParent = path::begin(Parent), EParent = path::end(Parent);
1456   for (auto IChild = path::begin(Path), EChild = path::end(Path);
1457        IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
1458     if (*IParent != *IChild)
1459       return false;
1460   }
1461   // Have we exhausted the parent path?
1462   return IParent == EParent;
1463 }
1464
1465 StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1466   assert(!Parent.empty());
1467   assert(containedIn(Parent, Path));
1468   return Path.slice(Parent.size() + 1, StringRef::npos);
1469 }
1470
1471 void JSONWriter::startDirectory(StringRef Path) {
1472   StringRef Name =
1473       DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
1474   DirStack.push_back(Path);
1475   unsigned Indent = getDirIndent();
1476   OS.indent(Indent) << "{\n";
1477   OS.indent(Indent + 2) << "'type': 'directory',\n";
1478   OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1479   OS.indent(Indent + 2) << "'contents': [\n";
1480 }
1481
1482 void JSONWriter::endDirectory() {
1483   unsigned Indent = getDirIndent();
1484   OS.indent(Indent + 2) << "]\n";
1485   OS.indent(Indent) << "}";
1486
1487   DirStack.pop_back();
1488 }
1489
1490 void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1491   unsigned Indent = getFileIndent();
1492   OS.indent(Indent) << "{\n";
1493   OS.indent(Indent + 2) << "'type': 'file',\n";
1494   OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1495   OS.indent(Indent + 2) << "'external-contents': \""
1496                         << llvm::yaml::escape(RPath) << "\"\n";
1497   OS.indent(Indent) << "}";
1498 }
1499
1500 void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1501                        Optional<bool> IsCaseSensitive) {
1502   using namespace llvm::sys;
1503
1504   OS << "{\n"
1505         "  'version': 0,\n";
1506   if (IsCaseSensitive.hasValue())
1507     OS << "  'case-sensitive': '"
1508        << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
1509   OS << "  'roots': [\n";
1510
1511   if (!Entries.empty()) {
1512     const YAMLVFSEntry &Entry = Entries.front();
1513     startDirectory(path::parent_path(Entry.VPath));
1514     writeEntry(path::filename(Entry.VPath), Entry.RPath);
1515
1516     for (const auto &Entry : Entries.slice(1)) {
1517       StringRef Dir = path::parent_path(Entry.VPath);
1518       if (Dir == DirStack.back())
1519         OS << ",\n";
1520       else {
1521         while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
1522           OS << "\n";
1523           endDirectory();
1524         }
1525         OS << ",\n";
1526         startDirectory(Dir);
1527       }
1528       writeEntry(path::filename(Entry.VPath), Entry.RPath);
1529     }
1530
1531     while (!DirStack.empty()) {
1532       OS << "\n";
1533       endDirectory();
1534     }
1535     OS << "\n";
1536   }
1537
1538   OS << "  ]\n"
1539      << "}\n";
1540 }
1541
1542 void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
1543   std::sort(Mappings.begin(), Mappings.end(),
1544             [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
1545     return LHS.VPath < RHS.VPath;
1546   });
1547
1548   JSONWriter(OS).write(Mappings, IsCaseSensitive);
1549 }
1550
1551 VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(
1552     const Twine &_Path, RedirectingFileSystem &FS,
1553     RedirectingDirectoryEntry::iterator Begin,
1554     RedirectingDirectoryEntry::iterator End, std::error_code &EC)
1555     : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
1556   if (Current != End) {
1557     SmallString<128> PathStr(Dir);
1558     llvm::sys::path::append(PathStr, (*Current)->getName());
1559     llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
1560     if (S)
1561       CurrentEntry = *S;
1562     else
1563       EC = S.getError();
1564   }
1565 }
1566
1567 std::error_code VFSFromYamlDirIterImpl::increment() {
1568   assert(Current != End && "cannot iterate past end");
1569   if (++Current != End) {
1570     SmallString<128> PathStr(Dir);
1571     llvm::sys::path::append(PathStr, (*Current)->getName());
1572     llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
1573     if (!S)
1574       return S.getError();
1575     CurrentEntry = *S;
1576   } else {
1577     CurrentEntry = Status();
1578   }
1579   return std::error_code();
1580 }
1581
1582 vfs::recursive_directory_iterator::recursive_directory_iterator(FileSystem &FS_,
1583                                                            const Twine &Path,
1584                                                            std::error_code &EC)
1585     : FS(&FS_) {
1586   directory_iterator I = FS->dir_begin(Path, EC);
1587   if (!EC && I != directory_iterator()) {
1588     State = std::make_shared<IterState>();
1589     State->push(I);
1590   }
1591 }
1592
1593 vfs::recursive_directory_iterator &
1594 recursive_directory_iterator::increment(std::error_code &EC) {
1595   assert(FS && State && !State->empty() && "incrementing past end");
1596   assert(State->top()->isStatusKnown() && "non-canonical end iterator");
1597   vfs::directory_iterator End;
1598   if (State->top()->isDirectory()) {
1599     vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
1600     if (EC)
1601       return *this;
1602     if (I != End) {
1603       State->push(I);
1604       return *this;
1605     }
1606   }
1607
1608   while (!State->empty() && State->top().increment(EC) == End)
1609     State->pop();
1610
1611   if (State->empty())
1612     State.reset(); // end iterator
1613
1614   return *this;
1615 }