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