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