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