VirtualFileSystem.cpp revision 651f13cea278ec967336033dd032faef0e9fc2ec
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
12#include "clang/Basic/VirtualFileSystem.h"
13#include "llvm/ADT/DenseMap.h"
14#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/StringExtras.h"
16#include "llvm/Support/MemoryBuffer.h"
17#include "llvm/Support/Path.h"
18#include "llvm/Support/YAMLParser.h"
19#include <atomic>
20#include <memory>
21
22using namespace clang;
23using namespace clang::vfs;
24using namespace llvm;
25using llvm::sys::fs::file_status;
26using llvm::sys::fs::file_type;
27using llvm::sys::fs::perms;
28using llvm::sys::fs::UniqueID;
29
30Status::Status(const file_status &Status)
31    : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
32      User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
33      Type(Status.type()), Perms(Status.permissions()) {}
34
35Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
36               sys::TimeValue MTime, uint32_t User, uint32_t Group,
37               uint64_t Size, file_type Type, perms Perms)
38    : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
39      Type(Type), Perms(Perms) {}
40
41bool Status::equivalent(const Status &Other) const {
42  return getUniqueID() == Other.getUniqueID();
43}
44bool Status::isDirectory() const {
45  return Type == file_type::directory_file;
46}
47bool Status::isRegularFile() const {
48  return Type == file_type::regular_file;
49}
50bool Status::isOther() const {
51  return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
52}
53bool Status::isSymlink() const {
54  return Type == file_type::symlink_file;
55}
56bool Status::isStatusKnown() const {
57  return Type != file_type::status_error;
58}
59bool Status::exists() const {
60  return isStatusKnown() && Type != file_type::file_not_found;
61}
62
63File::~File() {}
64
65FileSystem::~FileSystem() {}
66
67error_code FileSystem::getBufferForFile(const llvm::Twine &Name,
68                                        std::unique_ptr<MemoryBuffer> &Result,
69                                        int64_t FileSize,
70                                        bool RequiresNullTerminator) {
71  std::unique_ptr<File> F;
72  if (error_code EC = openFileForRead(Name, F))
73    return EC;
74
75  error_code EC = F->getBuffer(Name, Result, FileSize, RequiresNullTerminator);
76  return EC;
77}
78
79//===-----------------------------------------------------------------------===/
80// RealFileSystem implementation
81//===-----------------------------------------------------------------------===/
82
83namespace {
84/// \brief Wrapper around a raw file descriptor.
85class RealFile : public File {
86  int FD;
87  Status S;
88  friend class RealFileSystem;
89  RealFile(int FD) : FD(FD) {
90    assert(FD >= 0 && "Invalid or inactive file descriptor");
91  }
92
93public:
94  ~RealFile();
95  ErrorOr<Status> status() override;
96  error_code getBuffer(const Twine &Name, std::unique_ptr<MemoryBuffer> &Result,
97                       int64_t FileSize = -1,
98                       bool RequiresNullTerminator = true) override;
99  error_code close() override;
100  void setName(StringRef Name) override;
101};
102} // end anonymous namespace
103RealFile::~RealFile() { close(); }
104
105ErrorOr<Status> RealFile::status() {
106  assert(FD != -1 && "cannot stat closed file");
107  if (!S.isStatusKnown()) {
108    file_status RealStatus;
109    if (error_code EC = sys::fs::status(FD, RealStatus))
110      return EC;
111    Status NewS(RealStatus);
112    NewS.setName(S.getName());
113    S = std::move(NewS);
114  }
115  return S;
116}
117
118error_code RealFile::getBuffer(const Twine &Name,
119                               std::unique_ptr<MemoryBuffer> &Result,
120                               int64_t FileSize, bool RequiresNullTerminator) {
121  assert(FD != -1 && "cannot get buffer for closed file");
122  return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
123                                   RequiresNullTerminator);
124}
125
126// FIXME: This is terrible, we need this for ::close.
127#if !defined(_MSC_VER) && !defined(__MINGW32__)
128#include <unistd.h>
129#include <sys/uio.h>
130#else
131#include <io.h>
132#ifndef S_ISFIFO
133#define S_ISFIFO(x) (0)
134#endif
135#endif
136error_code RealFile::close() {
137  if (::close(FD))
138    return error_code(errno, system_category());
139  FD = -1;
140  return error_code::success();
141}
142
143void RealFile::setName(StringRef Name) {
144  S.setName(Name);
145}
146
147namespace {
148/// \brief The file system according to your operating system.
149class RealFileSystem : public FileSystem {
150public:
151  ErrorOr<Status> status(const Twine &Path) override;
152  error_code openFileForRead(const Twine &Path,
153                             std::unique_ptr<File> &Result) override;
154};
155} // end anonymous namespace
156
157ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
158  sys::fs::file_status RealStatus;
159  if (error_code EC = sys::fs::status(Path, RealStatus))
160    return EC;
161  Status Result(RealStatus);
162  Result.setName(Path.str());
163  return Result;
164}
165
166error_code RealFileSystem::openFileForRead(const Twine &Name,
167                                           std::unique_ptr<File> &Result) {
168  int FD;
169  if (error_code EC = sys::fs::openFileForRead(Name, FD))
170    return EC;
171  Result.reset(new RealFile(FD));
172  Result->setName(Name.str());
173  return error_code::success();
174}
175
176IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
177  static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
178  return FS;
179}
180
181//===-----------------------------------------------------------------------===/
182// OverlayFileSystem implementation
183//===-----------------------------------------------------------------------===/
184OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
185  pushOverlay(BaseFS);
186}
187
188void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
189  FSList.push_back(FS);
190}
191
192ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
193  // FIXME: handle symlinks that cross file systems
194  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
195    ErrorOr<Status> Status = (*I)->status(Path);
196    if (Status || Status.getError() != errc::no_such_file_or_directory)
197      return Status;
198  }
199  return error_code(errc::no_such_file_or_directory, system_category());
200}
201
202error_code OverlayFileSystem::openFileForRead(const llvm::Twine &Path,
203                                              std::unique_ptr<File> &Result) {
204  // FIXME: handle symlinks that cross file systems
205  for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
206    error_code EC = (*I)->openFileForRead(Path, Result);
207    if (!EC || EC != errc::no_such_file_or_directory)
208      return EC;
209  }
210  return error_code(errc::no_such_file_or_directory, system_category());
211}
212
213//===-----------------------------------------------------------------------===/
214// VFSFromYAML implementation
215//===-----------------------------------------------------------------------===/
216
217// Allow DenseMap<StringRef, ...>.  This is useful below because we know all the
218// strings are literals and will outlive the map, and there is no reason to
219// store them.
220namespace llvm {
221  template<>
222  struct DenseMapInfo<StringRef> {
223    // This assumes that "" will never be a valid key.
224    static inline StringRef getEmptyKey() { return StringRef(""); }
225    static inline StringRef getTombstoneKey() { return StringRef(); }
226    static unsigned getHashValue(StringRef Val) { return HashString(Val); }
227    static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
228  };
229}
230
231namespace {
232
233enum EntryKind {
234  EK_Directory,
235  EK_File
236};
237
238/// \brief A single file or directory in the VFS.
239class Entry {
240  EntryKind Kind;
241  std::string Name;
242
243public:
244  virtual ~Entry();
245  Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
246  StringRef getName() const { return Name; }
247  EntryKind getKind() const { return Kind; }
248};
249
250class DirectoryEntry : public Entry {
251  std::vector<Entry *> Contents;
252  Status S;
253
254public:
255  virtual ~DirectoryEntry();
256  DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
257      : Entry(EK_Directory, Name), Contents(std::move(Contents)),
258        S(std::move(S)) {}
259  Status getStatus() { return S; }
260  typedef std::vector<Entry *>::iterator iterator;
261  iterator contents_begin() { return Contents.begin(); }
262  iterator contents_end() { return Contents.end(); }
263  static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
264};
265
266class FileEntry : public Entry {
267public:
268  enum NameKind {
269    NK_NotSet,
270    NK_External,
271    NK_Virtual
272  };
273private:
274  std::string ExternalContentsPath;
275  NameKind UseName;
276public:
277  FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
278      : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
279        UseName(UseName) {}
280  StringRef getExternalContentsPath() const { return ExternalContentsPath; }
281  /// \brief whether to use the external path as the name for this file.
282  bool useExternalName(bool GlobalUseExternalName) const {
283    return UseName == NK_NotSet ? GlobalUseExternalName
284                                : (UseName == NK_External);
285  }
286  static bool classof(const Entry *E) { return E->getKind() == EK_File; }
287};
288
289/// \brief A virtual file system parsed from a YAML file.
290///
291/// Currently, this class allows creating virtual directories and mapping
292/// virtual file paths to existing external files, available in \c ExternalFS.
293///
294/// The basic structure of the parsed file is:
295/// \verbatim
296/// {
297///   'version': <version number>,
298///   <optional configuration>
299///   'roots': [
300///              <directory entries>
301///            ]
302/// }
303/// \endverbatim
304///
305/// All configuration options are optional.
306///   'case-sensitive': <boolean, default=true>
307///   'use-external-names': <boolean, default=true>
308///
309/// Virtual directories are represented as
310/// \verbatim
311/// {
312///   'type': 'directory',
313///   'name': <string>,
314///   'contents': [ <file or directory entries> ]
315/// }
316/// \endverbatim
317///
318/// The default attributes for virtual directories are:
319/// \verbatim
320/// MTime = now() when created
321/// Perms = 0777
322/// User = Group = 0
323/// Size = 0
324/// UniqueID = unspecified unique value
325/// \endverbatim
326///
327/// Re-mapped files are represented as
328/// \verbatim
329/// {
330///   'type': 'file',
331///   'name': <string>,
332///   'use-external-name': <boolean> # Optional
333///   'external-contents': <path to external file>)
334/// }
335/// \endverbatim
336///
337/// and inherit their attributes from the external contents.
338///
339/// In both cases, the 'name' field may contain multiple path components (e.g.
340/// /path/to/file). However, any directory that contains more than one child
341/// must be uniquely represented by a directory entry.
342class VFSFromYAML : public vfs::FileSystem {
343  std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
344  /// \brief The file system to use for external references.
345  IntrusiveRefCntPtr<FileSystem> ExternalFS;
346
347  /// @name Configuration
348  /// @{
349
350  /// \brief Whether to perform case-sensitive comparisons.
351  ///
352  /// Currently, case-insensitive matching only works correctly with ASCII.
353  bool CaseSensitive;
354
355  /// \brief Whether to use to use the value of 'external-contents' for the
356  /// names of files.  This global value is overridable on a per-file basis.
357  bool UseExternalNames;
358  /// @}
359
360  friend class VFSFromYAMLParser;
361
362private:
363  VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
364      : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
365
366  /// \brief Looks up \p Path in \c Roots.
367  ErrorOr<Entry *> lookupPath(const Twine &Path);
368
369  /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
370  /// recursing into the contents of \p From if it is a directory.
371  ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
372                              sys::path::const_iterator End, Entry *From);
373
374public:
375  ~VFSFromYAML();
376
377  /// \brief Parses \p Buffer, which is expected to be in YAML format and
378  /// returns a virtual file system representing its contents.
379  ///
380  /// Takes ownership of \p Buffer.
381  static VFSFromYAML *create(MemoryBuffer *Buffer,
382                             SourceMgr::DiagHandlerTy DiagHandler,
383                             void *DiagContext,
384                             IntrusiveRefCntPtr<FileSystem> ExternalFS);
385
386  ErrorOr<Status> status(const Twine &Path) override;
387  error_code openFileForRead(const Twine &Path,
388                             std::unique_ptr<File> &Result) override;
389};
390
391/// \brief A helper class to hold the common YAML parsing state.
392class VFSFromYAMLParser {
393  yaml::Stream &Stream;
394
395  void error(yaml::Node *N, const Twine &Msg) {
396    Stream.printError(N, Msg);
397  }
398
399  // false on error
400  bool parseScalarString(yaml::Node *N, StringRef &Result,
401                         SmallVectorImpl<char> &Storage) {
402    yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
403    if (!S) {
404      error(N, "expected string");
405      return false;
406    }
407    Result = S->getValue(Storage);
408    return true;
409  }
410
411  // false on error
412  bool parseScalarBool(yaml::Node *N, bool &Result) {
413    SmallString<5> Storage;
414    StringRef Value;
415    if (!parseScalarString(N, Value, Storage))
416      return false;
417
418    if (Value.equals_lower("true") || Value.equals_lower("on") ||
419        Value.equals_lower("yes") || Value == "1") {
420      Result = true;
421      return true;
422    } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
423               Value.equals_lower("no") || Value == "0") {
424      Result = false;
425      return true;
426    }
427
428    error(N, "expected boolean value");
429    return false;
430  }
431
432  struct KeyStatus {
433    KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
434    bool Required;
435    bool Seen;
436  };
437  typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
438
439  // false on error
440  bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
441                                  DenseMap<StringRef, KeyStatus> &Keys) {
442    if (!Keys.count(Key)) {
443      error(KeyNode, "unknown key");
444      return false;
445    }
446    KeyStatus &S = Keys[Key];
447    if (S.Seen) {
448      error(KeyNode, Twine("duplicate key '") + Key + "'");
449      return false;
450    }
451    S.Seen = true;
452    return true;
453  }
454
455  // false on error
456  bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
457    for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
458         E = Keys.end();
459         I != E; ++I) {
460      if (I->second.Required && !I->second.Seen) {
461        error(Obj, Twine("missing key '") + I->first + "'");
462        return false;
463      }
464    }
465    return true;
466  }
467
468  Entry *parseEntry(yaml::Node *N) {
469    yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
470    if (!M) {
471      error(N, "expected mapping node for file or directory entry");
472      return NULL;
473    }
474
475    KeyStatusPair Fields[] = {
476      KeyStatusPair("name", true),
477      KeyStatusPair("type", true),
478      KeyStatusPair("contents", false),
479      KeyStatusPair("external-contents", false),
480      KeyStatusPair("use-external-name", false),
481    };
482
483    DenseMap<StringRef, KeyStatus> Keys(
484        &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
485
486    bool HasContents = false; // external or otherwise
487    std::vector<Entry *> EntryArrayContents;
488    std::string ExternalContentsPath;
489    std::string Name;
490    FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
491    EntryKind Kind;
492
493    for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
494         ++I) {
495      StringRef Key;
496      // Reuse the buffer for key and value, since we don't look at key after
497      // parsing value.
498      SmallString<256> Buffer;
499      if (!parseScalarString(I->getKey(), Key, Buffer))
500        return NULL;
501
502      if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
503        return NULL;
504
505      StringRef Value;
506      if (Key == "name") {
507        if (!parseScalarString(I->getValue(), Value, Buffer))
508          return NULL;
509        Name = Value;
510      } else if (Key == "type") {
511        if (!parseScalarString(I->getValue(), Value, Buffer))
512          return NULL;
513        if (Value == "file")
514          Kind = EK_File;
515        else if (Value == "directory")
516          Kind = EK_Directory;
517        else {
518          error(I->getValue(), "unknown value for 'type'");
519          return NULL;
520        }
521      } else if (Key == "contents") {
522        if (HasContents) {
523          error(I->getKey(),
524                "entry already has 'contents' or 'external-contents'");
525          return NULL;
526        }
527        HasContents = true;
528        yaml::SequenceNode *Contents =
529            dyn_cast<yaml::SequenceNode>(I->getValue());
530        if (!Contents) {
531          // FIXME: this is only for directories, what about files?
532          error(I->getValue(), "expected array");
533          return NULL;
534        }
535
536        for (yaml::SequenceNode::iterator I = Contents->begin(),
537                                          E = Contents->end();
538             I != E; ++I) {
539          if (Entry *E = parseEntry(&*I))
540            EntryArrayContents.push_back(E);
541          else
542            return NULL;
543        }
544      } else if (Key == "external-contents") {
545        if (HasContents) {
546          error(I->getKey(),
547                "entry already has 'contents' or 'external-contents'");
548          return NULL;
549        }
550        HasContents = true;
551        if (!parseScalarString(I->getValue(), Value, Buffer))
552          return NULL;
553        ExternalContentsPath = Value;
554      } else if (Key == "use-external-name") {
555        bool Val;
556        if (!parseScalarBool(I->getValue(), Val))
557          return NULL;
558        UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
559      } else {
560        llvm_unreachable("key missing from Keys");
561      }
562    }
563
564    if (Stream.failed())
565      return NULL;
566
567    // check for missing keys
568    if (!HasContents) {
569      error(N, "missing key 'contents' or 'external-contents'");
570      return NULL;
571    }
572    if (!checkMissingKeys(N, Keys))
573      return NULL;
574
575    // check invalid configuration
576    if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
577      error(N, "'use-external-name' is not supported for directories");
578      return NULL;
579    }
580
581    // Remove trailing slash(es), being careful not to remove the root path
582    StringRef Trimmed(Name);
583    size_t RootPathLen = sys::path::root_path(Trimmed).size();
584    while (Trimmed.size() > RootPathLen &&
585           sys::path::is_separator(Trimmed.back()))
586      Trimmed = Trimmed.slice(0, Trimmed.size()-1);
587    // Get the last component
588    StringRef LastComponent = sys::path::filename(Trimmed);
589
590    Entry *Result = 0;
591    switch (Kind) {
592    case EK_File:
593      Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
594                             UseExternalName);
595      break;
596    case EK_Directory:
597      Result = new DirectoryEntry(LastComponent, std::move(EntryArrayContents),
598          Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
599                 0, file_type::directory_file, sys::fs::all_all));
600      break;
601    }
602
603    StringRef Parent = sys::path::parent_path(Trimmed);
604    if (Parent.empty())
605      return Result;
606
607    // if 'name' contains multiple components, create implicit directory entries
608    for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
609                                     E = sys::path::rend(Parent);
610         I != E; ++I) {
611      Result = new DirectoryEntry(*I, llvm::makeArrayRef(Result),
612          Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
613                 0, file_type::directory_file, sys::fs::all_all));
614    }
615    return Result;
616  }
617
618public:
619  VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
620
621  // false on error
622  bool parse(yaml::Node *Root, VFSFromYAML *FS) {
623    yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
624    if (!Top) {
625      error(Root, "expected mapping node");
626      return false;
627    }
628
629    KeyStatusPair Fields[] = {
630      KeyStatusPair("version", true),
631      KeyStatusPair("case-sensitive", false),
632      KeyStatusPair("use-external-names", false),
633      KeyStatusPair("roots", true),
634    };
635
636    DenseMap<StringRef, KeyStatus> Keys(
637        &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
638
639    // Parse configuration and 'roots'
640    for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
641         ++I) {
642      SmallString<10> KeyBuffer;
643      StringRef Key;
644      if (!parseScalarString(I->getKey(), Key, KeyBuffer))
645        return false;
646
647      if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
648        return false;
649
650      if (Key == "roots") {
651        yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
652        if (!Roots) {
653          error(I->getValue(), "expected array");
654          return false;
655        }
656
657        for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
658             I != E; ++I) {
659          if (Entry *E = parseEntry(&*I))
660            FS->Roots.push_back(E);
661          else
662            return false;
663        }
664      } else if (Key == "version") {
665        StringRef VersionString;
666        SmallString<4> Storage;
667        if (!parseScalarString(I->getValue(), VersionString, Storage))
668          return false;
669        int Version;
670        if (VersionString.getAsInteger<int>(10, Version)) {
671          error(I->getValue(), "expected integer");
672          return false;
673        }
674        if (Version < 0) {
675          error(I->getValue(), "invalid version number");
676          return false;
677        }
678        if (Version != 0) {
679          error(I->getValue(), "version mismatch, expected 0");
680          return false;
681        }
682      } else if (Key == "case-sensitive") {
683        if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
684          return false;
685      } else if (Key == "use-external-names") {
686        if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
687          return false;
688      } else {
689        llvm_unreachable("key missing from Keys");
690      }
691    }
692
693    if (Stream.failed())
694      return false;
695
696    if (!checkMissingKeys(Top, Keys))
697      return false;
698    return true;
699  }
700};
701} // end of anonymous namespace
702
703Entry::~Entry() {}
704DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
705
706VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
707
708VFSFromYAML *VFSFromYAML::create(MemoryBuffer *Buffer,
709                                 SourceMgr::DiagHandlerTy DiagHandler,
710                                 void *DiagContext,
711                                 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
712
713  SourceMgr SM;
714  yaml::Stream Stream(Buffer, SM);
715
716  SM.setDiagHandler(DiagHandler, DiagContext);
717  yaml::document_iterator DI = Stream.begin();
718  yaml::Node *Root = DI->getRoot();
719  if (DI == Stream.end() || !Root) {
720    SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
721    return NULL;
722  }
723
724  VFSFromYAMLParser P(Stream);
725
726  std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
727  if (!P.parse(Root, FS.get()))
728    return NULL;
729
730  return FS.release();
731}
732
733ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
734  SmallString<256> Path;
735  Path_.toVector(Path);
736
737  // Handle relative paths
738  if (error_code EC = sys::fs::make_absolute(Path))
739    return EC;
740
741  if (Path.empty())
742    return error_code(errc::invalid_argument, system_category());
743
744  sys::path::const_iterator Start = sys::path::begin(Path);
745  sys::path::const_iterator End = sys::path::end(Path);
746  for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
747       I != E; ++I) {
748    ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
749    if (Result || Result.getError() != errc::no_such_file_or_directory)
750      return Result;
751  }
752  return error_code(errc::no_such_file_or_directory, system_category());
753}
754
755ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
756                                         sys::path::const_iterator End,
757                                         Entry *From) {
758  if (Start->equals("."))
759    ++Start;
760
761  // FIXME: handle ..
762  if (CaseSensitive ? !Start->equals(From->getName())
763                    : !Start->equals_lower(From->getName()))
764    // failure to match
765    return error_code(errc::no_such_file_or_directory, system_category());
766
767  ++Start;
768
769  if (Start == End) {
770    // Match!
771    return From;
772  }
773
774  DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
775  if (!DE)
776    return error_code(errc::not_a_directory, system_category());
777
778  for (DirectoryEntry::iterator I = DE->contents_begin(),
779                                E = DE->contents_end();
780       I != E; ++I) {
781    ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
782    if (Result || Result.getError() != errc::no_such_file_or_directory)
783      return Result;
784  }
785  return error_code(errc::no_such_file_or_directory, system_category());
786}
787
788ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
789  ErrorOr<Entry *> Result = lookupPath(Path);
790  if (!Result)
791    return Result.getError();
792
793  std::string PathStr(Path.str());
794  if (FileEntry *F = dyn_cast<FileEntry>(*Result)) {
795    ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
796    assert(!S || S->getName() == F->getExternalContentsPath());
797    if (S && !F->useExternalName(UseExternalNames))
798      S->setName(PathStr);
799    return S;
800  } else { // directory
801    DirectoryEntry *DE = cast<DirectoryEntry>(*Result);
802    Status S = DE->getStatus();
803    S.setName(PathStr);
804    return S;
805  }
806}
807
808error_code VFSFromYAML::openFileForRead(const Twine &Path,
809                                        std::unique_ptr<vfs::File> &Result) {
810  ErrorOr<Entry *> E = lookupPath(Path);
811  if (!E)
812    return E.getError();
813
814  FileEntry *F = dyn_cast<FileEntry>(*E);
815  if (!F) // FIXME: errc::not_a_file?
816    return error_code(errc::invalid_argument, system_category());
817
818  if (error_code EC = ExternalFS->openFileForRead(F->getExternalContentsPath(),
819                                                  Result))
820    return EC;
821
822  if (!F->useExternalName(UseExternalNames))
823    Result->setName(Path.str());
824
825  return error_code::success();
826}
827
828IntrusiveRefCntPtr<FileSystem>
829vfs::getVFSFromYAML(MemoryBuffer *Buffer, SourceMgr::DiagHandlerTy DiagHandler,
830                    void *DiagContext,
831                    IntrusiveRefCntPtr<FileSystem> ExternalFS) {
832  return VFSFromYAML::create(Buffer, DiagHandler, DiagContext, ExternalFS);
833}
834
835UniqueID vfs::getNextVirtualUniqueID() {
836  static std::atomic<unsigned> UID;
837  unsigned ID = ++UID;
838  // The following assumes that uint64_t max will never collide with a real
839  // dev_t value from the OS.
840  return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
841}
842