clang  3.8.0
VirtualFileSystem.cpp
Go to the documentation of this file.
1 //===- VirtualFileSystem.cpp - Virtual File System Layer --------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 // This file implements the VirtualFileSystem interface.
10 //===----------------------------------------------------------------------===//
11 
14 #include "llvm/ADT/DenseMap.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/ADT/StringSet.h"
18 #include "llvm/ADT/iterator_range.h"
19 #include "llvm/Support/Errc.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/YAMLParser.h"
23 #include "llvm/Config/llvm-config.h"
24 #include <atomic>
25 #include <memory>
26 
27 // For chdir.
28 #ifdef LLVM_ON_WIN32
29 # include <direct.h>
30 #else
31 # include <unistd.h>
32 #endif
33 
34 using namespace clang;
35 using namespace clang::vfs;
36 using namespace llvm;
37 using llvm::sys::fs::file_status;
38 using llvm::sys::fs::file_type;
39 using llvm::sys::fs::perms;
40 using llvm::sys::fs::UniqueID;
41 
42 Status::Status(const file_status &Status)
43  : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
44  User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
45  Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false) {}
46 
47 Status::Status(StringRef Name, UniqueID UID, sys::TimeValue MTime,
48  uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
49  perms Perms)
50  : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
51  Type(Type), Perms(Perms), IsVFSMapped(false) {}
52 
53 Status Status::copyWithNewName(const Status &In, StringRef NewName) {
54  return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
55  In.getUser(), In.getGroup(), In.getSize(), In.getType(),
56  In.getPermissions());
57 }
58 
59 Status Status::copyWithNewName(const file_status &In, StringRef NewName) {
60  return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
61  In.getUser(), In.getGroup(), In.getSize(), In.type(),
62  In.permissions());
63 }
64 
65 bool Status::equivalent(const Status &Other) const {
66  return getUniqueID() == Other.getUniqueID();
67 }
68 bool Status::isDirectory() const {
69  return Type == file_type::directory_file;
70 }
71 bool Status::isRegularFile() const {
72  return Type == file_type::regular_file;
73 }
74 bool Status::isOther() const {
75  return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
76 }
77 bool Status::isSymlink() const {
78  return Type == file_type::symlink_file;
79 }
80 bool Status::isStatusKnown() const {
81  return Type != file_type::status_error;
82 }
83 bool Status::exists() const {
84  return isStatusKnown() && Type != file_type::file_not_found;
85 }
86 
88 
90 
91 ErrorOr<std::unique_ptr<MemoryBuffer>>
92 FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
93  bool RequiresNullTerminator, bool IsVolatile) {
94  auto F = openFileForRead(Name);
95  if (!F)
96  return F.getError();
97 
98  return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
99 }
100 
101 std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
102  auto WorkingDir = getCurrentWorkingDirectory();
103  if (!WorkingDir)
104  return WorkingDir.getError();
105 
106  return llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
107 }
108 
109 bool FileSystem::exists(const Twine &Path) {
110  auto Status = status(Path);
111  return Status && Status->exists();
112 }
113 
114 //===-----------------------------------------------------------------------===/
115 // RealFileSystem implementation
116 //===-----------------------------------------------------------------------===/
117 
118 namespace {
119 /// \brief Wrapper around a raw file descriptor.
120 class RealFile : public File {
121  int FD;
122  Status S;
123  friend class RealFileSystem;
124  RealFile(int FD, StringRef NewName)
125  : FD(FD), S(NewName, {}, {}, {}, {}, {},
126  llvm::sys::fs::file_type::status_error, {}) {
127  assert(FD >= 0 && "Invalid or inactive file descriptor");
128  }
129 
130 public:
131  ~RealFile() override;
132  ErrorOr<Status> status() override;
133  ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
134  int64_t FileSize,
135  bool RequiresNullTerminator,
136  bool IsVolatile) override;
137  std::error_code close() override;
138 };
139 } // end anonymous namespace
140 RealFile::~RealFile() { close(); }
141 
142 ErrorOr<Status> RealFile::status() {
143  assert(FD != -1 && "cannot stat closed file");
144  if (!S.isStatusKnown()) {
145  file_status RealStatus;
146  if (std::error_code EC = sys::fs::status(FD, RealStatus))
147  return EC;
148  S = Status::copyWithNewName(RealStatus, S.getName());
149  }
150  return S;
151 }
152 
153 ErrorOr<std::unique_ptr<MemoryBuffer>>
154 RealFile::getBuffer(const Twine &Name, int64_t FileSize,
155  bool RequiresNullTerminator, bool IsVolatile) {
156  assert(FD != -1 && "cannot get buffer for closed file");
157  return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
158  IsVolatile);
159 }
160 
161 // FIXME: This is terrible, we need this for ::close.
162 #if !defined(_MSC_VER) && !defined(__MINGW32__)
163 #include <unistd.h>
164 #include <sys/uio.h>
165 #else
166 #include <io.h>
167 #ifndef S_ISFIFO
168 #define S_ISFIFO(x) (0)
169 #endif
170 #endif
171 std::error_code RealFile::close() {
172  if (::close(FD))
173  return std::error_code(errno, std::generic_category());
174  FD = -1;
175  return std::error_code();
176 }
177 
178 namespace {
179 /// \brief The file system according to your operating system.
180 class RealFileSystem : public FileSystem {
181 public:
182  ErrorOr<Status> status(const Twine &Path) override;
183  ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
184  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
185 
186  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
187  std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
188 };
189 } // end anonymous namespace
190 
191 ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
192  sys::fs::file_status RealStatus;
193  if (std::error_code EC = sys::fs::status(Path, RealStatus))
194  return EC;
195  return Status::copyWithNewName(RealStatus, Path.str());
196 }
197 
198 ErrorOr<std::unique_ptr<File>>
199 RealFileSystem::openFileForRead(const Twine &Name) {
200  int FD;
201  if (std::error_code EC = sys::fs::openFileForRead(Name, FD))
202  return EC;
203  return std::unique_ptr<File>(new RealFile(FD, Name.str()));
204 }
205 
206 llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
207  SmallString<256> Dir;
208  if (std::error_code EC = llvm::sys::fs::current_path(Dir))
209  return EC;
210  return Dir.str().str();
211 }
212 
213 std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
214  // FIXME: chdir is thread hostile; on the other hand, creating the same
215  // behavior as chdir is complex: chdir resolves the path once, thus
216  // guaranteeing that all subsequent relative path operations work
217  // on the same path the original chdir resulted in. This makes a
218  // difference for example on network filesystems, where symlinks might be
219  // switched during runtime of the tool. Fixing this depends on having a
220  // file system abstraction that allows openat() style interactions.
221  SmallString<256> Storage;
222  StringRef Dir = Path.toNullTerminatedStringRef(Storage);
223  if (int Err = ::chdir(Dir.data()))
224  return std::error_code(Err, std::generic_category());
225  return std::error_code();
226 }
227 
229  static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
230  return FS;
231 }
232 
233 namespace {
234 class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
235  std::string Path;
236  llvm::sys::fs::directory_iterator Iter;
237 public:
238  RealFSDirIter(const Twine &_Path, std::error_code &EC)
239  : Path(_Path.str()), Iter(Path, EC) {
240  if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
241  llvm::sys::fs::file_status S;
242  EC = Iter->status(S);
243  if (!EC)
244  CurrentEntry = Status::copyWithNewName(S, Iter->path());
245  }
246  }
247 
248  std::error_code increment() override {
249  std::error_code EC;
250  Iter.increment(EC);
251  if (EC) {
252  return EC;
253  } else if (Iter == llvm::sys::fs::directory_iterator()) {
254  CurrentEntry = Status();
255  } else {
256  llvm::sys::fs::file_status S;
257  EC = Iter->status(S);
258  CurrentEntry = Status::copyWithNewName(S, Iter->path());
259  }
260  return EC;
261  }
262 };
263 }
264 
265 directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
266  std::error_code &EC) {
267  return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
268 }
269 
270 //===-----------------------------------------------------------------------===/
271 // OverlayFileSystem implementation
272 //===-----------------------------------------------------------------------===/
274  FSList.push_back(BaseFS);
275 }
276 
278  FSList.push_back(FS);
279  // Synchronize added file systems by duplicating the working directory from
280  // the first one in the list.
281  FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
282 }
283 
284 ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
285  // FIXME: handle symlinks that cross file systems
286  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
287  ErrorOr<Status> Status = (*I)->status(Path);
288  if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
289  return Status;
290  }
291  return make_error_code(llvm::errc::no_such_file_or_directory);
292 }
293 
294 ErrorOr<std::unique_ptr<File>>
295 OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
296  // FIXME: handle symlinks that cross file systems
297  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
298  auto Result = (*I)->openFileForRead(Path);
299  if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
300  return Result;
301  }
302  return make_error_code(llvm::errc::no_such_file_or_directory);
303 }
304 
305 llvm::ErrorOr<std::string>
307  // All file systems are synchronized, just take the first working directory.
308  return FSList.front()->getCurrentWorkingDirectory();
309 }
310 std::error_code
312  for (auto &FS : FSList)
313  if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
314  return EC;
315  return std::error_code();
316 }
317 
319 
320 namespace {
321 class OverlayFSDirIterImpl : public clang::vfs::detail::DirIterImpl {
322  OverlayFileSystem &Overlays;
323  std::string Path;
324  OverlayFileSystem::iterator CurrentFS;
325  directory_iterator CurrentDirIter;
326  llvm::StringSet<> SeenNames;
327 
328  std::error_code incrementFS() {
329  assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
330  ++CurrentFS;
331  for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
332  std::error_code EC;
333  CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
334  if (EC && EC != errc::no_such_file_or_directory)
335  return EC;
336  if (CurrentDirIter != directory_iterator())
337  break; // found
338  }
339  return std::error_code();
340  }
341 
342  std::error_code incrementDirIter(bool IsFirstTime) {
343  assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
344  "incrementing past end");
345  std::error_code EC;
346  if (!IsFirstTime)
347  CurrentDirIter.increment(EC);
348  if (!EC && CurrentDirIter == directory_iterator())
349  EC = incrementFS();
350  return EC;
351  }
352 
353  std::error_code incrementImpl(bool IsFirstTime) {
354  while (true) {
355  std::error_code EC = incrementDirIter(IsFirstTime);
356  if (EC || CurrentDirIter == directory_iterator()) {
357  CurrentEntry = Status();
358  return EC;
359  }
360  CurrentEntry = *CurrentDirIter;
361  StringRef Name = llvm::sys::path::filename(CurrentEntry.getName());
362  if (SeenNames.insert(Name).second)
363  return EC; // name not seen before
364  }
365  llvm_unreachable("returned above");
366  }
367 
368 public:
369  OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
370  std::error_code &EC)
371  : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
372  CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
373  EC = incrementImpl(true);
374  }
375 
376  std::error_code increment() override { return incrementImpl(false); }
377 };
378 } // end anonymous namespace
379 
381  std::error_code &EC) {
382  return directory_iterator(
383  std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
384 }
385 
386 namespace clang {
387 namespace vfs {
388 namespace detail {
389 
391 
392 /// The in memory file system is a tree of Nodes. Every node can either be a
393 /// file or a directory.
395  Status Stat;
397 
398 public:
400  : Stat(std::move(Stat)), Kind(Kind) {}
401  virtual ~InMemoryNode() {}
402  const Status &getStatus() const { return Stat; }
403  InMemoryNodeKind getKind() const { return Kind; }
404  virtual std::string toString(unsigned Indent) const = 0;
405 };
406 
407 namespace {
408 class InMemoryFile : public InMemoryNode {
409  std::unique_ptr<llvm::MemoryBuffer> Buffer;
410 
411 public:
412  InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
413  : InMemoryNode(std::move(Stat), IME_File), Buffer(std::move(Buffer)) {}
414 
415  llvm::MemoryBuffer *getBuffer() { return Buffer.get(); }
416  std::string toString(unsigned Indent) const override {
417  return (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
418  }
419  static bool classof(const InMemoryNode *N) {
420  return N->getKind() == IME_File;
421  }
422 };
423 
424 /// Adapt a InMemoryFile for VFS' File interface.
425 class InMemoryFileAdaptor : public File {
426  InMemoryFile &Node;
427 
428 public:
429  explicit InMemoryFileAdaptor(InMemoryFile &Node) : Node(Node) {}
430 
431  llvm::ErrorOr<Status> status() override { return Node.getStatus(); }
432  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
433  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
434  bool IsVolatile) override {
435  llvm::MemoryBuffer *Buf = Node.getBuffer();
436  return llvm::MemoryBuffer::getMemBuffer(
437  Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
438  }
439  std::error_code close() override { return std::error_code(); }
440 };
441 } // end anonymous namespace
442 
444  std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
445 
446 public:
448  : InMemoryNode(std::move(Stat), IME_Directory) {}
449  InMemoryNode *getChild(StringRef Name) {
450  auto I = Entries.find(Name);
451  if (I != Entries.end())
452  return I->second.get();
453  return nullptr;
454  }
455  InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
456  return Entries.insert(make_pair(Name, std::move(Child)))
457  .first->second.get();
458  }
459 
460  typedef decltype(Entries)::const_iterator const_iterator;
461  const_iterator begin() const { return Entries.begin(); }
462  const_iterator end() const { return Entries.end(); }
463 
464  std::string toString(unsigned Indent) const override {
465  std::string Result =
466  (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
467  for (const auto &Entry : Entries) {
468  Result += Entry.second->toString(Indent + 2);
469  }
470  return Result;
471  }
472  static bool classof(const InMemoryNode *N) {
473  return N->getKind() == IME_Directory;
474  }
475 };
476 }
477 
479  : Root(new detail::InMemoryDirectory(
480  Status("", getNextVirtualUniqueID(), llvm::sys::TimeValue::MinTime(),
481  0, 0, 0, llvm::sys::fs::file_type::directory_file,
482  llvm::sys::fs::perms::all_all))),
483  UseNormalizedPaths(UseNormalizedPaths) {}
484 
486 
487 std::string InMemoryFileSystem::toString() const {
488  return Root->toString(/*Indent=*/0);
489 }
490 
491 bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
492  std::unique_ptr<llvm::MemoryBuffer> Buffer) {
493  SmallString<128> Path;
494  P.toVector(Path);
495 
496  // Fix up relative paths. This just prepends the current working directory.
497  std::error_code EC = makeAbsolute(Path);
498  assert(!EC);
499  (void)EC;
500 
501  if (useNormalizedPaths())
502  llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
503 
504  if (Path.empty())
505  return false;
506 
507  detail::InMemoryDirectory *Dir = Root.get();
508  auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
509  while (true) {
510  StringRef Name = *I;
511  detail::InMemoryNode *Node = Dir->getChild(Name);
512  ++I;
513  if (!Node) {
514  if (I == E) {
515  // End of the path, create a new file.
516  // FIXME: expose the status details in the interface.
517  Status Stat(P.str(), getNextVirtualUniqueID(),
518  llvm::sys::TimeValue(ModificationTime, 0), 0, 0,
519  Buffer->getBufferSize(),
520  llvm::sys::fs::file_type::regular_file,
521  llvm::sys::fs::all_all);
522  Dir->addChild(Name, llvm::make_unique<detail::InMemoryFile>(
523  std::move(Stat), std::move(Buffer)));
524  return true;
525  }
526 
527  // Create a new directory. Use the path up to here.
528  // FIXME: expose the status details in the interface.
529  Status Stat(
530  StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
531  getNextVirtualUniqueID(), llvm::sys::TimeValue(ModificationTime, 0),
532  0, 0, Buffer->getBufferSize(),
533  llvm::sys::fs::file_type::directory_file, llvm::sys::fs::all_all);
534  Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
535  Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
536  continue;
537  }
538 
539  if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) {
540  Dir = NewDir;
541  } else {
542  assert(isa<detail::InMemoryFile>(Node) &&
543  "Must be either file or directory!");
544 
545  // Trying to insert a directory in place of a file.
546  if (I != E)
547  return false;
548 
549  // Return false only if the new file is different from the existing one.
550  return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
551  Buffer->getBuffer();
552  }
553  }
554 }
555 
556 bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
557  llvm::MemoryBuffer *Buffer) {
558  return addFile(P, ModificationTime,
559  llvm::MemoryBuffer::getMemBuffer(
560  Buffer->getBuffer(), Buffer->getBufferIdentifier()));
561 }
562 
563 static ErrorOr<detail::InMemoryNode *>
565  const Twine &P) {
566  SmallString<128> Path;
567  P.toVector(Path);
568 
569  // Fix up relative paths. This just prepends the current working directory.
570  std::error_code EC = FS.makeAbsolute(Path);
571  assert(!EC);
572  (void)EC;
573 
574  if (FS.useNormalizedPaths())
575  llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
576 
577  if (Path.empty())
578  return Dir;
579 
580  auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
581  while (true) {
583  ++I;
584  if (!Node)
585  return errc::no_such_file_or_directory;
586 
587  // Return the file if it's at the end of the path.
588  if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
589  if (I == E)
590  return File;
591  return errc::no_such_file_or_directory;
592  }
593 
594  // Traverse directories.
595  Dir = cast<detail::InMemoryDirectory>(Node);
596  if (I == E)
597  return Dir;
598  }
599 }
600 
601 llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
602  auto Node = lookupInMemoryNode(*this, Root.get(), Path);
603  if (Node)
604  return (*Node)->getStatus();
605  return Node.getError();
606 }
607 
608 llvm::ErrorOr<std::unique_ptr<File>>
610  auto Node = lookupInMemoryNode(*this, Root.get(), Path);
611  if (!Node)
612  return Node.getError();
613 
614  // When we have a file provide a heap-allocated wrapper for the memory buffer
615  // to match the ownership semantics for File.
616  if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
617  return std::unique_ptr<File>(new detail::InMemoryFileAdaptor(*F));
618 
619  // FIXME: errc::not_a_file?
620  return make_error_code(llvm::errc::invalid_argument);
621 }
622 
623 namespace {
624 /// Adaptor from InMemoryDir::iterator to directory_iterator.
625 class InMemoryDirIterator : public clang::vfs::detail::DirIterImpl {
628 
629 public:
630  InMemoryDirIterator() {}
631  explicit InMemoryDirIterator(detail::InMemoryDirectory &Dir)
632  : I(Dir.begin()), E(Dir.end()) {
633  if (I != E)
634  CurrentEntry = I->second->getStatus();
635  }
636 
637  std::error_code increment() override {
638  ++I;
639  // When we're at the end, make CurrentEntry invalid and DirIterImpl will do
640  // the rest.
641  CurrentEntry = I != E ? I->second->getStatus() : Status();
642  return std::error_code();
643  }
644 };
645 } // end anonymous namespace
646 
648  std::error_code &EC) {
649  auto Node = lookupInMemoryNode(*this, Root.get(), Dir);
650  if (!Node) {
651  EC = Node.getError();
652  return directory_iterator(std::make_shared<InMemoryDirIterator>());
653  }
654 
655  if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
656  return directory_iterator(std::make_shared<InMemoryDirIterator>(*DirNode));
657 
658  EC = make_error_code(llvm::errc::not_a_directory);
659  return directory_iterator(std::make_shared<InMemoryDirIterator>());
660 }
661 
662 std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) {
663  SmallString<128> Path;
664  P.toVector(Path);
665 
666  // Fix up relative paths. This just prepends the current working directory.
667  std::error_code EC = makeAbsolute(Path);
668  assert(!EC);
669  (void)EC;
670 
671  if (useNormalizedPaths())
672  llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
673 
674  if (!Path.empty())
675  WorkingDirectory = Path.str();
676  return std::error_code();
677 }
678 }
679 }
680 
681 //===-----------------------------------------------------------------------===/
682 // RedirectingFileSystem implementation
683 //===-----------------------------------------------------------------------===/
684 
685 namespace {
686 
687 enum EntryKind {
688  EK_Directory,
689  EK_File
690 };
691 
692 /// \brief A single file or directory in the VFS.
693 class Entry {
694  EntryKind Kind;
695  std::string Name;
696 
697 public:
698  virtual ~Entry();
699  Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
700  StringRef getName() const { return Name; }
701  EntryKind getKind() const { return Kind; }
702 };
703 
704 class RedirectingDirectoryEntry : public Entry {
705  std::vector<std::unique_ptr<Entry>> Contents;
706  Status S;
707 
708 public:
709  RedirectingDirectoryEntry(StringRef Name,
710  std::vector<std::unique_ptr<Entry>> Contents,
711  Status S)
712  : Entry(EK_Directory, Name), Contents(std::move(Contents)),
713  S(std::move(S)) {}
714  Status getStatus() { return S; }
715  typedef decltype(Contents)::iterator iterator;
716  iterator contents_begin() { return Contents.begin(); }
717  iterator contents_end() { return Contents.end(); }
718  static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
719 };
720 
721 class RedirectingFileEntry : public Entry {
722 public:
723  enum NameKind {
724  NK_NotSet,
725  NK_External,
726  NK_Virtual
727  };
728 private:
729  std::string ExternalContentsPath;
730  NameKind UseName;
731 public:
732  RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath,
733  NameKind UseName)
734  : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
735  UseName(UseName) {}
736  StringRef getExternalContentsPath() const { return ExternalContentsPath; }
737  /// \brief whether to use the external path as the name for this file.
738  bool useExternalName(bool GlobalUseExternalName) const {
739  return UseName == NK_NotSet ? GlobalUseExternalName
740  : (UseName == NK_External);
741  }
742  static bool classof(const Entry *E) { return E->getKind() == EK_File; }
743 };
744 
745 class RedirectingFileSystem;
746 
747 class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
748  std::string Dir;
749  RedirectingFileSystem &FS;
751 
752 public:
753  VFSFromYamlDirIterImpl(const Twine &Path, RedirectingFileSystem &FS,
756  std::error_code &EC);
757  std::error_code increment() override;
758 };
759 
760 /// \brief A virtual file system parsed from a YAML file.
761 ///
762 /// Currently, this class allows creating virtual directories and mapping
763 /// virtual file paths to existing external files, available in \c ExternalFS.
764 ///
765 /// The basic structure of the parsed file is:
766 /// \verbatim
767 /// {
768 /// 'version': <version number>,
769 /// <optional configuration>
770 /// 'roots': [
771 /// <directory entries>
772 /// ]
773 /// }
774 /// \endverbatim
775 ///
776 /// All configuration options are optional.
777 /// 'case-sensitive': <boolean, default=true>
778 /// 'use-external-names': <boolean, default=true>
779 ///
780 /// Virtual directories are represented as
781 /// \verbatim
782 /// {
783 /// 'type': 'directory',
784 /// 'name': <string>,
785 /// 'contents': [ <file or directory entries> ]
786 /// }
787 /// \endverbatim
788 ///
789 /// The default attributes for virtual directories are:
790 /// \verbatim
791 /// MTime = now() when created
792 /// Perms = 0777
793 /// User = Group = 0
794 /// Size = 0
795 /// UniqueID = unspecified unique value
796 /// \endverbatim
797 ///
798 /// Re-mapped files are represented as
799 /// \verbatim
800 /// {
801 /// 'type': 'file',
802 /// 'name': <string>,
803 /// 'use-external-name': <boolean> # Optional
804 /// 'external-contents': <path to external file>)
805 /// }
806 /// \endverbatim
807 ///
808 /// and inherit their attributes from the external contents.
809 ///
810 /// In both cases, the 'name' field may contain multiple path components (e.g.
811 /// /path/to/file). However, any directory that contains more than one child
812 /// must be uniquely represented by a directory entry.
813 class RedirectingFileSystem : public vfs::FileSystem {
814  /// The root(s) of the virtual file system.
815  std::vector<std::unique_ptr<Entry>> Roots;
816  /// \brief The file system to use for external references.
818 
819  /// @name Configuration
820  /// @{
821 
822  /// \brief Whether to perform case-sensitive comparisons.
823  ///
824  /// Currently, case-insensitive matching only works correctly with ASCII.
825  bool CaseSensitive;
826 
827  /// \brief Whether to use to use the value of 'external-contents' for the
828  /// names of files. This global value is overridable on a per-file basis.
829  bool UseExternalNames;
830  /// @}
831 
832  friend class RedirectingFileSystemParser;
833 
834 private:
835  RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)
836  : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
837 
838  /// \brief Looks up \p Path in \c Roots.
839  ErrorOr<Entry *> lookupPath(const Twine &Path);
840 
841  /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
842  /// recursing into the contents of \p From if it is a directory.
843  ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
844  sys::path::const_iterator End, Entry *From);
845 
846  /// \brief Get the status of a given an \c Entry.
847  ErrorOr<Status> status(const Twine &Path, Entry *E);
848 
849 public:
850  /// \brief Parses \p Buffer, which is expected to be in YAML format and
851  /// returns a virtual file system representing its contents.
852  static RedirectingFileSystem *
853  create(std::unique_ptr<MemoryBuffer> Buffer,
854  SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
855  IntrusiveRefCntPtr<FileSystem> ExternalFS);
856 
857  ErrorOr<Status> status(const Twine &Path) override;
858  ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
859 
860  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
861  return ExternalFS->getCurrentWorkingDirectory();
862  }
863  std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
864  return ExternalFS->setCurrentWorkingDirectory(Path);
865  }
866 
867  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
868  ErrorOr<Entry *> E = lookupPath(Dir);
869  if (!E) {
870  EC = E.getError();
871  return directory_iterator();
872  }
873  ErrorOr<Status> S = status(Dir, *E);
874  if (!S) {
875  EC = S.getError();
876  return directory_iterator();
877  }
878  if (!S->isDirectory()) {
879  EC = std::error_code(static_cast<int>(errc::not_a_directory),
880  std::system_category());
881  return directory_iterator();
882  }
883 
884  auto *D = cast<RedirectingDirectoryEntry>(*E);
885  return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
886  *this, D->contents_begin(), D->contents_end(), EC));
887  }
888 };
889 
890 /// \brief A helper class to hold the common YAML parsing state.
891 class RedirectingFileSystemParser {
892  yaml::Stream &Stream;
893 
894  void error(yaml::Node *N, const Twine &Msg) {
895  Stream.printError(N, Msg);
896  }
897 
898  // false on error
899  bool parseScalarString(yaml::Node *N, StringRef &Result,
900  SmallVectorImpl<char> &Storage) {
901  yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
902  if (!S) {
903  error(N, "expected string");
904  return false;
905  }
906  Result = S->getValue(Storage);
907  return true;
908  }
909 
910  // false on error
911  bool parseScalarBool(yaml::Node *N, bool &Result) {
912  SmallString<5> Storage;
913  StringRef Value;
914  if (!parseScalarString(N, Value, Storage))
915  return false;
916 
917  if (Value.equals_lower("true") || Value.equals_lower("on") ||
918  Value.equals_lower("yes") || Value == "1") {
919  Result = true;
920  return true;
921  } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
922  Value.equals_lower("no") || Value == "0") {
923  Result = false;
924  return true;
925  }
926 
927  error(N, "expected boolean value");
928  return false;
929  }
930 
931  struct KeyStatus {
932  KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
933  bool Required;
934  bool Seen;
935  };
936  typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
937 
938  // false on error
939  bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
940  DenseMap<StringRef, KeyStatus> &Keys) {
941  if (!Keys.count(Key)) {
942  error(KeyNode, "unknown key");
943  return false;
944  }
945  KeyStatus &S = Keys[Key];
946  if (S.Seen) {
947  error(KeyNode, Twine("duplicate key '") + Key + "'");
948  return false;
949  }
950  S.Seen = true;
951  return true;
952  }
953 
954  // false on error
955  bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
956  for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
957  E = Keys.end();
958  I != E; ++I) {
959  if (I->second.Required && !I->second.Seen) {
960  error(Obj, Twine("missing key '") + I->first + "'");
961  return false;
962  }
963  }
964  return true;
965  }
966 
967  std::unique_ptr<Entry> parseEntry(yaml::Node *N) {
968  yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
969  if (!M) {
970  error(N, "expected mapping node for file or directory entry");
971  return nullptr;
972  }
973 
974  KeyStatusPair Fields[] = {
975  KeyStatusPair("name", true),
976  KeyStatusPair("type", true),
977  KeyStatusPair("contents", false),
978  KeyStatusPair("external-contents", false),
979  KeyStatusPair("use-external-name", false),
980  };
981 
982  DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
983 
984  bool HasContents = false; // external or otherwise
985  std::vector<std::unique_ptr<Entry>> EntryArrayContents;
986  std::string ExternalContentsPath;
987  std::string Name;
988  auto UseExternalName = RedirectingFileEntry::NK_NotSet;
989  EntryKind Kind;
990 
991  for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
992  ++I) {
993  StringRef Key;
994  // Reuse the buffer for key and value, since we don't look at key after
995  // parsing value.
997  if (!parseScalarString(I->getKey(), Key, Buffer))
998  return nullptr;
999 
1000  if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
1001  return nullptr;
1002 
1003  StringRef Value;
1004  if (Key == "name") {
1005  if (!parseScalarString(I->getValue(), Value, Buffer))
1006  return nullptr;
1007  Name = Value;
1008  } else if (Key == "type") {
1009  if (!parseScalarString(I->getValue(), Value, Buffer))
1010  return nullptr;
1011  if (Value == "file")
1012  Kind = EK_File;
1013  else if (Value == "directory")
1014  Kind = EK_Directory;
1015  else {
1016  error(I->getValue(), "unknown value for 'type'");
1017  return nullptr;
1018  }
1019  } else if (Key == "contents") {
1020  if (HasContents) {
1021  error(I->getKey(),
1022  "entry already has 'contents' or 'external-contents'");
1023  return nullptr;
1024  }
1025  HasContents = true;
1026  yaml::SequenceNode *Contents =
1027  dyn_cast<yaml::SequenceNode>(I->getValue());
1028  if (!Contents) {
1029  // FIXME: this is only for directories, what about files?
1030  error(I->getValue(), "expected array");
1031  return nullptr;
1032  }
1033 
1034  for (yaml::SequenceNode::iterator I = Contents->begin(),
1035  E = Contents->end();
1036  I != E; ++I) {
1037  if (std::unique_ptr<Entry> E = parseEntry(&*I))
1038  EntryArrayContents.push_back(std::move(E));
1039  else
1040  return nullptr;
1041  }
1042  } else if (Key == "external-contents") {
1043  if (HasContents) {
1044  error(I->getKey(),
1045  "entry already has 'contents' or 'external-contents'");
1046  return nullptr;
1047  }
1048  HasContents = true;
1049  if (!parseScalarString(I->getValue(), Value, Buffer))
1050  return nullptr;
1051  ExternalContentsPath = Value;
1052  } else if (Key == "use-external-name") {
1053  bool Val;
1054  if (!parseScalarBool(I->getValue(), Val))
1055  return nullptr;
1056  UseExternalName = Val ? RedirectingFileEntry::NK_External
1057  : RedirectingFileEntry::NK_Virtual;
1058  } else {
1059  llvm_unreachable("key missing from Keys");
1060  }
1061  }
1062 
1063  if (Stream.failed())
1064  return nullptr;
1065 
1066  // check for missing keys
1067  if (!HasContents) {
1068  error(N, "missing key 'contents' or 'external-contents'");
1069  return nullptr;
1070  }
1071  if (!checkMissingKeys(N, Keys))
1072  return nullptr;
1073 
1074  // check invalid configuration
1075  if (Kind == EK_Directory &&
1076  UseExternalName != RedirectingFileEntry::NK_NotSet) {
1077  error(N, "'use-external-name' is not supported for directories");
1078  return nullptr;
1079  }
1080 
1081  // Remove trailing slash(es), being careful not to remove the root path
1082  StringRef Trimmed(Name);
1083  size_t RootPathLen = sys::path::root_path(Trimmed).size();
1084  while (Trimmed.size() > RootPathLen &&
1085  sys::path::is_separator(Trimmed.back()))
1086  Trimmed = Trimmed.slice(0, Trimmed.size()-1);
1087  // Get the last component
1088  StringRef LastComponent = sys::path::filename(Trimmed);
1089 
1090  std::unique_ptr<Entry> Result;
1091  switch (Kind) {
1092  case EK_File:
1093  Result = llvm::make_unique<RedirectingFileEntry>(
1094  LastComponent, std::move(ExternalContentsPath), UseExternalName);
1095  break;
1096  case EK_Directory:
1097  Result = llvm::make_unique<RedirectingDirectoryEntry>(
1098  LastComponent, std::move(EntryArrayContents),
1099  Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
1100  file_type::directory_file, sys::fs::all_all));
1101  break;
1102  }
1103 
1104  StringRef Parent = sys::path::parent_path(Trimmed);
1105  if (Parent.empty())
1106  return Result;
1107 
1108  // if 'name' contains multiple components, create implicit directory entries
1109  for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
1110  E = sys::path::rend(Parent);
1111  I != E; ++I) {
1112  std::vector<std::unique_ptr<Entry>> Entries;
1113  Entries.push_back(std::move(Result));
1114  Result = llvm::make_unique<RedirectingDirectoryEntry>(
1115  *I, std::move(Entries),
1116  Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0,
1117  file_type::directory_file, sys::fs::all_all));
1118  }
1119  return Result;
1120  }
1121 
1122 public:
1123  RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
1124 
1125  // false on error
1126  bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
1127  yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
1128  if (!Top) {
1129  error(Root, "expected mapping node");
1130  return false;
1131  }
1132 
1133  KeyStatusPair Fields[] = {
1134  KeyStatusPair("version", true),
1135  KeyStatusPair("case-sensitive", false),
1136  KeyStatusPair("use-external-names", false),
1137  KeyStatusPair("roots", true),
1138  };
1139 
1140  DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1141 
1142  // Parse configuration and 'roots'
1143  for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
1144  ++I) {
1145  SmallString<10> KeyBuffer;
1146  StringRef Key;
1147  if (!parseScalarString(I->getKey(), Key, KeyBuffer))
1148  return false;
1149 
1150  if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
1151  return false;
1152 
1153  if (Key == "roots") {
1154  yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
1155  if (!Roots) {
1156  error(I->getValue(), "expected array");
1157  return false;
1158  }
1159 
1160  for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
1161  I != E; ++I) {
1162  if (std::unique_ptr<Entry> E = parseEntry(&*I))
1163  FS->Roots.push_back(std::move(E));
1164  else
1165  return false;
1166  }
1167  } else if (Key == "version") {
1168  StringRef VersionString;
1169  SmallString<4> Storage;
1170  if (!parseScalarString(I->getValue(), VersionString, Storage))
1171  return false;
1172  int Version;
1173  if (VersionString.getAsInteger<int>(10, Version)) {
1174  error(I->getValue(), "expected integer");
1175  return false;
1176  }
1177  if (Version < 0) {
1178  error(I->getValue(), "invalid version number");
1179  return false;
1180  }
1181  if (Version != 0) {
1182  error(I->getValue(), "version mismatch, expected 0");
1183  return false;
1184  }
1185  } else if (Key == "case-sensitive") {
1186  if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
1187  return false;
1188  } else if (Key == "use-external-names") {
1189  if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
1190  return false;
1191  } else {
1192  llvm_unreachable("key missing from Keys");
1193  }
1194  }
1195 
1196  if (Stream.failed())
1197  return false;
1198 
1199  if (!checkMissingKeys(Top, Keys))
1200  return false;
1201  return true;
1202  }
1203 };
1204 } // end of anonymous namespace
1205 
1206 Entry::~Entry() = default;
1207 
1208 RedirectingFileSystem *RedirectingFileSystem::create(
1209  std::unique_ptr<MemoryBuffer> Buffer, SourceMgr::DiagHandlerTy DiagHandler,
1210  void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1211 
1212  SourceMgr SM;
1213  yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
1214 
1215  SM.setDiagHandler(DiagHandler, DiagContext);
1216  yaml::document_iterator DI = Stream.begin();
1217  yaml::Node *Root = DI->getRoot();
1218  if (DI == Stream.end() || !Root) {
1219  SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
1220  return nullptr;
1221  }
1222 
1223  RedirectingFileSystemParser P(Stream);
1224 
1225  std::unique_ptr<RedirectingFileSystem> FS(
1226  new RedirectingFileSystem(ExternalFS));
1227  if (!P.parse(Root, FS.get()))
1228  return nullptr;
1229 
1230  return FS.release();
1231 }
1232 
1233 ErrorOr<Entry *> RedirectingFileSystem::lookupPath(const Twine &Path_) {
1234  SmallString<256> Path;
1235  Path_.toVector(Path);
1236 
1237  // Handle relative paths
1238  if (std::error_code EC = makeAbsolute(Path))
1239  return EC;
1240 
1241  if (Path.empty())
1242  return make_error_code(llvm::errc::invalid_argument);
1243 
1244  sys::path::const_iterator Start = sys::path::begin(Path);
1245  sys::path::const_iterator End = sys::path::end(Path);
1246  for (const std::unique_ptr<Entry> &Root : Roots) {
1247  ErrorOr<Entry *> Result = lookupPath(Start, End, Root.get());
1248  if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
1249  return Result;
1250  }
1251  return make_error_code(llvm::errc::no_such_file_or_directory);
1252 }
1253 
1254 ErrorOr<Entry *>
1255 RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
1256  sys::path::const_iterator End, Entry *From) {
1257  if (Start->equals("."))
1258  ++Start;
1259 
1260  // FIXME: handle ..
1261  if (CaseSensitive ? !Start->equals(From->getName())
1262  : !Start->equals_lower(From->getName()))
1263  // failure to match
1264  return make_error_code(llvm::errc::no_such_file_or_directory);
1265 
1266  ++Start;
1267 
1268  if (Start == End) {
1269  // Match!
1270  return From;
1271  }
1272 
1273  auto *DE = dyn_cast<RedirectingDirectoryEntry>(From);
1274  if (!DE)
1275  return make_error_code(llvm::errc::not_a_directory);
1276 
1277  for (const std::unique_ptr<Entry> &DirEntry :
1278  llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1279  ErrorOr<Entry *> Result = lookupPath(Start, End, DirEntry.get());
1280  if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
1281  return Result;
1282  }
1283  return make_error_code(llvm::errc::no_such_file_or_directory);
1284 }
1285 
1286 static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
1287  Status ExternalStatus) {
1288  Status S = ExternalStatus;
1289  if (!UseExternalNames)
1290  S = Status::copyWithNewName(S, Path.str());
1291  S.IsVFSMapped = true;
1292  return S;
1293 }
1294 
1295 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) {
1296  assert(E != nullptr);
1297  if (auto *F = dyn_cast<RedirectingFileEntry>(E)) {
1298  ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
1299  assert(!S || S->getName() == F->getExternalContentsPath());
1300  if (S)
1301  return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
1302  *S);
1303  return S;
1304  } else { // directory
1305  auto *DE = cast<RedirectingDirectoryEntry>(E);
1306  return Status::copyWithNewName(DE->getStatus(), Path.str());
1307  }
1308 }
1309 
1310 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
1311  ErrorOr<Entry *> Result = lookupPath(Path);
1312  if (!Result)
1313  return Result.getError();
1314  return status(Path, *Result);
1315 }
1316 
1317 namespace {
1318 /// Provide a file wrapper with an overriden status.
1319 class FileWithFixedStatus : public File {
1320  std::unique_ptr<File> InnerFile;
1321  Status S;
1322 
1323 public:
1324  FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
1325  : InnerFile(std::move(InnerFile)), S(S) {}
1326 
1327  ErrorOr<Status> status() override { return S; }
1328  ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
1329  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
1330  bool IsVolatile) override {
1331  return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
1332  IsVolatile);
1333  }
1334  std::error_code close() override { return InnerFile->close(); }
1335 };
1336 } // end anonymous namespace
1337 
1338 ErrorOr<std::unique_ptr<File>>
1339 RedirectingFileSystem::openFileForRead(const Twine &Path) {
1340  ErrorOr<Entry *> E = lookupPath(Path);
1341  if (!E)
1342  return E.getError();
1343 
1344  auto *F = dyn_cast<RedirectingFileEntry>(*E);
1345  if (!F) // FIXME: errc::not_a_file?
1346  return make_error_code(llvm::errc::invalid_argument);
1347 
1348  auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
1349  if (!Result)
1350  return Result;
1351 
1352  auto ExternalStatus = (*Result)->status();
1353  if (!ExternalStatus)
1354  return ExternalStatus.getError();
1355 
1356  // FIXME: Update the status with the name and VFSMapped.
1357  Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
1358  *ExternalStatus);
1359  return std::unique_ptr<File>(
1360  llvm::make_unique<FileWithFixedStatus>(std::move(*Result), S));
1361 }
1362 
1364 vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1365  SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
1366  IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1367  return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
1368  DiagContext, ExternalFS);
1369 }
1370 
1372  static std::atomic<unsigned> UID;
1373  unsigned ID = ++UID;
1374  // The following assumes that uint64_t max will never collide with a real
1375  // dev_t value from the OS.
1376  return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
1377 }
1378 
1379 #ifndef NDEBUG
1380 static bool pathHasTraversal(StringRef Path) {
1381  using namespace llvm::sys;
1382  for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1383  if (Comp == "." || Comp == "..")
1384  return true;
1385  return false;
1386 }
1387 #endif
1388 
1389 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1390  assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1391  assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1392  assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1393  Mappings.emplace_back(VirtualPath, RealPath);
1394 }
1395 
1396 namespace {
1397 class JSONWriter {
1398  llvm::raw_ostream &OS;
1399  SmallVector<StringRef, 16> DirStack;
1400  inline unsigned getDirIndent() { return 4 * DirStack.size(); }
1401  inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1402  bool containedIn(StringRef Parent, StringRef Path);
1403  StringRef containedPart(StringRef Parent, StringRef Path);
1404  void startDirectory(StringRef Path);
1405  void endDirectory();
1406  void writeEntry(StringRef VPath, StringRef RPath);
1407 
1408 public:
1409  JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1410  void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> IsCaseSensitive);
1411 };
1412 }
1413 
1414 bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
1415  using namespace llvm::sys;
1416  // Compare each path component.
1417  auto IParent = path::begin(Parent), EParent = path::end(Parent);
1418  for (auto IChild = path::begin(Path), EChild = path::end(Path);
1419  IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
1420  if (*IParent != *IChild)
1421  return false;
1422  }
1423  // Have we exhausted the parent path?
1424  return IParent == EParent;
1425 }
1426 
1427 StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1428  assert(!Parent.empty());
1429  assert(containedIn(Parent, Path));
1430  return Path.slice(Parent.size() + 1, StringRef::npos);
1431 }
1432 
1433 void JSONWriter::startDirectory(StringRef Path) {
1434  StringRef Name =
1435  DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
1436  DirStack.push_back(Path);
1437  unsigned Indent = getDirIndent();
1438  OS.indent(Indent) << "{\n";
1439  OS.indent(Indent + 2) << "'type': 'directory',\n";
1440  OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1441  OS.indent(Indent + 2) << "'contents': [\n";
1442 }
1443 
1444 void JSONWriter::endDirectory() {
1445  unsigned Indent = getDirIndent();
1446  OS.indent(Indent + 2) << "]\n";
1447  OS.indent(Indent) << "}";
1448 
1449  DirStack.pop_back();
1450 }
1451 
1452 void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1453  unsigned Indent = getFileIndent();
1454  OS.indent(Indent) << "{\n";
1455  OS.indent(Indent + 2) << "'type': 'file',\n";
1456  OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1457  OS.indent(Indent + 2) << "'external-contents': \""
1458  << llvm::yaml::escape(RPath) << "\"\n";
1459  OS.indent(Indent) << "}";
1460 }
1461 
1462 void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1463  Optional<bool> IsCaseSensitive) {
1464  using namespace llvm::sys;
1465 
1466  OS << "{\n"
1467  " 'version': 0,\n";
1468  if (IsCaseSensitive.hasValue())
1469  OS << " 'case-sensitive': '"
1470  << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
1471  OS << " 'roots': [\n";
1472 
1473  if (!Entries.empty()) {
1474  const YAMLVFSEntry &Entry = Entries.front();
1475  startDirectory(path::parent_path(Entry.VPath));
1476  writeEntry(path::filename(Entry.VPath), Entry.RPath);
1477 
1478  for (const auto &Entry : Entries.slice(1)) {
1479  StringRef Dir = path::parent_path(Entry.VPath);
1480  if (Dir == DirStack.back())
1481  OS << ",\n";
1482  else {
1483  while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
1484  OS << "\n";
1485  endDirectory();
1486  }
1487  OS << ",\n";
1488  startDirectory(Dir);
1489  }
1490  writeEntry(path::filename(Entry.VPath), Entry.RPath);
1491  }
1492 
1493  while (!DirStack.empty()) {
1494  OS << "\n";
1495  endDirectory();
1496  }
1497  OS << "\n";
1498  }
1499 
1500  OS << " ]\n"
1501  << "}\n";
1502 }
1503 
1504 void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
1505  std::sort(Mappings.begin(), Mappings.end(),
1506  [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
1507  return LHS.VPath < RHS.VPath;
1508  });
1509 
1510  JSONWriter(OS).write(Mappings, IsCaseSensitive);
1511 }
1512 
1513 VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(
1514  const Twine &_Path, RedirectingFileSystem &FS,
1516  RedirectingDirectoryEntry::iterator End, std::error_code &EC)
1517  : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
1518  if (Current != End) {
1519  SmallString<128> PathStr(Dir);
1520  llvm::sys::path::append(PathStr, (*Current)->getName());
1521  llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
1522  if (S)
1523  CurrentEntry = *S;
1524  else
1525  EC = S.getError();
1526  }
1527 }
1528 
1529 std::error_code VFSFromYamlDirIterImpl::increment() {
1530  assert(Current != End && "cannot iterate past end");
1531  if (++Current != End) {
1532  SmallString<128> PathStr(Dir);
1533  llvm::sys::path::append(PathStr, (*Current)->getName());
1534  llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
1535  if (!S)
1536  return S.getError();
1537  CurrentEntry = *S;
1538  } else {
1539  CurrentEntry = Status();
1540  }
1541  return std::error_code();
1542 }
1543 
1545  const Twine &Path,
1546  std::error_code &EC)
1547  : FS(&FS_) {
1548  directory_iterator I = FS->dir_begin(Path, EC);
1549  if (!EC && I != directory_iterator()) {
1550  State = std::make_shared<IterState>();
1551  State->push(I);
1552  }
1553 }
1554 
1557  assert(FS && State && !State->empty() && "incrementing past end");
1558  assert(State->top()->isStatusKnown() && "non-canonical end iterator");
1560  if (State->top()->isDirectory()) {
1561  vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
1562  if (EC)
1563  return *this;
1564  if (I != End) {
1565  State->push(I);
1566  return *this;
1567  }
1568  }
1569 
1570  while (!State->empty() && State->top().increment(EC) == End)
1571  State->pop();
1572 
1573  if (State->empty())
1574  State.reset(); // end iterator
1575 
1576  return *this;
1577 }
static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames, Status ExternalStatus)
decltype(Entries) typedef::const_iterator const_iterator
static bool classof(const InMemoryNode *N)
Defines the clang::FileManager interface and associated types.
bool addFile(const Twine &Path, time_t ModificationTime, std::unique_ptr< llvm::MemoryBuffer > Buffer)
Add a buffer to the VFS with a path.
IntrusiveRefCntPtr< FileSystem > getRealFileSystem()
Gets an vfs::FileSystem for the 'real' file system, as seen by the operating system.
virtual llvm::ErrorOr< Status > status()=0
Get the status of the file.
The base class of the type hierarchy.
Definition: Type.h:1249
bool equivalent(const Status &Other) const
std::unique_ptr< llvm::MemoryBuffer > Buffer
bool isStatusKnown() const
uint32_t getUser() const
llvm::sys::TimeValue getLastModificationTime() const
InMemoryNode * getChild(StringRef Name)
std::error_code make_error_code(ParseError e)
Definition: Format.cpp:396
virtual llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBuffer(const Twine &Name, int64_t FileSize=-1, bool RequiresNullTerminator=true, bool IsVolatile=false)=0
Get the contents of the file as a MemoryBuffer.
virtual llvm::ErrorOr< std::unique_ptr< File > > openFileForRead(const Twine &Path)=0
Get a File object for the file at Path, if one exists.
virtual directory_iterator dir_begin(const Twine &Dir, std::error_code &EC)=0
Get a directory_iterator for Dir.
bool useNormalizedPaths() const
Return true if this file system normalizes . and .. in paths.
iterator begin() const
Definition: Type.h:4072
The virtual file system interface.
IntrusiveRefCntPtr< FileSystem > getVFSFromYAML(std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext=nullptr, IntrusiveRefCntPtr< FileSystem > ExternalFS=getRealFileSystem())
Gets a FileSystem for a virtual file system described in YAML format.
class LLVM_ALIGNAS(8) DependentTemplateSpecializationType const IdentifierInfo * Name
Represents a template specialization type whose template cannot be resolved, e.g. ...
Definition: Type.h:4381
void write(llvm::raw_ostream &OS)
virtual std::error_code close()=0
Closes the file.
An input iterator over the recursive contents of a virtual path, similar to llvm::sys::fs::recursive_...
An in-memory file system.
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override
Get a directory_iterator for Dir.
bool addFileNoOwn(const Twine &Path, time_t ModificationTime, llvm::MemoryBuffer *Buffer)
Add a buffer to the VFS with a path.
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override
Get a directory_iterator for Dir.
A file system that allows overlaying one AbstractFileSystem on top of another.
virtual llvm::ErrorOr< Status > status(const Twine &Path)=0
Get the status of the entry at Path, if one exists.
llvm::sys::fs::file_type getType() const
InMemoryNode(Status Stat, InMemoryNodeKind Kind)
FileSystemList::reverse_iterator iterator
llvm::ErrorOr< std::unique_ptr< File > > openFileForRead(const Twine &Path) override
Get a File object for the file at Path, if one exists.
The result of a status operation.
void pushOverlay(IntrusiveRefCntPtr< FileSystem > FS)
Pushes a file system on top of the stack.
iterator end() const
detail::InMemoryDirectory::const_iterator I
recursive_directory_iterator()
Construct an 'end' iterator.
AnnotatingParser & P
const SmallVectorImpl< AnnotatedLine * >::const_iterator End
virtual std::error_code setCurrentWorkingDirectory(const Twine &Path)=0
Set the working directory.
ID
Defines the set of possible language-specific address spaces.
Definition: AddressSpaces.h:27
SourceManager & SM
The in memory file system is a tree of Nodes.
llvm::sys::fs::UniqueID getUniqueID() const
InMemoryFile & Node
SourceManager & SourceMgr
Definition: Format.cpp:1352
virtual llvm::ErrorOr< std::string > getCurrentWorkingDirectory() const =0
Get the working directory of this file system.
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.
uint64_t getSize() const
Represents an open file.
llvm::ErrorOr< std::string > getCurrentWorkingDirectory() const override
Get the working directory of this file system.
static bool pathHasTraversal(StringRef Path)
The result type of a method or function.
InMemoryNodeKind getKind() const
InMemoryFileSystem(bool UseNormalizedPaths=true)
#define false
Definition: stdbool.h:33
Kind
recursive_directory_iterator & increment(std::error_code &EC)
Equivalent to operator++, with an error code.
const TemplateArgument * iterator
Definition: Type.h:4070
llvm::ErrorOr< std::unique_ptr< File > > openFileForRead(const Twine &Path) override
Get a File object for the file at Path, if one exists.
uint32_t getGroup() const
iterator overlays_begin()
Get an iterator pointing to the most recently added file system.
bool isDirectory() const
InMemoryNode * addChild(StringRef Name, std::unique_ptr< InMemoryNode > Child)
std::error_code setCurrentWorkingDirectory(const Twine &Path) override
Set the working directory.
Represents a template argument.
Definition: TemplateBase.h:40
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
Definition: ASTMatchers.h:1723
bool isRegularFile() 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::perms getPermissions() const
detail::InMemoryDirectory::const_iterator E
OverlayFileSystem(IntrusiveRefCntPtr< FileSystem > Base)
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)
bool exists(const Twine &Path)
Check whether a file exists. Provided for convenience.
std::string toString(const til::SExpr *E)
static bool classof(const OMPClause *T)
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(const Twine &Name, int64_t FileSize=-1, bool RequiresNullTerminator=true, bool IsVolatile=false)
This is a convenience method that opens a file, gets its content and then closes the file...
std::string toString(unsigned Indent) const override
virtual ~File()
Destroy the file after closing it (if open).
An input iterator over the entries in a virtual path, similar to llvm::sys::fs::directory_iterator.
llvm::ErrorOr< Status > status(const Twine &Path) override
Get the status of the entry at Path, if one exists.
std::error_code setCurrentWorkingDirectory(const Twine &Path) override
Set the working directory.
FormatToken * Current
static Decl::Kind getKind(const Decl *D)
Definition: DeclBase.cpp:772
An interface for virtual file systems to provide an iterator over the (non-recursive) contents of a d...
llvm::ErrorOr< Status > status(const Twine &Path) override
Get the status of the entry at Path, if one exists.
virtual std::error_code increment()=0
Sets CurrentEntry to the next entry in the directory on success, or returns a system-defined error_co...
#define true
Definition: stdbool.h:32
std::error_code makeAbsolute(SmallVectorImpl< char > &Path) const
Make Path an absolute path.
unsigned Indent
The current line's indent.