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