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