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 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 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 46 bool Status::equivalent(const Status &Other) const { 47 return getUniqueID() == Other.getUniqueID(); 48 } 49 bool Status::isDirectory() const { 50 return Type == file_type::directory_file; 51 } 52 bool Status::isRegularFile() const { 53 return Type == file_type::regular_file; 54 } 55 bool Status::isOther() const { 56 return exists() && !isRegularFile() && !isDirectory() && !isSymlink(); 57 } 58 bool Status::isSymlink() const { 59 return Type == file_type::symlink_file; 60 } 61 bool Status::isStatusKnown() const { 62 return Type != file_type::status_error; 63 } 64 bool Status::exists() const { 65 return isStatusKnown() && Type != file_type::file_not_found; 66 } 67 68 File::~File() {} 69 70 FileSystem::~FileSystem() {} 71 72 ErrorOr<std::unique_ptr<MemoryBuffer>> 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; 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 107 RealFile::~RealFile() { close(); } 108 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>> 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 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 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 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>> 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 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: 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 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 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 //===-----------------------------------------------------------------------===/ 228 OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) { 229 pushOverlay(BaseFS); 230 } 231 232 void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) { 233 FSList.push_back(FS); 234 } 235 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>> 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 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 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 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 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: 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 315 std::error_code increment() override { return incrementImpl(false); } 316 }; 317 } // end anonymous namespace 318 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. 336 static inline StringRef getEmptyKey() { return StringRef(""); } 337 static inline StringRef getTombstoneKey() { return StringRef(); } 338 static unsigned getHashValue(StringRef Val) { return HashString(Val); } 339 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(); 357 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {} 358 StringRef getName() const { return Name; } 359 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(); 368 DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S) 369 : Entry(EK_Directory, Name), Contents(std::move(Contents)), 370 S(std::move(S)) {} 371 Status getStatus() { return S; } 372 typedef std::vector<Entry *>::iterator iterator; 373 iterator contents_begin() { return Contents.begin(); } 374 iterator contents_end() { return Contents.end(); } 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: 389 FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName) 390 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath), 391 UseName(UseName) {} 392 StringRef getExternalContentsPath() const { return ExternalContentsPath; } 393 /// \brief whether to use the external path as the name for this file. 394 bool useExternalName(bool GlobalUseExternalName) const { 395 return UseName == NK_NotSet ? GlobalUseExternalName 396 : (UseName == NK_External); 397 } 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: 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 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 542 void error(yaml::Node *N, const Twine &Msg) { 543 Stream.printError(N, Msg); 544 } 545 546 // false on error 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 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 { 580 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 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 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 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: 766 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {} 767 768 // false on error 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 850 Entry::~Entry() {} 851 DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); } 852 853 VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); } 854 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 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 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 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 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 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> 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 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 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 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; 1021 inline unsigned getDirIndent() { return 4 * DirStack.size(); } 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: 1030 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {} 1031 void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> IsCaseSensitive); 1032 }; 1033 } 1034 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 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 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 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 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 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 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 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 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 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 & 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