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/ADT/StringSet.h"
17 #include "llvm/ADT/iterator_range.h"
18 #include "llvm/Support/Errc.h"
19 #include "llvm/Support/MemoryBuffer.h"
20 #include "llvm/Support/Path.h"
21 #include "llvm/Support/YAMLParser.h"
22 #if !defined(_LIBCPP_HAS_NO_THREADS) && defined(__minix)
23 #include <atomic>
24 #endif // !defined(_LIBCPP_HAS_NO_THREADS) && defined(__minix)
25 #include <memory>
26 
27 using namespace clang;
28 using namespace clang::vfs;
29 using namespace llvm;
30 using llvm::sys::fs::file_status;
31 using llvm::sys::fs::file_type;
32 using llvm::sys::fs::perms;
33 using llvm::sys::fs::UniqueID;
34 
Status(const file_status & Status)35 Status::Status(const file_status &Status)
36     : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
37       User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
38       Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false)  {}
39 
Status(StringRef Name,StringRef ExternalName,UniqueID UID,sys::TimeValue MTime,uint32_t User,uint32_t Group,uint64_t Size,file_type Type,perms Perms)40 Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
41                sys::TimeValue MTime, uint32_t User, uint32_t Group,
42                uint64_t Size, file_type Type, perms Perms)
43     : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
44       Type(Type), Perms(Perms), IsVFSMapped(false) {}
45 
equivalent(const Status & Other) const46 bool Status::equivalent(const Status &Other) const {
47   return getUniqueID() == Other.getUniqueID();
48 }
isDirectory() const49 bool Status::isDirectory() const {
50   return Type == file_type::directory_file;
51 }
isRegularFile() const52 bool Status::isRegularFile() const {
53   return Type == file_type::regular_file;
54 }
isOther() const55 bool Status::isOther() const {
56   return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
57 }
isSymlink() const58 bool Status::isSymlink() const {
59   return Type == file_type::symlink_file;
60 }
isStatusKnown() const61 bool Status::isStatusKnown() const {
62   return Type != file_type::status_error;
63 }
exists() const64 bool Status::exists() const {
65   return isStatusKnown() && Type != file_type::file_not_found;
66 }
67 
~File()68 File::~File() {}
69 
~FileSystem()70 FileSystem::~FileSystem() {}
71 
72 ErrorOr<std::unique_ptr<MemoryBuffer>>
getBufferForFile(const llvm::Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)73 FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
74                              bool RequiresNullTerminator, bool IsVolatile) {
75   auto F = openFileForRead(Name);
76   if (!F)
77     return F.getError();
78 
79   return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
80 }
81 
82 //===-----------------------------------------------------------------------===/
83 // RealFileSystem implementation
84 //===-----------------------------------------------------------------------===/
85 
86 namespace {
87 /// \brief Wrapper around a raw file descriptor.
88 class RealFile : public File {
89   int FD;
90   Status S;
91   friend class RealFileSystem;
RealFile(int FD)92   RealFile(int FD) : FD(FD) {
93     assert(FD >= 0 && "Invalid or inactive file descriptor");
94   }
95 
96 public:
97   ~RealFile();
98   ErrorOr<Status> status() override;
99   ErrorOr<std::unique_ptr<MemoryBuffer>>
100   getBuffer(const Twine &Name, int64_t FileSize = -1,
101             bool RequiresNullTerminator = true,
102             bool IsVolatile = false) override;
103   std::error_code close() override;
104   void setName(StringRef Name) override;
105 };
106 } // end anonymous namespace
~RealFile()107 RealFile::~RealFile() { close(); }
108 
status()109 ErrorOr<Status> RealFile::status() {
110   assert(FD != -1 && "cannot stat closed file");
111   if (!S.isStatusKnown()) {
112     file_status RealStatus;
113     if (std::error_code EC = sys::fs::status(FD, RealStatus))
114       return EC;
115     Status NewS(RealStatus);
116     NewS.setName(S.getName());
117     S = std::move(NewS);
118   }
119   return S;
120 }
121 
122 ErrorOr<std::unique_ptr<MemoryBuffer>>
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)123 RealFile::getBuffer(const Twine &Name, int64_t FileSize,
124                     bool RequiresNullTerminator, bool IsVolatile) {
125   assert(FD != -1 && "cannot get buffer for closed file");
126   return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
127                                    IsVolatile);
128 }
129 
130 // FIXME: This is terrible, we need this for ::close.
131 #if !defined(_MSC_VER) && !defined(__MINGW32__)
132 #include <unistd.h>
133 #include <sys/uio.h>
134 #else
135 #include <io.h>
136 #ifndef S_ISFIFO
137 #define S_ISFIFO(x) (0)
138 #endif
139 #endif
close()140 std::error_code RealFile::close() {
141   if (::close(FD))
142     return std::error_code(errno, std::generic_category());
143   FD = -1;
144   return std::error_code();
145 }
146 
setName(StringRef Name)147 void RealFile::setName(StringRef Name) {
148   S.setName(Name);
149 }
150 
151 namespace {
152 /// \brief The file system according to your operating system.
153 class RealFileSystem : public FileSystem {
154 public:
155   ErrorOr<Status> status(const Twine &Path) override;
156   ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
157   directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
158 };
159 } // end anonymous namespace
160 
status(const Twine & Path)161 ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
162   sys::fs::file_status RealStatus;
163   if (std::error_code EC = sys::fs::status(Path, RealStatus))
164     return EC;
165   Status Result(RealStatus);
166   Result.setName(Path.str());
167   return Result;
168 }
169 
170 ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Name)171 RealFileSystem::openFileForRead(const Twine &Name) {
172   int FD;
173   if (std::error_code EC = sys::fs::openFileForRead(Name, FD))
174     return EC;
175   std::unique_ptr<File> Result(new RealFile(FD));
176   Result->setName(Name.str());
177   return std::move(Result);
178 }
179 
getRealFileSystem()180 IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
181   static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
182   return FS;
183 }
184 
185 namespace {
186 class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
187   std::string Path;
188   llvm::sys::fs::directory_iterator Iter;
189 public:
RealFSDirIter(const Twine & _Path,std::error_code & EC)190   RealFSDirIter(const Twine &_Path, std::error_code &EC)
191       : Path(_Path.str()), Iter(Path, EC) {
192     if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
193       llvm::sys::fs::file_status S;
194       EC = Iter->status(S);
195       if (!EC) {
196         CurrentEntry = Status(S);
197         CurrentEntry.setName(Iter->path());
198       }
199     }
200   }
201 
increment()202   std::error_code increment() override {
203     std::error_code EC;
204     Iter.increment(EC);
205     if (EC) {
206       return EC;
207     } else if (Iter == llvm::sys::fs::directory_iterator()) {
208       CurrentEntry = Status();
209     } else {
210       llvm::sys::fs::file_status S;
211       EC = Iter->status(S);
212       CurrentEntry = Status(S);
213       CurrentEntry.setName(Iter->path());
214     }
215     return EC;
216   }
217 };
218 }
219 
dir_begin(const Twine & Dir,std::error_code & EC)220 directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
221                                              std::error_code &EC) {
222   return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
223 }
224 
225 //===-----------------------------------------------------------------------===/
226 // OverlayFileSystem implementation
227 //===-----------------------------------------------------------------------===/
OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS)228 OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
229   pushOverlay(BaseFS);
230 }
231 
pushOverlay(IntrusiveRefCntPtr<FileSystem> FS)232 void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
233   FSList.push_back(FS);
234 }
235 
status(const Twine & Path)236 ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
237   // FIXME: handle symlinks that cross file systems
238   for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
239     ErrorOr<Status> Status = (*I)->status(Path);
240     if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
241       return Status;
242   }
243   return make_error_code(llvm::errc::no_such_file_or_directory);
244 }
245 
246 ErrorOr<std::unique_ptr<File>>
openFileForRead(const llvm::Twine & Path)247 OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
248   // FIXME: handle symlinks that cross file systems
249   for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
250     auto Result = (*I)->openFileForRead(Path);
251     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
252       return Result;
253   }
254   return make_error_code(llvm::errc::no_such_file_or_directory);
255 }
256 
~DirIterImpl()257 clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
258 
259 namespace {
260 class OverlayFSDirIterImpl : public clang::vfs::detail::DirIterImpl {
261   OverlayFileSystem &Overlays;
262   std::string Path;
263   OverlayFileSystem::iterator CurrentFS;
264   directory_iterator CurrentDirIter;
265   llvm::StringSet<> SeenNames;
266 
incrementFS()267   std::error_code incrementFS() {
268     assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
269     ++CurrentFS;
270     for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
271       std::error_code EC;
272       CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
273       if (EC && EC != errc::no_such_file_or_directory)
274         return EC;
275       if (CurrentDirIter != directory_iterator())
276         break; // found
277     }
278     return std::error_code();
279   }
280 
incrementDirIter(bool IsFirstTime)281   std::error_code incrementDirIter(bool IsFirstTime) {
282     assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
283            "incrementing past end");
284     std::error_code EC;
285     if (!IsFirstTime)
286       CurrentDirIter.increment(EC);
287     if (!EC && CurrentDirIter == directory_iterator())
288       EC = incrementFS();
289     return EC;
290   }
291 
incrementImpl(bool IsFirstTime)292   std::error_code incrementImpl(bool IsFirstTime) {
293     while (true) {
294       std::error_code EC = incrementDirIter(IsFirstTime);
295       if (EC || CurrentDirIter == directory_iterator()) {
296         CurrentEntry = Status();
297         return EC;
298       }
299       CurrentEntry = *CurrentDirIter;
300       StringRef Name = llvm::sys::path::filename(CurrentEntry.getName());
301       if (SeenNames.insert(Name).second)
302         return EC; // name not seen before
303     }
304     llvm_unreachable("returned above");
305   }
306 
307 public:
OverlayFSDirIterImpl(const Twine & Path,OverlayFileSystem & FS,std::error_code & EC)308   OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
309                        std::error_code &EC)
310       : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
311     CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
312     EC = incrementImpl(true);
313   }
314 
increment()315   std::error_code increment() override { return incrementImpl(false); }
316 };
317 } // end anonymous namespace
318 
dir_begin(const Twine & Dir,std::error_code & EC)319 directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
320                                                 std::error_code &EC) {
321   return directory_iterator(
322       std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
323 }
324 
325 //===-----------------------------------------------------------------------===/
326 // VFSFromYAML implementation
327 //===-----------------------------------------------------------------------===/
328 
329 // Allow DenseMap<StringRef, ...>.  This is useful below because we know all the
330 // strings are literals and will outlive the map, and there is no reason to
331 // store them.
332 namespace llvm {
333   template<>
334   struct DenseMapInfo<StringRef> {
335     // This assumes that "" will never be a valid key.
getEmptyKeyllvm::DenseMapInfo336     static inline StringRef getEmptyKey() { return StringRef(""); }
getTombstoneKeyllvm::DenseMapInfo337     static inline StringRef getTombstoneKey() { return StringRef(); }
getHashValuellvm::DenseMapInfo338     static unsigned getHashValue(StringRef Val) { return HashString(Val); }
isEqualllvm::DenseMapInfo339     static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
340   };
341 }
342 
343 namespace {
344 
345 enum EntryKind {
346   EK_Directory,
347   EK_File
348 };
349 
350 /// \brief A single file or directory in the VFS.
351 class Entry {
352   EntryKind Kind;
353   std::string Name;
354 
355 public:
356   virtual ~Entry();
Entry(EntryKind K,StringRef Name)357   Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
getName() const358   StringRef getName() const { return Name; }
getKind() const359   EntryKind getKind() const { return Kind; }
360 };
361 
362 class DirectoryEntry : public Entry {
363   std::vector<Entry *> Contents;
364   Status S;
365 
366 public:
367   virtual ~DirectoryEntry();
DirectoryEntry(StringRef Name,std::vector<Entry * > Contents,Status S)368   DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
369       : Entry(EK_Directory, Name), Contents(std::move(Contents)),
370         S(std::move(S)) {}
getStatus()371   Status getStatus() { return S; }
372   typedef std::vector<Entry *>::iterator iterator;
contents_begin()373   iterator contents_begin() { return Contents.begin(); }
contents_end()374   iterator contents_end() { return Contents.end(); }
classof(const Entry * E)375   static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
376 };
377 
378 class FileEntry : public Entry {
379 public:
380   enum NameKind {
381     NK_NotSet,
382     NK_External,
383     NK_Virtual
384   };
385 private:
386   std::string ExternalContentsPath;
387   NameKind UseName;
388 public:
FileEntry(StringRef Name,StringRef ExternalContentsPath,NameKind UseName)389   FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
390       : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
391         UseName(UseName) {}
getExternalContentsPath() const392   StringRef getExternalContentsPath() const { return ExternalContentsPath; }
393   /// \brief whether to use the external path as the name for this file.
useExternalName(bool GlobalUseExternalName) const394   bool useExternalName(bool GlobalUseExternalName) const {
395     return UseName == NK_NotSet ? GlobalUseExternalName
396                                 : (UseName == NK_External);
397   }
classof(const Entry * E)398   static bool classof(const Entry *E) { return E->getKind() == EK_File; }
399 };
400 
401 class VFSFromYAML;
402 
403 class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
404   std::string Dir;
405   VFSFromYAML &FS;
406   DirectoryEntry::iterator Current, End;
407 public:
408   VFSFromYamlDirIterImpl(const Twine &Path, VFSFromYAML &FS,
409                          DirectoryEntry::iterator Begin,
410                          DirectoryEntry::iterator End, std::error_code &EC);
411   std::error_code increment() override;
412 };
413 
414 /// \brief A virtual file system parsed from a YAML file.
415 ///
416 /// Currently, this class allows creating virtual directories and mapping
417 /// virtual file paths to existing external files, available in \c ExternalFS.
418 ///
419 /// The basic structure of the parsed file is:
420 /// \verbatim
421 /// {
422 ///   'version': <version number>,
423 ///   <optional configuration>
424 ///   'roots': [
425 ///              <directory entries>
426 ///            ]
427 /// }
428 /// \endverbatim
429 ///
430 /// All configuration options are optional.
431 ///   'case-sensitive': <boolean, default=true>
432 ///   'use-external-names': <boolean, default=true>
433 ///
434 /// Virtual directories are represented as
435 /// \verbatim
436 /// {
437 ///   'type': 'directory',
438 ///   'name': <string>,
439 ///   'contents': [ <file or directory entries> ]
440 /// }
441 /// \endverbatim
442 ///
443 /// The default attributes for virtual directories are:
444 /// \verbatim
445 /// MTime = now() when created
446 /// Perms = 0777
447 /// User = Group = 0
448 /// Size = 0
449 /// UniqueID = unspecified unique value
450 /// \endverbatim
451 ///
452 /// Re-mapped files are represented as
453 /// \verbatim
454 /// {
455 ///   'type': 'file',
456 ///   'name': <string>,
457 ///   'use-external-name': <boolean> # Optional
458 ///   'external-contents': <path to external file>)
459 /// }
460 /// \endverbatim
461 ///
462 /// and inherit their attributes from the external contents.
463 ///
464 /// In both cases, the 'name' field may contain multiple path components (e.g.
465 /// /path/to/file). However, any directory that contains more than one child
466 /// must be uniquely represented by a directory entry.
467 class VFSFromYAML : public vfs::FileSystem {
468   std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
469   /// \brief The file system to use for external references.
470   IntrusiveRefCntPtr<FileSystem> ExternalFS;
471 
472   /// @name Configuration
473   /// @{
474 
475   /// \brief Whether to perform case-sensitive comparisons.
476   ///
477   /// Currently, case-insensitive matching only works correctly with ASCII.
478   bool CaseSensitive;
479 
480   /// \brief Whether to use to use the value of 'external-contents' for the
481   /// names of files.  This global value is overridable on a per-file basis.
482   bool UseExternalNames;
483   /// @}
484 
485   friend class VFSFromYAMLParser;
486 
487 private:
VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)488   VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
489       : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
490 
491   /// \brief Looks up \p Path in \c Roots.
492   ErrorOr<Entry *> lookupPath(const Twine &Path);
493 
494   /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
495   /// recursing into the contents of \p From if it is a directory.
496   ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
497                               sys::path::const_iterator End, Entry *From);
498 
499   /// \brief Get the status of a given an \c Entry.
500   ErrorOr<Status> status(const Twine &Path, Entry *E);
501 
502 public:
503   ~VFSFromYAML();
504 
505   /// \brief Parses \p Buffer, which is expected to be in YAML format and
506   /// returns a virtual file system representing its contents.
507   static VFSFromYAML *create(std::unique_ptr<MemoryBuffer> Buffer,
508                              SourceMgr::DiagHandlerTy DiagHandler,
509                              void *DiagContext,
510                              IntrusiveRefCntPtr<FileSystem> ExternalFS);
511 
512   ErrorOr<Status> status(const Twine &Path) override;
513   ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
514 
dir_begin(const Twine & Dir,std::error_code & EC)515   directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
516     ErrorOr<Entry *> E = lookupPath(Dir);
517     if (!E) {
518       EC = E.getError();
519       return directory_iterator();
520     }
521     ErrorOr<Status> S = status(Dir, *E);
522     if (!S) {
523       EC = S.getError();
524       return directory_iterator();
525     }
526     if (!S->isDirectory()) {
527       EC = std::error_code(static_cast<int>(errc::not_a_directory),
528                            std::system_category());
529       return directory_iterator();
530     }
531 
532     DirectoryEntry *D = cast<DirectoryEntry>(*E);
533     return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
534         *this, D->contents_begin(), D->contents_end(), EC));
535   }
536 };
537 
538 /// \brief A helper class to hold the common YAML parsing state.
539 class VFSFromYAMLParser {
540   yaml::Stream &Stream;
541 
error(yaml::Node * N,const Twine & Msg)542   void error(yaml::Node *N, const Twine &Msg) {
543     Stream.printError(N, Msg);
544   }
545 
546   // false on error
parseScalarString(yaml::Node * N,StringRef & Result,SmallVectorImpl<char> & Storage)547   bool parseScalarString(yaml::Node *N, StringRef &Result,
548                          SmallVectorImpl<char> &Storage) {
549     yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
550     if (!S) {
551       error(N, "expected string");
552       return false;
553     }
554     Result = S->getValue(Storage);
555     return true;
556   }
557 
558   // false on error
parseScalarBool(yaml::Node * N,bool & Result)559   bool parseScalarBool(yaml::Node *N, bool &Result) {
560     SmallString<5> Storage;
561     StringRef Value;
562     if (!parseScalarString(N, Value, Storage))
563       return false;
564 
565     if (Value.equals_lower("true") || Value.equals_lower("on") ||
566         Value.equals_lower("yes") || Value == "1") {
567       Result = true;
568       return true;
569     } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
570                Value.equals_lower("no") || Value == "0") {
571       Result = false;
572       return true;
573     }
574 
575     error(N, "expected boolean value");
576     return false;
577   }
578 
579   struct KeyStatus {
KeyStatus__anon23f9d4220511::VFSFromYAMLParser::KeyStatus580     KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
581     bool Required;
582     bool Seen;
583   };
584   typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
585 
586   // false on error
checkDuplicateOrUnknownKey(yaml::Node * KeyNode,StringRef Key,DenseMap<StringRef,KeyStatus> & Keys)587   bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
588                                   DenseMap<StringRef, KeyStatus> &Keys) {
589     if (!Keys.count(Key)) {
590       error(KeyNode, "unknown key");
591       return false;
592     }
593     KeyStatus &S = Keys[Key];
594     if (S.Seen) {
595       error(KeyNode, Twine("duplicate key '") + Key + "'");
596       return false;
597     }
598     S.Seen = true;
599     return true;
600   }
601 
602   // false on error
checkMissingKeys(yaml::Node * Obj,DenseMap<StringRef,KeyStatus> & Keys)603   bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
604     for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
605          E = Keys.end();
606          I != E; ++I) {
607       if (I->second.Required && !I->second.Seen) {
608         error(Obj, Twine("missing key '") + I->first + "'");
609         return false;
610       }
611     }
612     return true;
613   }
614 
parseEntry(yaml::Node * N)615   Entry *parseEntry(yaml::Node *N) {
616     yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
617     if (!M) {
618       error(N, "expected mapping node for file or directory entry");
619       return nullptr;
620     }
621 
622     KeyStatusPair Fields[] = {
623       KeyStatusPair("name", true),
624       KeyStatusPair("type", true),
625       KeyStatusPair("contents", false),
626       KeyStatusPair("external-contents", false),
627       KeyStatusPair("use-external-name", false),
628     };
629 
630     DenseMap<StringRef, KeyStatus> Keys(
631         &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
632 
633     bool HasContents = false; // external or otherwise
634     std::vector<Entry *> EntryArrayContents;
635     std::string ExternalContentsPath;
636     std::string Name;
637     FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
638     EntryKind Kind;
639 
640     for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
641          ++I) {
642       StringRef Key;
643       // Reuse the buffer for key and value, since we don't look at key after
644       // parsing value.
645       SmallString<256> Buffer;
646       if (!parseScalarString(I->getKey(), Key, Buffer))
647         return nullptr;
648 
649       if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
650         return nullptr;
651 
652       StringRef Value;
653       if (Key == "name") {
654         if (!parseScalarString(I->getValue(), Value, Buffer))
655           return nullptr;
656         Name = Value;
657       } else if (Key == "type") {
658         if (!parseScalarString(I->getValue(), Value, Buffer))
659           return nullptr;
660         if (Value == "file")
661           Kind = EK_File;
662         else if (Value == "directory")
663           Kind = EK_Directory;
664         else {
665           error(I->getValue(), "unknown value for 'type'");
666           return nullptr;
667         }
668       } else if (Key == "contents") {
669         if (HasContents) {
670           error(I->getKey(),
671                 "entry already has 'contents' or 'external-contents'");
672           return nullptr;
673         }
674         HasContents = true;
675         yaml::SequenceNode *Contents =
676             dyn_cast<yaml::SequenceNode>(I->getValue());
677         if (!Contents) {
678           // FIXME: this is only for directories, what about files?
679           error(I->getValue(), "expected array");
680           return nullptr;
681         }
682 
683         for (yaml::SequenceNode::iterator I = Contents->begin(),
684                                           E = Contents->end();
685              I != E; ++I) {
686           if (Entry *E = parseEntry(&*I))
687             EntryArrayContents.push_back(E);
688           else
689             return nullptr;
690         }
691       } else if (Key == "external-contents") {
692         if (HasContents) {
693           error(I->getKey(),
694                 "entry already has 'contents' or 'external-contents'");
695           return nullptr;
696         }
697         HasContents = true;
698         if (!parseScalarString(I->getValue(), Value, Buffer))
699           return nullptr;
700         ExternalContentsPath = Value;
701       } else if (Key == "use-external-name") {
702         bool Val;
703         if (!parseScalarBool(I->getValue(), Val))
704           return nullptr;
705         UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
706       } else {
707         llvm_unreachable("key missing from Keys");
708       }
709     }
710 
711     if (Stream.failed())
712       return nullptr;
713 
714     // check for missing keys
715     if (!HasContents) {
716       error(N, "missing key 'contents' or 'external-contents'");
717       return nullptr;
718     }
719     if (!checkMissingKeys(N, Keys))
720       return nullptr;
721 
722     // check invalid configuration
723     if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
724       error(N, "'use-external-name' is not supported for directories");
725       return nullptr;
726     }
727 
728     // Remove trailing slash(es), being careful not to remove the root path
729     StringRef Trimmed(Name);
730     size_t RootPathLen = sys::path::root_path(Trimmed).size();
731     while (Trimmed.size() > RootPathLen &&
732            sys::path::is_separator(Trimmed.back()))
733       Trimmed = Trimmed.slice(0, Trimmed.size()-1);
734     // Get the last component
735     StringRef LastComponent = sys::path::filename(Trimmed);
736 
737     Entry *Result = nullptr;
738     switch (Kind) {
739     case EK_File:
740       Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
741                              UseExternalName);
742       break;
743     case EK_Directory:
744       Result = new DirectoryEntry(LastComponent, std::move(EntryArrayContents),
745           Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
746                  0, file_type::directory_file, sys::fs::all_all));
747       break;
748     }
749 
750     StringRef Parent = sys::path::parent_path(Trimmed);
751     if (Parent.empty())
752       return Result;
753 
754     // if 'name' contains multiple components, create implicit directory entries
755     for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
756                                      E = sys::path::rend(Parent);
757          I != E; ++I) {
758       Result = new DirectoryEntry(*I, llvm::makeArrayRef(Result),
759           Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
760                  0, file_type::directory_file, sys::fs::all_all));
761     }
762     return Result;
763   }
764 
765 public:
VFSFromYAMLParser(yaml::Stream & S)766   VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
767 
768   // false on error
parse(yaml::Node * Root,VFSFromYAML * FS)769   bool parse(yaml::Node *Root, VFSFromYAML *FS) {
770     yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
771     if (!Top) {
772       error(Root, "expected mapping node");
773       return false;
774     }
775 
776     KeyStatusPair Fields[] = {
777       KeyStatusPair("version", true),
778       KeyStatusPair("case-sensitive", false),
779       KeyStatusPair("use-external-names", false),
780       KeyStatusPair("roots", true),
781     };
782 
783     DenseMap<StringRef, KeyStatus> Keys(
784         &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
785 
786     // Parse configuration and 'roots'
787     for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
788          ++I) {
789       SmallString<10> KeyBuffer;
790       StringRef Key;
791       if (!parseScalarString(I->getKey(), Key, KeyBuffer))
792         return false;
793 
794       if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
795         return false;
796 
797       if (Key == "roots") {
798         yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
799         if (!Roots) {
800           error(I->getValue(), "expected array");
801           return false;
802         }
803 
804         for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
805              I != E; ++I) {
806           if (Entry *E = parseEntry(&*I))
807             FS->Roots.push_back(E);
808           else
809             return false;
810         }
811       } else if (Key == "version") {
812         StringRef VersionString;
813         SmallString<4> Storage;
814         if (!parseScalarString(I->getValue(), VersionString, Storage))
815           return false;
816         int Version;
817         if (VersionString.getAsInteger<int>(10, Version)) {
818           error(I->getValue(), "expected integer");
819           return false;
820         }
821         if (Version < 0) {
822           error(I->getValue(), "invalid version number");
823           return false;
824         }
825         if (Version != 0) {
826           error(I->getValue(), "version mismatch, expected 0");
827           return false;
828         }
829       } else if (Key == "case-sensitive") {
830         if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
831           return false;
832       } else if (Key == "use-external-names") {
833         if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
834           return false;
835       } else {
836         llvm_unreachable("key missing from Keys");
837       }
838     }
839 
840     if (Stream.failed())
841       return false;
842 
843     if (!checkMissingKeys(Top, Keys))
844       return false;
845     return true;
846   }
847 };
848 } // end of anonymous namespace
849 
~Entry()850 Entry::~Entry() {}
~DirectoryEntry()851 DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
852 
~VFSFromYAML()853 VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
854 
create(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)855 VFSFromYAML *VFSFromYAML::create(std::unique_ptr<MemoryBuffer> Buffer,
856                                  SourceMgr::DiagHandlerTy DiagHandler,
857                                  void *DiagContext,
858                                  IntrusiveRefCntPtr<FileSystem> ExternalFS) {
859 
860   SourceMgr SM;
861   yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
862 
863   SM.setDiagHandler(DiagHandler, DiagContext);
864   yaml::document_iterator DI = Stream.begin();
865   yaml::Node *Root = DI->getRoot();
866   if (DI == Stream.end() || !Root) {
867     SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
868     return nullptr;
869   }
870 
871   VFSFromYAMLParser P(Stream);
872 
873   std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
874   if (!P.parse(Root, FS.get()))
875     return nullptr;
876 
877   return FS.release();
878 }
879 
lookupPath(const Twine & Path_)880 ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
881   SmallString<256> Path;
882   Path_.toVector(Path);
883 
884   // Handle relative paths
885   if (std::error_code EC = sys::fs::make_absolute(Path))
886     return EC;
887 
888   if (Path.empty())
889     return make_error_code(llvm::errc::invalid_argument);
890 
891   sys::path::const_iterator Start = sys::path::begin(Path);
892   sys::path::const_iterator End = sys::path::end(Path);
893   for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
894        I != E; ++I) {
895     ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
896     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
897       return Result;
898   }
899   return make_error_code(llvm::errc::no_such_file_or_directory);
900 }
901 
lookupPath(sys::path::const_iterator Start,sys::path::const_iterator End,Entry * From)902 ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
903                                          sys::path::const_iterator End,
904                                          Entry *From) {
905   if (Start->equals("."))
906     ++Start;
907 
908   // FIXME: handle ..
909   if (CaseSensitive ? !Start->equals(From->getName())
910                     : !Start->equals_lower(From->getName()))
911     // failure to match
912     return make_error_code(llvm::errc::no_such_file_or_directory);
913 
914   ++Start;
915 
916   if (Start == End) {
917     // Match!
918     return From;
919   }
920 
921   DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
922   if (!DE)
923     return make_error_code(llvm::errc::not_a_directory);
924 
925   for (DirectoryEntry::iterator I = DE->contents_begin(),
926                                 E = DE->contents_end();
927        I != E; ++I) {
928     ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
929     if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
930       return Result;
931   }
932   return make_error_code(llvm::errc::no_such_file_or_directory);
933 }
934 
status(const Twine & Path,Entry * E)935 ErrorOr<Status> VFSFromYAML::status(const Twine &Path, Entry *E) {
936   assert(E != nullptr);
937   std::string PathStr(Path.str());
938   if (FileEntry *F = dyn_cast<FileEntry>(E)) {
939     ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
940     assert(!S || S->getName() == F->getExternalContentsPath());
941     if (S && !F->useExternalName(UseExternalNames))
942       S->setName(PathStr);
943     if (S)
944       S->IsVFSMapped = true;
945     return S;
946   } else { // directory
947     DirectoryEntry *DE = cast<DirectoryEntry>(E);
948     Status S = DE->getStatus();
949     S.setName(PathStr);
950     return S;
951   }
952 }
953 
status(const Twine & Path)954 ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
955   ErrorOr<Entry *> Result = lookupPath(Path);
956   if (!Result)
957     return Result.getError();
958   return status(Path, *Result);
959 }
960 
openFileForRead(const Twine & Path)961 ErrorOr<std::unique_ptr<File>> VFSFromYAML::openFileForRead(const Twine &Path) {
962   ErrorOr<Entry *> E = lookupPath(Path);
963   if (!E)
964     return E.getError();
965 
966   FileEntry *F = dyn_cast<FileEntry>(*E);
967   if (!F) // FIXME: errc::not_a_file?
968     return make_error_code(llvm::errc::invalid_argument);
969 
970   auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
971   if (!Result)
972     return Result;
973 
974   if (!F->useExternalName(UseExternalNames))
975     (*Result)->setName(Path.str());
976 
977   return Result;
978 }
979 
980 IntrusiveRefCntPtr<FileSystem>
getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)981 vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
982                     SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
983                     IntrusiveRefCntPtr<FileSystem> ExternalFS) {
984   return VFSFromYAML::create(std::move(Buffer), DiagHandler, DiagContext,
985                              ExternalFS);
986 }
987 
getNextVirtualUniqueID()988 UniqueID vfs::getNextVirtualUniqueID() {
989 #if !defined(_LIBCPP_HAS_NO_THREADS) && defined(__minix)
990   static std::atomic<unsigned> UID;
991 #else
992   static unsigned UID;
993 #endif // !defined(_LIBCPP_HAS_NO_THREADS) && defined(__minix)
994   unsigned ID = ++UID;
995   // The following assumes that uint64_t max will never collide with a real
996   // dev_t value from the OS.
997   return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
998 }
999 
1000 #ifndef NDEBUG
pathHasTraversal(StringRef Path)1001 static bool pathHasTraversal(StringRef Path) {
1002   using namespace llvm::sys;
1003   for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1004     if (Comp == "." || Comp == "..")
1005       return true;
1006   return false;
1007 }
1008 #endif
1009 
addFileMapping(StringRef VirtualPath,StringRef RealPath)1010 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1011   assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1012   assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1013   assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1014   Mappings.emplace_back(VirtualPath, RealPath);
1015 }
1016 
1017 namespace {
1018 class JSONWriter {
1019   llvm::raw_ostream &OS;
1020   SmallVector<StringRef, 16> DirStack;
getDirIndent()1021   inline unsigned getDirIndent() { return 4 * DirStack.size(); }
getFileIndent()1022   inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1023   bool containedIn(StringRef Parent, StringRef Path);
1024   StringRef containedPart(StringRef Parent, StringRef Path);
1025   void startDirectory(StringRef Path);
1026   void endDirectory();
1027   void writeEntry(StringRef VPath, StringRef RPath);
1028 
1029 public:
JSONWriter(llvm::raw_ostream & OS)1030   JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1031   void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> IsCaseSensitive);
1032 };
1033 }
1034 
containedIn(StringRef Parent,StringRef Path)1035 bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
1036   using namespace llvm::sys;
1037   // Compare each path component.
1038   auto IParent = path::begin(Parent), EParent = path::end(Parent);
1039   for (auto IChild = path::begin(Path), EChild = path::end(Path);
1040        IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
1041     if (*IParent != *IChild)
1042       return false;
1043   }
1044   // Have we exhausted the parent path?
1045   return IParent == EParent;
1046 }
1047 
containedPart(StringRef Parent,StringRef Path)1048 StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1049   assert(!Parent.empty());
1050   assert(containedIn(Parent, Path));
1051   return Path.slice(Parent.size() + 1, StringRef::npos);
1052 }
1053 
startDirectory(StringRef Path)1054 void JSONWriter::startDirectory(StringRef Path) {
1055   StringRef Name =
1056       DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
1057   DirStack.push_back(Path);
1058   unsigned Indent = getDirIndent();
1059   OS.indent(Indent) << "{\n";
1060   OS.indent(Indent + 2) << "'type': 'directory',\n";
1061   OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1062   OS.indent(Indent + 2) << "'contents': [\n";
1063 }
1064 
endDirectory()1065 void JSONWriter::endDirectory() {
1066   unsigned Indent = getDirIndent();
1067   OS.indent(Indent + 2) << "]\n";
1068   OS.indent(Indent) << "}";
1069 
1070   DirStack.pop_back();
1071 }
1072 
writeEntry(StringRef VPath,StringRef RPath)1073 void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1074   unsigned Indent = getFileIndent();
1075   OS.indent(Indent) << "{\n";
1076   OS.indent(Indent + 2) << "'type': 'file',\n";
1077   OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1078   OS.indent(Indent + 2) << "'external-contents': \""
1079                         << llvm::yaml::escape(RPath) << "\"\n";
1080   OS.indent(Indent) << "}";
1081 }
1082 
write(ArrayRef<YAMLVFSEntry> Entries,Optional<bool> IsCaseSensitive)1083 void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1084                        Optional<bool> IsCaseSensitive) {
1085   using namespace llvm::sys;
1086 
1087   OS << "{\n"
1088         "  'version': 0,\n";
1089   if (IsCaseSensitive.hasValue())
1090     OS << "  'case-sensitive': '"
1091        << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
1092   OS << "  'roots': [\n";
1093 
1094   if (!Entries.empty()) {
1095     const YAMLVFSEntry &Entry = Entries.front();
1096     startDirectory(path::parent_path(Entry.VPath));
1097     writeEntry(path::filename(Entry.VPath), Entry.RPath);
1098 
1099     for (const auto &Entry : Entries.slice(1)) {
1100       StringRef Dir = path::parent_path(Entry.VPath);
1101       if (Dir == DirStack.back())
1102         OS << ",\n";
1103       else {
1104         while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
1105           OS << "\n";
1106           endDirectory();
1107         }
1108         OS << ",\n";
1109         startDirectory(Dir);
1110       }
1111       writeEntry(path::filename(Entry.VPath), Entry.RPath);
1112     }
1113 
1114     while (!DirStack.empty()) {
1115       OS << "\n";
1116       endDirectory();
1117     }
1118     OS << "\n";
1119   }
1120 
1121   OS << "  ]\n"
1122      << "}\n";
1123 }
1124 
write(llvm::raw_ostream & OS)1125 void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
1126   std::sort(Mappings.begin(), Mappings.end(),
1127             [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
1128     return LHS.VPath < RHS.VPath;
1129   });
1130 
1131   JSONWriter(OS).write(Mappings, IsCaseSensitive);
1132 }
1133 
VFSFromYamlDirIterImpl(const Twine & _Path,VFSFromYAML & FS,DirectoryEntry::iterator Begin,DirectoryEntry::iterator End,std::error_code & EC)1134 VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(const Twine &_Path,
1135                                                VFSFromYAML &FS,
1136                                                DirectoryEntry::iterator Begin,
1137                                                DirectoryEntry::iterator End,
1138                                                std::error_code &EC)
1139     : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
1140   if (Current != End) {
1141     SmallString<128> PathStr(Dir);
1142     llvm::sys::path::append(PathStr, (*Current)->getName());
1143     llvm::ErrorOr<vfs::Status> S = FS.status(PathStr.str());
1144     if (S)
1145       CurrentEntry = *S;
1146     else
1147       EC = S.getError();
1148   }
1149 }
1150 
increment()1151 std::error_code VFSFromYamlDirIterImpl::increment() {
1152   assert(Current != End && "cannot iterate past end");
1153   if (++Current != End) {
1154     SmallString<128> PathStr(Dir);
1155     llvm::sys::path::append(PathStr, (*Current)->getName());
1156     llvm::ErrorOr<vfs::Status> S = FS.status(PathStr.str());
1157     if (!S)
1158       return S.getError();
1159     CurrentEntry = *S;
1160   } else {
1161     CurrentEntry = Status();
1162   }
1163   return std::error_code();
1164 }
1165 
recursive_directory_iterator(FileSystem & FS_,const Twine & Path,std::error_code & EC)1166 vfs::recursive_directory_iterator::recursive_directory_iterator(FileSystem &FS_,
1167                                                            const Twine &Path,
1168                                                            std::error_code &EC)
1169     : FS(&FS_) {
1170   directory_iterator I = FS->dir_begin(Path, EC);
1171   if (!EC && I != directory_iterator()) {
1172     State = std::make_shared<IterState>();
1173     State->push(I);
1174   }
1175 }
1176 
1177 vfs::recursive_directory_iterator &
increment(std::error_code & EC)1178 recursive_directory_iterator::increment(std::error_code &EC) {
1179   assert(FS && State && !State->empty() && "incrementing past end");
1180   assert(State->top()->isStatusKnown() && "non-canonical end iterator");
1181   vfs::directory_iterator End;
1182   if (State->top()->isDirectory()) {
1183     vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
1184     if (EC)
1185       return *this;
1186     if (I != End) {
1187       State->push(I);
1188       return *this;
1189     }
1190   }
1191 
1192   while (!State->empty() && State->top().increment(EC) == End)
1193     State->pop();
1194 
1195   if (State->empty())
1196     State.reset(); // end iterator
1197 
1198   return *this;
1199 }
1200