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