clang  6.0.0svn
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  assert(isStatusKnown() && Other.isStatusKnown());
63  return getUniqueID() == Other.getUniqueID();
64 }
65 bool Status::isDirectory() const {
66  return Type == file_type::directory_file;
67 }
68 bool Status::isRegularFile() const {
69  return Type == file_type::regular_file;
70 }
71 bool Status::isOther() const {
72  return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
73 }
74 bool Status::isSymlink() const {
75  return Type == file_type::symlink_file;
76 }
77 bool Status::isStatusKnown() const {
78  return Type != file_type::status_error;
79 }
80 bool Status::exists() const {
81  return isStatusKnown() && Type != file_type::file_not_found;
82 }
83 
84 File::~File() {}
85 
86 FileSystem::~FileSystem() {}
87 
88 ErrorOr<std::unique_ptr<MemoryBuffer>>
89 FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
90  bool RequiresNullTerminator, bool IsVolatile) {
91  auto F = openFileForRead(Name);
92  if (!F)
93  return F.getError();
94 
95  return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
96 }
97 
98 std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
99  if (llvm::sys::path::is_absolute(Path))
100  return std::error_code();
101 
102  auto WorkingDir = getCurrentWorkingDirectory();
103  if (!WorkingDir)
104  return WorkingDir.getError();
105 
106  return llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
107 }
108 
109 bool FileSystem::exists(const Twine &Path) {
110  auto Status = status(Path);
111  return Status && Status->exists();
112 }
113 
114 #ifndef NDEBUG
115 static bool isTraversalComponent(StringRef Component) {
116  return Component.equals("..") || Component.equals(".");
117 }
118 
119 static bool pathHasTraversal(StringRef Path) {
120  using namespace llvm::sys;
121  for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
122  if (isTraversalComponent(Comp))
123  return true;
124  return false;
125 }
126 #endif
127 
128 //===-----------------------------------------------------------------------===/
129 // RealFileSystem implementation
130 //===-----------------------------------------------------------------------===/
131 
132 namespace {
133 /// \brief Wrapper around a raw file descriptor.
134 class RealFile : public File {
135  int FD;
136  Status S;
137  std::string RealName;
138  friend class RealFileSystem;
139  RealFile(int FD, StringRef NewName, StringRef NewRealPathName)
140  : FD(FD), S(NewName, {}, {}, {}, {}, {},
141  llvm::sys::fs::file_type::status_error, {}),
142  RealName(NewRealPathName.str()) {
143  assert(FD >= 0 && "Invalid or inactive file descriptor");
144  }
145 
146 public:
147  ~RealFile() override;
148  ErrorOr<Status> status() override;
149  ErrorOr<std::string> getName() override;
150  ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
151  int64_t FileSize,
152  bool RequiresNullTerminator,
153  bool IsVolatile) override;
154  std::error_code close() override;
155 };
156 } // end anonymous namespace
157 RealFile::~RealFile() { close(); }
158 
159 ErrorOr<Status> RealFile::status() {
160  assert(FD != -1 && "cannot stat closed file");
161  if (!S.isStatusKnown()) {
162  file_status RealStatus;
163  if (std::error_code EC = sys::fs::status(FD, RealStatus))
164  return EC;
165  S = Status::copyWithNewName(RealStatus, S.getName());
166  }
167  return S;
168 }
169 
170 ErrorOr<std::string> RealFile::getName() {
171  return RealName.empty() ? S.getName().str() : RealName;
172 }
173 
174 ErrorOr<std::unique_ptr<MemoryBuffer>>
175 RealFile::getBuffer(const Twine &Name, int64_t FileSize,
176  bool RequiresNullTerminator, bool IsVolatile) {
177  assert(FD != -1 && "cannot get buffer for closed file");
178  return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
179  IsVolatile);
180 }
181 
182 std::error_code RealFile::close() {
183  std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD);
184  FD = -1;
185  return EC;
186 }
187 
188 namespace {
189 /// \brief The file system according to your operating system.
190 class RealFileSystem : public FileSystem {
191 public:
192  ErrorOr<Status> status(const Twine &Path) override;
193  ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
194  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
195 
196  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
197  std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
198 };
199 } // end anonymous namespace
200 
201 ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
202  sys::fs::file_status RealStatus;
203  if (std::error_code EC = sys::fs::status(Path, RealStatus))
204  return EC;
205  return Status::copyWithNewName(RealStatus, Path.str());
206 }
207 
208 ErrorOr<std::unique_ptr<File>>
209 RealFileSystem::openFileForRead(const Twine &Name) {
210  int FD;
211  SmallString<256> RealName;
212  if (std::error_code EC = sys::fs::openFileForRead(Name, FD, &RealName))
213  return EC;
214  return std::unique_ptr<File>(new RealFile(FD, Name.str(), RealName.str()));
215 }
216 
217 llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
218  SmallString<256> Dir;
219  if (std::error_code EC = llvm::sys::fs::current_path(Dir))
220  return EC;
221  return Dir.str().str();
222 }
223 
224 std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
225  // FIXME: chdir is thread hostile; on the other hand, creating the same
226  // behavior as chdir is complex: chdir resolves the path once, thus
227  // guaranteeing that all subsequent relative path operations work
228  // on the same path the original chdir resulted in. This makes a
229  // difference for example on network filesystems, where symlinks might be
230  // switched during runtime of the tool. Fixing this depends on having a
231  // file system abstraction that allows openat() style interactions.
232  return llvm::sys::fs::set_current_path(Path);
233 }
234 
236  static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
237  return FS;
238 }
239 
240 namespace {
241 class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
242  llvm::sys::fs::directory_iterator Iter;
243 public:
244  RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
245  if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
246  llvm::sys::fs::file_status S;
247  EC = llvm::sys::fs::status(Iter->path(), S, true);
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 = llvm::sys::fs::status(Iter->path(), S, true);
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(std::move(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 
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 
395 
396 /// The in memory file system is a tree of Nodes. Every node can either be a
397 /// file or a directory.
399  Status Stat;
401 
402 public:
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 
448  std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
449 
450 public:
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::TimePoint<>(), 0, 0,
485  0, llvm::sys::fs::file_type::directory_file,
486  llvm::sys::fs::perms::all_all))),
487  UseNormalizedPaths(UseNormalizedPaths) {}
488 
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::toTimePoint(ModificationTime), 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::toTimePoint(ModificationTime), 0,
536  0, Buffer->getBufferSize(), llvm::sys::fs::file_type::directory_file,
537  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 *>
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>>
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 {
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 
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  RedirectingDirectoryEntry(StringRef Name, Status S)
719  : Entry(EK_Directory, Name), S(std::move(S)) {}
720  Status getStatus() { return S; }
721  void addContent(std::unique_ptr<Entry> Content) {
722  Contents.push_back(std::move(Content));
723  }
724  Entry *getLastContent() const { return Contents.back().get(); }
725  typedef decltype(Contents)::iterator iterator;
726  iterator contents_begin() { return Contents.begin(); }
727  iterator contents_end() { return Contents.end(); }
728  static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
729 };
730 
731 class RedirectingFileEntry : public Entry {
732 public:
733  enum NameKind {
734  NK_NotSet,
735  NK_External,
736  NK_Virtual
737  };
738 private:
739  std::string ExternalContentsPath;
740  NameKind UseName;
741 public:
742  RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath,
743  NameKind UseName)
744  : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
745  UseName(UseName) {}
746  StringRef getExternalContentsPath() const { return ExternalContentsPath; }
747  /// \brief whether to use the external path as the name for this file.
748  bool useExternalName(bool GlobalUseExternalName) const {
749  return UseName == NK_NotSet ? GlobalUseExternalName
750  : (UseName == NK_External);
751  }
752  NameKind getUseName() const { return UseName; }
753  static bool classof(const Entry *E) { return E->getKind() == EK_File; }
754 };
755 
756 class RedirectingFileSystem;
757 
758 class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
759  std::string Dir;
760  RedirectingFileSystem &FS;
761  RedirectingDirectoryEntry::iterator Current, End;
762 
763 public:
764  VFSFromYamlDirIterImpl(const Twine &Path, RedirectingFileSystem &FS,
765  RedirectingDirectoryEntry::iterator Begin,
766  RedirectingDirectoryEntry::iterator End,
767  std::error_code &EC);
768  std::error_code increment() override;
769 };
770 
771 /// \brief A virtual file system parsed from a YAML file.
772 ///
773 /// Currently, this class allows creating virtual directories and mapping
774 /// virtual file paths to existing external files, available in \c ExternalFS.
775 ///
776 /// The basic structure of the parsed file is:
777 /// \verbatim
778 /// {
779 /// 'version': <version number>,
780 /// <optional configuration>
781 /// 'roots': [
782 /// <directory entries>
783 /// ]
784 /// }
785 /// \endverbatim
786 ///
787 /// All configuration options are optional.
788 /// 'case-sensitive': <boolean, default=true>
789 /// 'use-external-names': <boolean, default=true>
790 /// 'overlay-relative': <boolean, default=false>
791 /// 'ignore-non-existent-contents': <boolean, default=true>
792 ///
793 /// Virtual directories are represented as
794 /// \verbatim
795 /// {
796 /// 'type': 'directory',
797 /// 'name': <string>,
798 /// 'contents': [ <file or directory entries> ]
799 /// }
800 /// \endverbatim
801 ///
802 /// The default attributes for virtual directories are:
803 /// \verbatim
804 /// MTime = now() when created
805 /// Perms = 0777
806 /// User = Group = 0
807 /// Size = 0
808 /// UniqueID = unspecified unique value
809 /// \endverbatim
810 ///
811 /// Re-mapped files are represented as
812 /// \verbatim
813 /// {
814 /// 'type': 'file',
815 /// 'name': <string>,
816 /// 'use-external-name': <boolean> # Optional
817 /// 'external-contents': <path to external file>)
818 /// }
819 /// \endverbatim
820 ///
821 /// and inherit their attributes from the external contents.
822 ///
823 /// In both cases, the 'name' field may contain multiple path components (e.g.
824 /// /path/to/file). However, any directory that contains more than one child
825 /// must be uniquely represented by a directory entry.
826 class RedirectingFileSystem : public vfs::FileSystem {
827  /// The root(s) of the virtual file system.
828  std::vector<std::unique_ptr<Entry>> Roots;
829  /// \brief The file system to use for external references.
831  /// If IsRelativeOverlay is set, this represents the directory
832  /// path that should be prefixed to each 'external-contents' entry
833  /// when reading from YAML files.
834  std::string ExternalContentsPrefixDir;
835 
836  /// @name Configuration
837  /// @{
838 
839  /// \brief Whether to perform case-sensitive comparisons.
840  ///
841  /// Currently, case-insensitive matching only works correctly with ASCII.
842  bool CaseSensitive = true;
843 
844  /// IsRelativeOverlay marks whether a IsExternalContentsPrefixDir path must
845  /// be prefixed in every 'external-contents' when reading from YAML files.
846  bool IsRelativeOverlay = false;
847 
848  /// \brief Whether to use to use the value of 'external-contents' for the
849  /// names of files. This global value is overridable on a per-file basis.
850  bool UseExternalNames = true;
851 
852  /// \brief Whether an invalid path obtained via 'external-contents' should
853  /// cause iteration on the VFS to stop. If 'true', the VFS should ignore
854  /// the entry and continue with the next. Allows YAML files to be shared
855  /// across multiple compiler invocations regardless of prior existent
856  /// paths in 'external-contents'. This global value is overridable on a
857  /// per-file basis.
858  bool IgnoreNonExistentContents = true;
859  /// @}
860 
861  /// Virtual file paths and external files could be canonicalized without "..",
862  /// "." and "./" in their paths. FIXME: some unittests currently fail on
863  /// win32 when using remove_dots and remove_leading_dotslash on paths.
864  bool UseCanonicalizedPaths =
865 #ifdef LLVM_ON_WIN32
866  false;
867 #else
868  true;
869 #endif
870 
871  friend class RedirectingFileSystemParser;
872 
873 private:
874  RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)
875  : ExternalFS(std::move(ExternalFS)) {}
876 
877  /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
878  /// recursing into the contents of \p From if it is a directory.
879  ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
880  sys::path::const_iterator End, Entry *From);
881 
882  /// \brief Get the status of a given an \c Entry.
883  ErrorOr<Status> status(const Twine &Path, Entry *E);
884 
885 public:
886  /// \brief Looks up \p Path in \c Roots.
887  ErrorOr<Entry *> lookupPath(const Twine &Path);
888 
889  /// \brief Parses \p Buffer, which is expected to be in YAML format and
890  /// returns a virtual file system representing its contents.
891  static RedirectingFileSystem *
892  create(std::unique_ptr<MemoryBuffer> Buffer,
893  SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath,
894  void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS);
895 
896  ErrorOr<Status> status(const Twine &Path) override;
897  ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
898 
899  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
900  return ExternalFS->getCurrentWorkingDirectory();
901  }
902  std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
903  return ExternalFS->setCurrentWorkingDirectory(Path);
904  }
905 
906  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
907  ErrorOr<Entry *> E = lookupPath(Dir);
908  if (!E) {
909  EC = E.getError();
910  return directory_iterator();
911  }
912  ErrorOr<Status> S = status(Dir, *E);
913  if (!S) {
914  EC = S.getError();
915  return directory_iterator();
916  }
917  if (!S->isDirectory()) {
918  EC = std::error_code(static_cast<int>(errc::not_a_directory),
919  std::system_category());
920  return directory_iterator();
921  }
922 
923  auto *D = cast<RedirectingDirectoryEntry>(*E);
924  return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
925  *this, D->contents_begin(), D->contents_end(), EC));
926  }
927 
928  void setExternalContentsPrefixDir(StringRef PrefixDir) {
929  ExternalContentsPrefixDir = PrefixDir.str();
930  }
931 
932  StringRef getExternalContentsPrefixDir() const {
933  return ExternalContentsPrefixDir;
934  }
935 
936  bool ignoreNonExistentContents() const {
937  return IgnoreNonExistentContents;
938  }
939 
940 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
941 LLVM_DUMP_METHOD void dump() const {
942  for (const std::unique_ptr<Entry> &Root : Roots)
943  dumpEntry(Root.get());
944  }
945 
946 LLVM_DUMP_METHOD void dumpEntry(Entry *E, int NumSpaces = 0) const {
947  StringRef Name = E->getName();
948  for (int i = 0, e = NumSpaces; i < e; ++i)
949  dbgs() << " ";
950  dbgs() << "'" << Name.str().c_str() << "'" << "\n";
951 
952  if (E->getKind() == EK_Directory) {
953  auto *DE = dyn_cast<RedirectingDirectoryEntry>(E);
954  assert(DE && "Should be a directory");
955 
956  for (std::unique_ptr<Entry> &SubEntry :
957  llvm::make_range(DE->contents_begin(), DE->contents_end()))
958  dumpEntry(SubEntry.get(), NumSpaces+2);
959  }
960  }
961 #endif
962 
963 };
964 
965 /// \brief A helper class to hold the common YAML parsing state.
966 class RedirectingFileSystemParser {
967  yaml::Stream &Stream;
968 
969  void error(yaml::Node *N, const Twine &Msg) {
970  Stream.printError(N, Msg);
971  }
972 
973  // false on error
974  bool parseScalarString(yaml::Node *N, StringRef &Result,
975  SmallVectorImpl<char> &Storage) {
976  yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
977  if (!S) {
978  error(N, "expected string");
979  return false;
980  }
981  Result = S->getValue(Storage);
982  return true;
983  }
984 
985  // false on error
986  bool parseScalarBool(yaml::Node *N, bool &Result) {
987  SmallString<5> Storage;
988  StringRef Value;
989  if (!parseScalarString(N, Value, Storage))
990  return false;
991 
992  if (Value.equals_lower("true") || Value.equals_lower("on") ||
993  Value.equals_lower("yes") || Value == "1") {
994  Result = true;
995  return true;
996  } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
997  Value.equals_lower("no") || Value == "0") {
998  Result = false;
999  return true;
1000  }
1001 
1002  error(N, "expected boolean value");
1003  return false;
1004  }
1005 
1006  struct KeyStatus {
1007  KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
1008  bool Required;
1009  bool Seen;
1010  };
1011  typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
1012 
1013  // false on error
1014  bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
1015  DenseMap<StringRef, KeyStatus> &Keys) {
1016  if (!Keys.count(Key)) {
1017  error(KeyNode, "unknown key");
1018  return false;
1019  }
1020  KeyStatus &S = Keys[Key];
1021  if (S.Seen) {
1022  error(KeyNode, Twine("duplicate key '") + Key + "'");
1023  return false;
1024  }
1025  S.Seen = true;
1026  return true;
1027  }
1028 
1029  // false on error
1030  bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
1031  for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
1032  E = Keys.end();
1033  I != E; ++I) {
1034  if (I->second.Required && !I->second.Seen) {
1035  error(Obj, Twine("missing key '") + I->first + "'");
1036  return false;
1037  }
1038  }
1039  return true;
1040  }
1041 
1042  Entry *lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,
1043  Entry *ParentEntry = nullptr) {
1044  if (!ParentEntry) { // Look for a existent root
1045  for (const std::unique_ptr<Entry> &Root : FS->Roots) {
1046  if (Name.equals(Root->getName())) {
1047  ParentEntry = Root.get();
1048  return ParentEntry;
1049  }
1050  }
1051  } else { // Advance to the next component
1052  auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry);
1053  for (std::unique_ptr<Entry> &Content :
1054  llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1055  auto *DirContent = dyn_cast<RedirectingDirectoryEntry>(Content.get());
1056  if (DirContent && Name.equals(Content->getName()))
1057  return DirContent;
1058  }
1059  }
1060 
1061  // ... or create a new one
1062  std::unique_ptr<Entry> E = llvm::make_unique<RedirectingDirectoryEntry>(
1063  Name,
1064  Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1065  0, 0, 0, file_type::directory_file, sys::fs::all_all));
1066 
1067  if (!ParentEntry) { // Add a new root to the overlay
1068  FS->Roots.push_back(std::move(E));
1069  ParentEntry = FS->Roots.back().get();
1070  return ParentEntry;
1071  }
1072 
1073  auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry);
1074  DE->addContent(std::move(E));
1075  return DE->getLastContent();
1076  }
1077 
1078  void uniqueOverlayTree(RedirectingFileSystem *FS, Entry *SrcE,
1079  Entry *NewParentE = nullptr) {
1080  StringRef Name = SrcE->getName();
1081  switch (SrcE->getKind()) {
1082  case EK_Directory: {
1083  auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE);
1084  assert(DE && "Must be a directory");
1085  // Empty directories could be present in the YAML as a way to
1086  // describe a file for a current directory after some of its subdir
1087  // is parsed. This only leads to redundant walks, ignore it.
1088  if (!Name.empty())
1089  NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
1090  for (std::unique_ptr<Entry> &SubEntry :
1091  llvm::make_range(DE->contents_begin(), DE->contents_end()))
1092  uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
1093  break;
1094  }
1095  case EK_File: {
1096  auto *FE = dyn_cast<RedirectingFileEntry>(SrcE);
1097  assert(FE && "Must be a file");
1098  assert(NewParentE && "Parent entry must exist");
1099  auto *DE = dyn_cast<RedirectingDirectoryEntry>(NewParentE);
1100  DE->addContent(llvm::make_unique<RedirectingFileEntry>(
1101  Name, FE->getExternalContentsPath(), FE->getUseName()));
1102  break;
1103  }
1104  }
1105  }
1106 
1107  std::unique_ptr<Entry> parseEntry(yaml::Node *N, RedirectingFileSystem *FS) {
1108  yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
1109  if (!M) {
1110  error(N, "expected mapping node for file or directory entry");
1111  return nullptr;
1112  }
1113 
1114  KeyStatusPair Fields[] = {
1115  KeyStatusPair("name", true),
1116  KeyStatusPair("type", true),
1117  KeyStatusPair("contents", false),
1118  KeyStatusPair("external-contents", false),
1119  KeyStatusPair("use-external-name", false),
1120  };
1121 
1122  DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1123 
1124  bool HasContents = false; // external or otherwise
1125  std::vector<std::unique_ptr<Entry>> EntryArrayContents;
1126  std::string ExternalContentsPath;
1127  std::string Name;
1128  auto UseExternalName = RedirectingFileEntry::NK_NotSet;
1129  EntryKind Kind;
1130 
1131  for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
1132  ++I) {
1133  StringRef Key;
1134  // Reuse the buffer for key and value, since we don't look at key after
1135  // parsing value.
1136  SmallString<256> Buffer;
1137  if (!parseScalarString(I->getKey(), Key, Buffer))
1138  return nullptr;
1139 
1140  if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
1141  return nullptr;
1142 
1143  StringRef Value;
1144  if (Key == "name") {
1145  if (!parseScalarString(I->getValue(), Value, Buffer))
1146  return nullptr;
1147 
1148  if (FS->UseCanonicalizedPaths) {
1149  SmallString<256> Path(Value);
1150  // Guarantee that old YAML files containing paths with ".." and "."
1151  // are properly canonicalized before read into the VFS.
1152  Path = sys::path::remove_leading_dotslash(Path);
1153  sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1154  Name = Path.str();
1155  } else {
1156  Name = Value;
1157  }
1158  } else if (Key == "type") {
1159  if (!parseScalarString(I->getValue(), Value, Buffer))
1160  return nullptr;
1161  if (Value == "file")
1162  Kind = EK_File;
1163  else if (Value == "directory")
1164  Kind = EK_Directory;
1165  else {
1166  error(I->getValue(), "unknown value for 'type'");
1167  return nullptr;
1168  }
1169  } else if (Key == "contents") {
1170  if (HasContents) {
1171  error(I->getKey(),
1172  "entry already has 'contents' or 'external-contents'");
1173  return nullptr;
1174  }
1175  HasContents = true;
1176  yaml::SequenceNode *Contents =
1177  dyn_cast<yaml::SequenceNode>(I->getValue());
1178  if (!Contents) {
1179  // FIXME: this is only for directories, what about files?
1180  error(I->getValue(), "expected array");
1181  return nullptr;
1182  }
1183 
1184  for (yaml::SequenceNode::iterator I = Contents->begin(),
1185  E = Contents->end();
1186  I != E; ++I) {
1187  if (std::unique_ptr<Entry> E = parseEntry(&*I, FS))
1188  EntryArrayContents.push_back(std::move(E));
1189  else
1190  return nullptr;
1191  }
1192  } else if (Key == "external-contents") {
1193  if (HasContents) {
1194  error(I->getKey(),
1195  "entry already has 'contents' or 'external-contents'");
1196  return nullptr;
1197  }
1198  HasContents = true;
1199  if (!parseScalarString(I->getValue(), Value, Buffer))
1200  return nullptr;
1201 
1202  SmallString<256> FullPath;
1203  if (FS->IsRelativeOverlay) {
1204  FullPath = FS->getExternalContentsPrefixDir();
1205  assert(!FullPath.empty() &&
1206  "External contents prefix directory must exist");
1207  llvm::sys::path::append(FullPath, Value);
1208  } else {
1209  FullPath = Value;
1210  }
1211 
1212  if (FS->UseCanonicalizedPaths) {
1213  // Guarantee that old YAML files containing paths with ".." and "."
1214  // are properly canonicalized before read into the VFS.
1215  FullPath = sys::path::remove_leading_dotslash(FullPath);
1216  sys::path::remove_dots(FullPath, /*remove_dot_dot=*/true);
1217  }
1218  ExternalContentsPath = FullPath.str();
1219  } else if (Key == "use-external-name") {
1220  bool Val;
1221  if (!parseScalarBool(I->getValue(), Val))
1222  return nullptr;
1223  UseExternalName = Val ? RedirectingFileEntry::NK_External
1224  : RedirectingFileEntry::NK_Virtual;
1225  } else {
1226  llvm_unreachable("key missing from Keys");
1227  }
1228  }
1229 
1230  if (Stream.failed())
1231  return nullptr;
1232 
1233  // check for missing keys
1234  if (!HasContents) {
1235  error(N, "missing key 'contents' or 'external-contents'");
1236  return nullptr;
1237  }
1238  if (!checkMissingKeys(N, Keys))
1239  return nullptr;
1240 
1241  // check invalid configuration
1242  if (Kind == EK_Directory &&
1243  UseExternalName != RedirectingFileEntry::NK_NotSet) {
1244  error(N, "'use-external-name' is not supported for directories");
1245  return nullptr;
1246  }
1247 
1248  // Remove trailing slash(es), being careful not to remove the root path
1249  StringRef Trimmed(Name);
1250  size_t RootPathLen = sys::path::root_path(Trimmed).size();
1251  while (Trimmed.size() > RootPathLen &&
1252  sys::path::is_separator(Trimmed.back()))
1253  Trimmed = Trimmed.slice(0, Trimmed.size()-1);
1254  // Get the last component
1255  StringRef LastComponent = sys::path::filename(Trimmed);
1256 
1257  std::unique_ptr<Entry> Result;
1258  switch (Kind) {
1259  case EK_File:
1260  Result = llvm::make_unique<RedirectingFileEntry>(
1261  LastComponent, std::move(ExternalContentsPath), UseExternalName);
1262  break;
1263  case EK_Directory:
1264  Result = llvm::make_unique<RedirectingDirectoryEntry>(
1265  LastComponent, std::move(EntryArrayContents),
1266  Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1267  0, 0, 0, file_type::directory_file, sys::fs::all_all));
1268  break;
1269  }
1270 
1271  StringRef Parent = sys::path::parent_path(Trimmed);
1272  if (Parent.empty())
1273  return Result;
1274 
1275  // if 'name' contains multiple components, create implicit directory entries
1276  for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
1277  E = sys::path::rend(Parent);
1278  I != E; ++I) {
1279  std::vector<std::unique_ptr<Entry>> Entries;
1280  Entries.push_back(std::move(Result));
1281  Result = llvm::make_unique<RedirectingDirectoryEntry>(
1282  *I, std::move(Entries),
1283  Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1284  0, 0, 0, file_type::directory_file, sys::fs::all_all));
1285  }
1286  return Result;
1287  }
1288 
1289 public:
1290  RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
1291 
1292  // false on error
1293  bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
1294  yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
1295  if (!Top) {
1296  error(Root, "expected mapping node");
1297  return false;
1298  }
1299 
1300  KeyStatusPair Fields[] = {
1301  KeyStatusPair("version", true),
1302  KeyStatusPair("case-sensitive", false),
1303  KeyStatusPair("use-external-names", false),
1304  KeyStatusPair("overlay-relative", false),
1305  KeyStatusPair("ignore-non-existent-contents", false),
1306  KeyStatusPair("roots", true),
1307  };
1308 
1309  DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1310  std::vector<std::unique_ptr<Entry>> RootEntries;
1311 
1312  // Parse configuration and 'roots'
1313  for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
1314  ++I) {
1315  SmallString<10> KeyBuffer;
1316  StringRef Key;
1317  if (!parseScalarString(I->getKey(), Key, KeyBuffer))
1318  return false;
1319 
1320  if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
1321  return false;
1322 
1323  if (Key == "roots") {
1324  yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
1325  if (!Roots) {
1326  error(I->getValue(), "expected array");
1327  return false;
1328  }
1329 
1330  for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
1331  I != E; ++I) {
1332  if (std::unique_ptr<Entry> E = parseEntry(&*I, FS))
1333  RootEntries.push_back(std::move(E));
1334  else
1335  return false;
1336  }
1337  } else if (Key == "version") {
1338  StringRef VersionString;
1339  SmallString<4> Storage;
1340  if (!parseScalarString(I->getValue(), VersionString, Storage))
1341  return false;
1342  int Version;
1343  if (VersionString.getAsInteger<int>(10, Version)) {
1344  error(I->getValue(), "expected integer");
1345  return false;
1346  }
1347  if (Version < 0) {
1348  error(I->getValue(), "invalid version number");
1349  return false;
1350  }
1351  if (Version != 0) {
1352  error(I->getValue(), "version mismatch, expected 0");
1353  return false;
1354  }
1355  } else if (Key == "case-sensitive") {
1356  if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
1357  return false;
1358  } else if (Key == "overlay-relative") {
1359  if (!parseScalarBool(I->getValue(), FS->IsRelativeOverlay))
1360  return false;
1361  } else if (Key == "use-external-names") {
1362  if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
1363  return false;
1364  } else if (Key == "ignore-non-existent-contents") {
1365  if (!parseScalarBool(I->getValue(), FS->IgnoreNonExistentContents))
1366  return false;
1367  } else {
1368  llvm_unreachable("key missing from Keys");
1369  }
1370  }
1371 
1372  if (Stream.failed())
1373  return false;
1374 
1375  if (!checkMissingKeys(Top, Keys))
1376  return false;
1377 
1378  // Now that we sucessefully parsed the YAML file, canonicalize the internal
1379  // representation to a proper directory tree so that we can search faster
1380  // inside the VFS.
1381  for (std::unique_ptr<Entry> &E : RootEntries)
1382  uniqueOverlayTree(FS, E.get());
1383 
1384  return true;
1385  }
1386 };
1387 } // end of anonymous namespace
1388 
1389 Entry::~Entry() = default;
1390 
1391 RedirectingFileSystem *
1392 RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
1393  SourceMgr::DiagHandlerTy DiagHandler,
1394  StringRef YAMLFilePath, void *DiagContext,
1395  IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1396 
1397  SourceMgr SM;
1398  yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
1399 
1400  SM.setDiagHandler(DiagHandler, DiagContext);
1401  yaml::document_iterator DI = Stream.begin();
1402  yaml::Node *Root = DI->getRoot();
1403  if (DI == Stream.end() || !Root) {
1404  SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
1405  return nullptr;
1406  }
1407 
1408  RedirectingFileSystemParser P(Stream);
1409 
1410  std::unique_ptr<RedirectingFileSystem> FS(
1411  new RedirectingFileSystem(std::move(ExternalFS)));
1412 
1413  if (!YAMLFilePath.empty()) {
1414  // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
1415  // to each 'external-contents' path.
1416  //
1417  // Example:
1418  // -ivfsoverlay dummy.cache/vfs/vfs.yaml
1419  // yields:
1420  // FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs
1421  //
1422  SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
1423  std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir);
1424  assert(!EC && "Overlay dir final path must be absolute");
1425  (void)EC;
1426  FS->setExternalContentsPrefixDir(OverlayAbsDir);
1427  }
1428 
1429  if (!P.parse(Root, FS.get()))
1430  return nullptr;
1431 
1432  return FS.release();
1433 }
1434 
1435 ErrorOr<Entry *> RedirectingFileSystem::lookupPath(const Twine &Path_) {
1436  SmallString<256> Path;
1437  Path_.toVector(Path);
1438 
1439  // Handle relative paths
1440  if (std::error_code EC = makeAbsolute(Path))
1441  return EC;
1442 
1443  // Canonicalize path by removing ".", "..", "./", etc components. This is
1444  // a VFS request, do bot bother about symlinks in the path components
1445  // but canonicalize in order to perform the correct entry search.
1446  if (UseCanonicalizedPaths) {
1447  Path = sys::path::remove_leading_dotslash(Path);
1448  sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1449  }
1450 
1451  if (Path.empty())
1452  return make_error_code(llvm::errc::invalid_argument);
1453 
1454  sys::path::const_iterator Start = sys::path::begin(Path);
1455  sys::path::const_iterator End = sys::path::end(Path);
1456  for (const std::unique_ptr<Entry> &Root : Roots) {
1457  ErrorOr<Entry *> Result = lookupPath(Start, End, Root.get());
1458  if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
1459  return Result;
1460  }
1461  return make_error_code(llvm::errc::no_such_file_or_directory);
1462 }
1463 
1464 ErrorOr<Entry *>
1465 RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
1466  sys::path::const_iterator End, Entry *From) {
1467 #ifndef LLVM_ON_WIN32
1468  assert(!isTraversalComponent(*Start) &&
1469  !isTraversalComponent(From->getName()) &&
1470  "Paths should not contain traversal components");
1471 #else
1472  // FIXME: this is here to support windows, remove it once canonicalized
1473  // paths become globally default.
1474  if (Start->equals("."))
1475  ++Start;
1476 #endif
1477 
1478  StringRef FromName = From->getName();
1479 
1480  // Forward the search to the next component in case this is an empty one.
1481  if (!FromName.empty()) {
1482  if (CaseSensitive ? !Start->equals(FromName)
1483  : !Start->equals_lower(FromName))
1484  // failure to match
1485  return make_error_code(llvm::errc::no_such_file_or_directory);
1486 
1487  ++Start;
1488 
1489  if (Start == End) {
1490  // Match!
1491  return From;
1492  }
1493  }
1494 
1495  auto *DE = dyn_cast<RedirectingDirectoryEntry>(From);
1496  if (!DE)
1497  return make_error_code(llvm::errc::not_a_directory);
1498 
1499  for (const std::unique_ptr<Entry> &DirEntry :
1500  llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1501  ErrorOr<Entry *> Result = lookupPath(Start, End, DirEntry.get());
1502  if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
1503  return Result;
1504  }
1505  return make_error_code(llvm::errc::no_such_file_or_directory);
1506 }
1507 
1508 static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
1509  Status ExternalStatus) {
1510  Status S = ExternalStatus;
1511  if (!UseExternalNames)
1512  S = Status::copyWithNewName(S, Path.str());
1513  S.IsVFSMapped = true;
1514  return S;
1515 }
1516 
1517 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) {
1518  assert(E != nullptr);
1519  if (auto *F = dyn_cast<RedirectingFileEntry>(E)) {
1520  ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
1521  assert(!S || S->getName() == F->getExternalContentsPath());
1522  if (S)
1523  return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
1524  *S);
1525  return S;
1526  } else { // directory
1527  auto *DE = cast<RedirectingDirectoryEntry>(E);
1528  return Status::copyWithNewName(DE->getStatus(), Path.str());
1529  }
1530 }
1531 
1532 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
1533  ErrorOr<Entry *> Result = lookupPath(Path);
1534  if (!Result)
1535  return Result.getError();
1536  return status(Path, *Result);
1537 }
1538 
1539 namespace {
1540 /// Provide a file wrapper with an overriden status.
1541 class FileWithFixedStatus : public File {
1542  std::unique_ptr<File> InnerFile;
1543  Status S;
1544 
1545 public:
1546  FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
1547  : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
1548 
1549  ErrorOr<Status> status() override { return S; }
1550  ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
1551  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
1552  bool IsVolatile) override {
1553  return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
1554  IsVolatile);
1555  }
1556  std::error_code close() override { return InnerFile->close(); }
1557 };
1558 } // end anonymous namespace
1559 
1560 ErrorOr<std::unique_ptr<File>>
1561 RedirectingFileSystem::openFileForRead(const Twine &Path) {
1562  ErrorOr<Entry *> E = lookupPath(Path);
1563  if (!E)
1564  return E.getError();
1565 
1566  auto *F = dyn_cast<RedirectingFileEntry>(*E);
1567  if (!F) // FIXME: errc::not_a_file?
1568  return make_error_code(llvm::errc::invalid_argument);
1569 
1570  auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
1571  if (!Result)
1572  return Result;
1573 
1574  auto ExternalStatus = (*Result)->status();
1575  if (!ExternalStatus)
1576  return ExternalStatus.getError();
1577 
1578  // FIXME: Update the status with the name and VFSMapped.
1579  Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
1580  *ExternalStatus);
1581  return std::unique_ptr<File>(
1582  llvm::make_unique<FileWithFixedStatus>(std::move(*Result), S));
1583 }
1584 
1586 vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1587  SourceMgr::DiagHandlerTy DiagHandler,
1588  StringRef YAMLFilePath,
1589  void *DiagContext,
1590  IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1591  return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
1592  YAMLFilePath, DiagContext,
1593  std::move(ExternalFS));
1594 }
1595 
1596 static void getVFSEntries(Entry *SrcE, SmallVectorImpl<StringRef> &Path,
1597  SmallVectorImpl<YAMLVFSEntry> &Entries) {
1598  auto Kind = SrcE->getKind();
1599  if (Kind == EK_Directory) {
1600  auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE);
1601  assert(DE && "Must be a directory");
1602  for (std::unique_ptr<Entry> &SubEntry :
1603  llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1604  Path.push_back(SubEntry->getName());
1605  getVFSEntries(SubEntry.get(), Path, Entries);
1606  Path.pop_back();
1607  }
1608  return;
1609  }
1610 
1611  assert(Kind == EK_File && "Must be a EK_File");
1612  auto *FE = dyn_cast<RedirectingFileEntry>(SrcE);
1613  assert(FE && "Must be a file");
1614  SmallString<128> VPath;
1615  for (auto &Comp : Path)
1616  llvm::sys::path::append(VPath, Comp);
1617  Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
1618 }
1619 
1620 void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1621  SourceMgr::DiagHandlerTy DiagHandler,
1622  StringRef YAMLFilePath,
1623  SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
1624  void *DiagContext,
1625  IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1626  RedirectingFileSystem *VFS = RedirectingFileSystem::create(
1627  std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
1628  std::move(ExternalFS));
1629  ErrorOr<Entry *> RootE = VFS->lookupPath("/");
1630  if (!RootE)
1631  return;
1632  SmallVector<StringRef, 8> Components;
1633  Components.push_back("/");
1634  getVFSEntries(*RootE, Components, CollectedEntries);
1635 }
1636 
1638  static std::atomic<unsigned> UID;
1639  unsigned ID = ++UID;
1640  // The following assumes that uint64_t max will never collide with a real
1641  // dev_t value from the OS.
1642  return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
1643 }
1644 
1645 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1646  assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1647  assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1648  assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1649  Mappings.emplace_back(VirtualPath, RealPath);
1650 }
1651 
1652 namespace {
1653 class JSONWriter {
1654  llvm::raw_ostream &OS;
1655  SmallVector<StringRef, 16> DirStack;
1656  inline unsigned getDirIndent() { return 4 * DirStack.size(); }
1657  inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1658  bool containedIn(StringRef Parent, StringRef Path);
1659  StringRef containedPart(StringRef Parent, StringRef Path);
1660  void startDirectory(StringRef Path);
1661  void endDirectory();
1662  void writeEntry(StringRef VPath, StringRef RPath);
1663 
1664 public:
1665  JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1666  void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> UseExternalNames,
1667  Optional<bool> IsCaseSensitive, Optional<bool> IsOverlayRelative,
1668  Optional<bool> IgnoreNonExistentContents, StringRef OverlayDir);
1669 };
1670 }
1671 
1672 bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
1673  using namespace llvm::sys;
1674  // Compare each path component.
1675  auto IParent = path::begin(Parent), EParent = path::end(Parent);
1676  for (auto IChild = path::begin(Path), EChild = path::end(Path);
1677  IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
1678  if (*IParent != *IChild)
1679  return false;
1680  }
1681  // Have we exhausted the parent path?
1682  return IParent == EParent;
1683 }
1684 
1685 StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1686  assert(!Parent.empty());
1687  assert(containedIn(Parent, Path));
1688  return Path.slice(Parent.size() + 1, StringRef::npos);
1689 }
1690 
1691 void JSONWriter::startDirectory(StringRef Path) {
1692  StringRef Name =
1693  DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
1694  DirStack.push_back(Path);
1695  unsigned Indent = getDirIndent();
1696  OS.indent(Indent) << "{\n";
1697  OS.indent(Indent + 2) << "'type': 'directory',\n";
1698  OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1699  OS.indent(Indent + 2) << "'contents': [\n";
1700 }
1701 
1702 void JSONWriter::endDirectory() {
1703  unsigned Indent = getDirIndent();
1704  OS.indent(Indent + 2) << "]\n";
1705  OS.indent(Indent) << "}";
1706 
1707  DirStack.pop_back();
1708 }
1709 
1710 void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1711  unsigned Indent = getFileIndent();
1712  OS.indent(Indent) << "{\n";
1713  OS.indent(Indent + 2) << "'type': 'file',\n";
1714  OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1715  OS.indent(Indent + 2) << "'external-contents': \""
1716  << llvm::yaml::escape(RPath) << "\"\n";
1717  OS.indent(Indent) << "}";
1718 }
1719 
1720 void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1721  Optional<bool> UseExternalNames,
1722  Optional<bool> IsCaseSensitive,
1723  Optional<bool> IsOverlayRelative,
1724  Optional<bool> IgnoreNonExistentContents,
1725  StringRef OverlayDir) {
1726  using namespace llvm::sys;
1727 
1728  OS << "{\n"
1729  " 'version': 0,\n";
1730  if (IsCaseSensitive.hasValue())
1731  OS << " 'case-sensitive': '"
1732  << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
1733  if (UseExternalNames.hasValue())
1734  OS << " 'use-external-names': '"
1735  << (UseExternalNames.getValue() ? "true" : "false") << "',\n";
1736  bool UseOverlayRelative = false;
1737  if (IsOverlayRelative.hasValue()) {
1738  UseOverlayRelative = IsOverlayRelative.getValue();
1739  OS << " 'overlay-relative': '"
1740  << (UseOverlayRelative ? "true" : "false") << "',\n";
1741  }
1742  if (IgnoreNonExistentContents.hasValue())
1743  OS << " 'ignore-non-existent-contents': '"
1744  << (IgnoreNonExistentContents.getValue() ? "true" : "false") << "',\n";
1745  OS << " 'roots': [\n";
1746 
1747  if (!Entries.empty()) {
1748  const YAMLVFSEntry &Entry = Entries.front();
1749  startDirectory(path::parent_path(Entry.VPath));
1750 
1751  StringRef RPath = Entry.RPath;
1752  if (UseOverlayRelative) {
1753  unsigned OverlayDirLen = OverlayDir.size();
1754  assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
1755  "Overlay dir must be contained in RPath");
1756  RPath = RPath.slice(OverlayDirLen, RPath.size());
1757  }
1758 
1759  writeEntry(path::filename(Entry.VPath), RPath);
1760 
1761  for (const auto &Entry : Entries.slice(1)) {
1762  StringRef Dir = path::parent_path(Entry.VPath);
1763  if (Dir == DirStack.back())
1764  OS << ",\n";
1765  else {
1766  while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
1767  OS << "\n";
1768  endDirectory();
1769  }
1770  OS << ",\n";
1771  startDirectory(Dir);
1772  }
1773  StringRef RPath = Entry.RPath;
1774  if (UseOverlayRelative) {
1775  unsigned OverlayDirLen = OverlayDir.size();
1776  assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
1777  "Overlay dir must be contained in RPath");
1778  RPath = RPath.slice(OverlayDirLen, RPath.size());
1779  }
1780  writeEntry(path::filename(Entry.VPath), RPath);
1781  }
1782 
1783  while (!DirStack.empty()) {
1784  OS << "\n";
1785  endDirectory();
1786  }
1787  OS << "\n";
1788  }
1789 
1790  OS << " ]\n"
1791  << "}\n";
1792 }
1793 
1794 void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
1795  std::sort(Mappings.begin(), Mappings.end(),
1796  [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
1797  return LHS.VPath < RHS.VPath;
1798  });
1799 
1800  JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
1801  IsOverlayRelative, IgnoreNonExistentContents,
1802  OverlayDir);
1803 }
1804 
1805 VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(
1806  const Twine &_Path, RedirectingFileSystem &FS,
1807  RedirectingDirectoryEntry::iterator Begin,
1808  RedirectingDirectoryEntry::iterator End, std::error_code &EC)
1809  : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
1810  while (Current != End) {
1811  SmallString<128> PathStr(Dir);
1812  llvm::sys::path::append(PathStr, (*Current)->getName());
1813  llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
1814  if (S) {
1815  CurrentEntry = *S;
1816  return;
1817  }
1818  // Skip entries which do not map to a reliable external content.
1819  if (FS.ignoreNonExistentContents() &&
1820  S.getError() == llvm::errc::no_such_file_or_directory) {
1821  ++Current;
1822  continue;
1823  } else {
1824  EC = S.getError();
1825  break;
1826  }
1827  }
1828 }
1829 
1830 std::error_code VFSFromYamlDirIterImpl::increment() {
1831  assert(Current != End && "cannot iterate past end");
1832  while (++Current != End) {
1833  SmallString<128> PathStr(Dir);
1834  llvm::sys::path::append(PathStr, (*Current)->getName());
1835  llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
1836  if (!S) {
1837  // Skip entries which do not map to a reliable external content.
1838  if (FS.ignoreNonExistentContents() &&
1839  S.getError() == llvm::errc::no_such_file_or_directory) {
1840  continue;
1841  } else {
1842  return S.getError();
1843  }
1844  }
1845  CurrentEntry = *S;
1846  break;
1847  }
1848 
1849  if (Current == End)
1850  CurrentEntry = Status();
1851  return std::error_code();
1852 }
1853 
1855  const Twine &Path,
1856  std::error_code &EC)
1857  : FS(&FS_) {
1858  directory_iterator I = FS->dir_begin(Path, EC);
1859  if (I != directory_iterator()) {
1860  State = std::make_shared<IterState>();
1861  State->push(I);
1862  }
1863 }
1864 
1867  assert(FS && State && !State->empty() && "incrementing past end");
1868  assert(State->top()->isStatusKnown() && "non-canonical end iterator");
1870  if (State->top()->isDirectory()) {
1871  vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
1872  if (I != End) {
1873  State->push(I);
1874  return *this;
1875  }
1876  }
1877 
1878  while (!State->empty() && State->top().increment(EC) == End)
1879  State->pop();
1880 
1881  if (State->empty())
1882  State.reset(); // end iterator
1883 
1884  return *this;
1885 }
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.
DominatorTree GraphTraits specialization so the DominatorTree can be iterable by generic graph iterat...
Definition: Dominators.h:26
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 &#39;real&#39; file system, as seen by the operating system.
StringRef P
llvm::sys::fs::perms getPermissions() const
The base class of the type hierarchy.
Definition: Type.h:1300
InMemoryNode * getChild(StringRef Name)
uint32_t getUser() const
The virtual file system interface.
llvm::ErrorOr< std::string > getCurrentWorkingDirectory() const override
Get the working directory of this file system.
void write(llvm::raw_ostream &OS)
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.
Definition: Format.h:1821
bool isStatusKnown() const
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.
bool useNormalizedPaths() const
Return true if this file system normalizes . and .. in paths.
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.
std::error_code make_error_code(BuildPreambleError Error)
directory_iterator & increment(std::error_code &EC)
Equivalent to operator++, with an error code.
static void getVFSEntries(Entry *SrcE, SmallVectorImpl< StringRef > &Path, SmallVectorImpl< YAMLVFSEntry > &Entries)
void addFileMapping(StringRef VirtualPath, StringRef RealPath)
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.
The result of a status operation.
NodeId Parent
Definition: ASTDiff.cpp:192
SourceLocation End
The in memory file system is a tree of Nodes.
static Status copyWithNewName(const Status &In, StringRef NewName)
Get a copy of a Status with a different name.
iterator overlays_end()
Get an iterator pointing one-past the least recently added file system.
SourceLocation Begin
Represents an open file.
static bool pathHasTraversal(StringRef Path)
static bool isTraversalComponent(StringRef Component)
The result type of a method or function.
const SourceManager & SM
Definition: Format.cpp:1308
llvm::sys::fs::file_type getType() const
#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.
iterator overlays_begin()
Get an iterator pointing to the most recently added file system.
llvm::sys::TimePoint getLastModificationTime() const
std::error_code makeAbsolute(SmallVectorImpl< char > &Path) const
Make Path an absolute path.
recursive_directory_iterator()=default
Construct an &#39;end&#39; iterator.
InMemoryNode * addChild(StringRef Name, std::unique_ptr< InMemoryNode > Child)
ast_type_traits::DynTypedNode Node
Dataflow Directional Tag Classes.
uint64_t getSize() const
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::UniqueID getNextVirtualUniqueID()
Get a globally unique ID for a virtual file or directory.
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)
llvm::sys::fs::UniqueID getUniqueID() const
std::string toString(unsigned Indent) const override
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.
static Decl::Kind getKind(const Decl *D)
Definition: DeclBase.cpp:896
An interface for virtual file systems to provide an iterator over the (non-recursive) contents of a d...
uint32_t getGroup() const
InMemoryNodeKind getKind() const