1 //===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===// 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 // 10 // This file implements the VirtualFileSystem interface. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Support/VirtualFileSystem.h" 15 #include "llvm/ADT/ArrayRef.h" 16 #include "llvm/ADT/DenseMap.h" 17 #include "llvm/ADT/IntrusiveRefCntPtr.h" 18 #include "llvm/ADT/None.h" 19 #include "llvm/ADT/Optional.h" 20 #include "llvm/ADT/STLExtras.h" 21 #include "llvm/ADT/SmallString.h" 22 #include "llvm/ADT/SmallVector.h" 23 #include "llvm/ADT/StringRef.h" 24 #include "llvm/ADT/StringSet.h" 25 #include "llvm/ADT/Twine.h" 26 #include "llvm/ADT/iterator_range.h" 27 #include "llvm/Config/llvm-config.h" 28 #include "llvm/Support/Casting.h" 29 #include "llvm/Support/Chrono.h" 30 #include "llvm/Support/Compiler.h" 31 #include "llvm/Support/Debug.h" 32 #include "llvm/Support/Errc.h" 33 #include "llvm/Support/ErrorHandling.h" 34 #include "llvm/Support/ErrorOr.h" 35 #include "llvm/Support/FileSystem.h" 36 #include "llvm/Support/MemoryBuffer.h" 37 #include "llvm/Support/Path.h" 38 #include "llvm/Support/Process.h" 39 #include "llvm/Support/SMLoc.h" 40 #include "llvm/Support/SourceMgr.h" 41 #include "llvm/Support/YAMLParser.h" 42 #include "llvm/Support/raw_ostream.h" 43 #include <algorithm> 44 #include <atomic> 45 #include <cassert> 46 #include <cstdint> 47 #include <iterator> 48 #include <limits> 49 #include <map> 50 #include <memory> 51 #include <mutex> 52 #include <string> 53 #include <system_error> 54 #include <utility> 55 #include <vector> 56 57 using namespace llvm; 58 using namespace llvm::vfs; 59 60 using llvm::sys::fs::file_status; 61 using llvm::sys::fs::file_type; 62 using llvm::sys::fs::perms; 63 using llvm::sys::fs::UniqueID; 64 65 Status::Status(const file_status &Status) 66 : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()), 67 User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()), 68 Type(Status.type()), Perms(Status.permissions()) {} 69 70 Status::Status(StringRef Name, UniqueID UID, sys::TimePoint<> MTime, 71 uint32_t User, uint32_t Group, uint64_t Size, file_type Type, 72 perms Perms) 73 : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size), 74 Type(Type), Perms(Perms) {} 75 76 Status Status::copyWithNewName(const Status &In, StringRef NewName) { 77 return Status(NewName, In.getUniqueID(), In.getLastModificationTime(), 78 In.getUser(), In.getGroup(), In.getSize(), In.getType(), 79 In.getPermissions()); 80 } 81 82 Status Status::copyWithNewName(const file_status &In, StringRef NewName) { 83 return Status(NewName, In.getUniqueID(), In.getLastModificationTime(), 84 In.getUser(), In.getGroup(), In.getSize(), In.type(), 85 In.permissions()); 86 } 87 88 bool Status::equivalent(const Status &Other) const { 89 assert(isStatusKnown() && Other.isStatusKnown()); 90 return getUniqueID() == Other.getUniqueID(); 91 } 92 93 bool Status::isDirectory() const { return Type == file_type::directory_file; } 94 95 bool Status::isRegularFile() const { return Type == file_type::regular_file; } 96 97 bool Status::isOther() const { 98 return exists() && !isRegularFile() && !isDirectory() && !isSymlink(); 99 } 100 101 bool Status::isSymlink() const { return Type == file_type::symlink_file; } 102 103 bool Status::isStatusKnown() const { return Type != file_type::status_error; } 104 105 bool Status::exists() const { 106 return isStatusKnown() && Type != file_type::file_not_found; 107 } 108 109 File::~File() = default; 110 111 FileSystem::~FileSystem() = default; 112 113 ErrorOr<std::unique_ptr<MemoryBuffer>> 114 FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize, 115 bool RequiresNullTerminator, bool IsVolatile) { 116 auto F = openFileForRead(Name); 117 if (!F) 118 return F.getError(); 119 120 return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile); 121 } 122 123 std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const { 124 if (llvm::sys::path::is_absolute(Path)) 125 return {}; 126 127 auto WorkingDir = getCurrentWorkingDirectory(); 128 if (!WorkingDir) 129 return WorkingDir.getError(); 130 131 return llvm::sys::fs::make_absolute(WorkingDir.get(), Path); 132 } 133 134 std::error_code FileSystem::getRealPath(const Twine &Path, 135 SmallVectorImpl<char> &Output, 136 bool ExpandTilde) const { 137 return errc::operation_not_permitted; 138 } 139 140 std::error_code FileSystem::isLocal(const Twine &Path, bool &Result) { 141 return errc::operation_not_permitted; 142 } 143 144 bool FileSystem::exists(const Twine &Path) { 145 auto Status = status(Path); 146 return Status && Status->exists(); 147 } 148 149 #ifndef NDEBUG 150 static bool isTraversalComponent(StringRef Component) { 151 return Component.equals("..") || Component.equals("."); 152 } 153 154 static bool pathHasTraversal(StringRef Path) { 155 using namespace llvm::sys; 156 157 for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path))) 158 if (isTraversalComponent(Comp)) 159 return true; 160 return false; 161 } 162 #endif 163 164 //===-----------------------------------------------------------------------===/ 165 // RealFileSystem implementation 166 //===-----------------------------------------------------------------------===/ 167 168 namespace { 169 170 /// Wrapper around a raw file descriptor. 171 class RealFile : public File { 172 friend class RealFileSystem; 173 174 int FD; 175 Status S; 176 std::string RealName; 177 178 RealFile(int FD, StringRef NewName, StringRef NewRealPathName) 179 : FD(FD), S(NewName, {}, {}, {}, {}, {}, 180 llvm::sys::fs::file_type::status_error, {}), 181 RealName(NewRealPathName.str()) { 182 assert(FD >= 0 && "Invalid or inactive file descriptor"); 183 } 184 185 public: 186 ~RealFile() override; 187 188 ErrorOr<Status> status() override; 189 ErrorOr<std::string> getName() override; 190 ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name, 191 int64_t FileSize, 192 bool RequiresNullTerminator, 193 bool IsVolatile) override; 194 std::error_code close() override; 195 }; 196 197 } // namespace 198 199 RealFile::~RealFile() { close(); } 200 201 ErrorOr<Status> RealFile::status() { 202 assert(FD != -1 && "cannot stat closed file"); 203 if (!S.isStatusKnown()) { 204 file_status RealStatus; 205 if (std::error_code EC = sys::fs::status(FD, RealStatus)) 206 return EC; 207 S = Status::copyWithNewName(RealStatus, S.getName()); 208 } 209 return S; 210 } 211 212 ErrorOr<std::string> RealFile::getName() { 213 return RealName.empty() ? S.getName().str() : RealName; 214 } 215 216 ErrorOr<std::unique_ptr<MemoryBuffer>> 217 RealFile::getBuffer(const Twine &Name, int64_t FileSize, 218 bool RequiresNullTerminator, bool IsVolatile) { 219 assert(FD != -1 && "cannot get buffer for closed file"); 220 return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator, 221 IsVolatile); 222 } 223 224 std::error_code RealFile::close() { 225 std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD); 226 FD = -1; 227 return EC; 228 } 229 230 namespace { 231 232 /// The file system according to your operating system. 233 class RealFileSystem : public FileSystem { 234 public: 235 ErrorOr<Status> status(const Twine &Path) override; 236 ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override; 237 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; 238 239 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; 240 std::error_code setCurrentWorkingDirectory(const Twine &Path) override; 241 std::error_code isLocal(const Twine &Path, bool &Result) override; 242 std::error_code getRealPath(const Twine &Path, SmallVectorImpl<char> &Output, 243 bool ExpandTilde = false) const override; 244 245 private: 246 mutable std::mutex CWDMutex; 247 mutable std::string CWDCache; 248 }; 249 250 } // namespace 251 252 ErrorOr<Status> RealFileSystem::status(const Twine &Path) { 253 sys::fs::file_status RealStatus; 254 if (std::error_code EC = sys::fs::status(Path, RealStatus)) 255 return EC; 256 return Status::copyWithNewName(RealStatus, Path.str()); 257 } 258 259 ErrorOr<std::unique_ptr<File>> 260 RealFileSystem::openFileForRead(const Twine &Name) { 261 int FD; 262 SmallString<256> RealName; 263 if (std::error_code EC = 264 sys::fs::openFileForRead(Name, FD, sys::fs::OF_None, &RealName)) 265 return EC; 266 return std::unique_ptr<File>(new RealFile(FD, Name.str(), RealName.str())); 267 } 268 269 llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const { 270 std::lock_guard<std::mutex> Lock(CWDMutex); 271 if (!CWDCache.empty()) 272 return CWDCache; 273 SmallString<256> Dir; 274 if (std::error_code EC = llvm::sys::fs::current_path(Dir)) 275 return EC; 276 CWDCache = Dir.str(); 277 return CWDCache; 278 } 279 280 std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) { 281 // FIXME: chdir is thread hostile; on the other hand, creating the same 282 // behavior as chdir is complex: chdir resolves the path once, thus 283 // guaranteeing that all subsequent relative path operations work 284 // on the same path the original chdir resulted in. This makes a 285 // difference for example on network filesystems, where symlinks might be 286 // switched during runtime of the tool. Fixing this depends on having a 287 // file system abstraction that allows openat() style interactions. 288 if (auto EC = llvm::sys::fs::set_current_path(Path)) 289 return EC; 290 291 // Invalidate cache. 292 std::lock_guard<std::mutex> Lock(CWDMutex); 293 CWDCache.clear(); 294 return std::error_code(); 295 } 296 297 std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) { 298 return llvm::sys::fs::is_local(Path, Result); 299 } 300 301 std::error_code RealFileSystem::getRealPath(const Twine &Path, 302 SmallVectorImpl<char> &Output, 303 bool ExpandTilde) const { 304 return llvm::sys::fs::real_path(Path, Output); 305 } 306 307 IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() { 308 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem(); 309 return FS; 310 } 311 312 namespace { 313 314 class RealFSDirIter : public llvm::vfs::detail::DirIterImpl { 315 llvm::sys::fs::directory_iterator Iter; 316 317 public: 318 RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) { 319 if (Iter != llvm::sys::fs::directory_iterator()) 320 CurrentEntry = directory_entry(Iter->path(), Iter->type()); 321 } 322 323 std::error_code increment() override { 324 std::error_code EC; 325 Iter.increment(EC); 326 CurrentEntry = (Iter == llvm::sys::fs::directory_iterator()) 327 ? directory_entry() 328 : directory_entry(Iter->path(), Iter->type()); 329 return EC; 330 } 331 }; 332 333 } // namespace 334 335 directory_iterator RealFileSystem::dir_begin(const Twine &Dir, 336 std::error_code &EC) { 337 return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC)); 338 } 339 340 //===-----------------------------------------------------------------------===/ 341 // OverlayFileSystem implementation 342 //===-----------------------------------------------------------------------===/ 343 344 OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) { 345 FSList.push_back(std::move(BaseFS)); 346 } 347 348 void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) { 349 FSList.push_back(FS); 350 // Synchronize added file systems by duplicating the working directory from 351 // the first one in the list. 352 FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get()); 353 } 354 355 ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) { 356 // FIXME: handle symlinks that cross file systems 357 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) { 358 ErrorOr<Status> Status = (*I)->status(Path); 359 if (Status || Status.getError() != llvm::errc::no_such_file_or_directory) 360 return Status; 361 } 362 return make_error_code(llvm::errc::no_such_file_or_directory); 363 } 364 365 ErrorOr<std::unique_ptr<File>> 366 OverlayFileSystem::openFileForRead(const llvm::Twine &Path) { 367 // FIXME: handle symlinks that cross file systems 368 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) { 369 auto Result = (*I)->openFileForRead(Path); 370 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) 371 return Result; 372 } 373 return make_error_code(llvm::errc::no_such_file_or_directory); 374 } 375 376 llvm::ErrorOr<std::string> 377 OverlayFileSystem::getCurrentWorkingDirectory() const { 378 // All file systems are synchronized, just take the first working directory. 379 return FSList.front()->getCurrentWorkingDirectory(); 380 } 381 382 std::error_code 383 OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) { 384 for (auto &FS : FSList) 385 if (std::error_code EC = FS->setCurrentWorkingDirectory(Path)) 386 return EC; 387 return {}; 388 } 389 390 std::error_code OverlayFileSystem::isLocal(const Twine &Path, bool &Result) { 391 for (auto &FS : FSList) 392 if (FS->exists(Path)) 393 return FS->isLocal(Path, Result); 394 return errc::no_such_file_or_directory; 395 } 396 397 std::error_code OverlayFileSystem::getRealPath(const Twine &Path, 398 SmallVectorImpl<char> &Output, 399 bool ExpandTilde) const { 400 for (auto &FS : FSList) 401 if (FS->exists(Path)) 402 return FS->getRealPath(Path, Output, ExpandTilde); 403 return errc::no_such_file_or_directory; 404 } 405 406 llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default; 407 408 namespace { 409 410 class OverlayFSDirIterImpl : public llvm::vfs::detail::DirIterImpl { 411 OverlayFileSystem &Overlays; 412 std::string Path; 413 OverlayFileSystem::iterator CurrentFS; 414 directory_iterator CurrentDirIter; 415 llvm::StringSet<> SeenNames; 416 417 std::error_code incrementFS() { 418 assert(CurrentFS != Overlays.overlays_end() && "incrementing past end"); 419 ++CurrentFS; 420 for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) { 421 std::error_code EC; 422 CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC); 423 if (EC && EC != errc::no_such_file_or_directory) 424 return EC; 425 if (CurrentDirIter != directory_iterator()) 426 break; // found 427 } 428 return {}; 429 } 430 431 std::error_code incrementDirIter(bool IsFirstTime) { 432 assert((IsFirstTime || CurrentDirIter != directory_iterator()) && 433 "incrementing past end"); 434 std::error_code EC; 435 if (!IsFirstTime) 436 CurrentDirIter.increment(EC); 437 if (!EC && CurrentDirIter == directory_iterator()) 438 EC = incrementFS(); 439 return EC; 440 } 441 442 std::error_code incrementImpl(bool IsFirstTime) { 443 while (true) { 444 std::error_code EC = incrementDirIter(IsFirstTime); 445 if (EC || CurrentDirIter == directory_iterator()) { 446 CurrentEntry = directory_entry(); 447 return EC; 448 } 449 CurrentEntry = *CurrentDirIter; 450 StringRef Name = llvm::sys::path::filename(CurrentEntry.path()); 451 if (SeenNames.insert(Name).second) 452 return EC; // name not seen before 453 } 454 llvm_unreachable("returned above"); 455 } 456 457 public: 458 OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS, 459 std::error_code &EC) 460 : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) { 461 CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC); 462 EC = incrementImpl(true); 463 } 464 465 std::error_code increment() override { return incrementImpl(false); } 466 }; 467 468 } // namespace 469 470 directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir, 471 std::error_code &EC) { 472 return directory_iterator( 473 std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC)); 474 } 475 476 namespace llvm { 477 namespace vfs { 478 479 namespace detail { 480 481 enum InMemoryNodeKind { IME_File, IME_Directory, IME_HardLink }; 482 483 /// The in memory file system is a tree of Nodes. Every node can either be a 484 /// file , hardlink or a directory. 485 class InMemoryNode { 486 InMemoryNodeKind Kind; 487 std::string FileName; 488 489 public: 490 InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind) 491 : Kind(Kind), FileName(llvm::sys::path::filename(FileName)) {} 492 virtual ~InMemoryNode() = default; 493 494 /// Get the filename of this node (the name without the directory part). 495 StringRef getFileName() const { return FileName; } 496 InMemoryNodeKind getKind() const { return Kind; } 497 virtual std::string toString(unsigned Indent) const = 0; 498 }; 499 500 class InMemoryFile : public InMemoryNode { 501 Status Stat; 502 std::unique_ptr<llvm::MemoryBuffer> Buffer; 503 504 public: 505 InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer) 506 : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)), 507 Buffer(std::move(Buffer)) {} 508 509 /// Return the \p Status for this node. \p RequestedName should be the name 510 /// through which the caller referred to this node. It will override 511 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile. 512 Status getStatus(StringRef RequestedName) const { 513 return Status::copyWithNewName(Stat, RequestedName); 514 } 515 llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); } 516 517 std::string toString(unsigned Indent) const override { 518 return (std::string(Indent, ' ') + Stat.getName() + "\n").str(); 519 } 520 521 static bool classof(const InMemoryNode *N) { 522 return N->getKind() == IME_File; 523 } 524 }; 525 526 namespace { 527 528 class InMemoryHardLink : public InMemoryNode { 529 const InMemoryFile &ResolvedFile; 530 531 public: 532 InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile) 533 : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {} 534 const InMemoryFile &getResolvedFile() const { return ResolvedFile; } 535 536 std::string toString(unsigned Indent) const override { 537 return std::string(Indent, ' ') + "HardLink to -> " + 538 ResolvedFile.toString(0); 539 } 540 541 static bool classof(const InMemoryNode *N) { 542 return N->getKind() == IME_HardLink; 543 } 544 }; 545 546 /// Adapt a InMemoryFile for VFS' File interface. The goal is to make 547 /// \p InMemoryFileAdaptor mimic as much as possible the behavior of 548 /// \p RealFile. 549 class InMemoryFileAdaptor : public File { 550 const InMemoryFile &Node; 551 /// The name to use when returning a Status for this file. 552 std::string RequestedName; 553 554 public: 555 explicit InMemoryFileAdaptor(const InMemoryFile &Node, 556 std::string RequestedName) 557 : Node(Node), RequestedName(std::move(RequestedName)) {} 558 559 llvm::ErrorOr<Status> status() override { 560 return Node.getStatus(RequestedName); 561 } 562 563 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 564 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 565 bool IsVolatile) override { 566 llvm::MemoryBuffer *Buf = Node.getBuffer(); 567 return llvm::MemoryBuffer::getMemBuffer( 568 Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator); 569 } 570 571 std::error_code close() override { return {}; } 572 }; 573 } // namespace 574 575 class InMemoryDirectory : public InMemoryNode { 576 Status Stat; 577 llvm::StringMap<std::unique_ptr<InMemoryNode>> Entries; 578 579 public: 580 InMemoryDirectory(Status Stat) 581 : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {} 582 583 /// Return the \p Status for this node. \p RequestedName should be the name 584 /// through which the caller referred to this node. It will override 585 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile. 586 Status getStatus(StringRef RequestedName) const { 587 return Status::copyWithNewName(Stat, RequestedName); 588 } 589 InMemoryNode *getChild(StringRef Name) { 590 auto I = Entries.find(Name); 591 if (I != Entries.end()) 592 return I->second.get(); 593 return nullptr; 594 } 595 596 InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) { 597 return Entries.insert(make_pair(Name, std::move(Child))) 598 .first->second.get(); 599 } 600 601 using const_iterator = decltype(Entries)::const_iterator; 602 603 const_iterator begin() const { return Entries.begin(); } 604 const_iterator end() const { return Entries.end(); } 605 606 std::string toString(unsigned Indent) const override { 607 std::string Result = 608 (std::string(Indent, ' ') + Stat.getName() + "\n").str(); 609 for (const auto &Entry : Entries) 610 Result += Entry.second->toString(Indent + 2); 611 return Result; 612 } 613 614 static bool classof(const InMemoryNode *N) { 615 return N->getKind() == IME_Directory; 616 } 617 }; 618 619 namespace { 620 Status getNodeStatus(const InMemoryNode *Node, StringRef RequestedName) { 621 if (auto Dir = dyn_cast<detail::InMemoryDirectory>(Node)) 622 return Dir->getStatus(RequestedName); 623 if (auto File = dyn_cast<detail::InMemoryFile>(Node)) 624 return File->getStatus(RequestedName); 625 if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) 626 return Link->getResolvedFile().getStatus(RequestedName); 627 llvm_unreachable("Unknown node type"); 628 } 629 } // namespace 630 } // namespace detail 631 632 InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths) 633 : Root(new detail::InMemoryDirectory( 634 Status("", getNextVirtualUniqueID(), llvm::sys::TimePoint<>(), 0, 0, 635 0, llvm::sys::fs::file_type::directory_file, 636 llvm::sys::fs::perms::all_all))), 637 UseNormalizedPaths(UseNormalizedPaths) {} 638 639 InMemoryFileSystem::~InMemoryFileSystem() = default; 640 641 std::string InMemoryFileSystem::toString() const { 642 return Root->toString(/*Indent=*/0); 643 } 644 645 bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime, 646 std::unique_ptr<llvm::MemoryBuffer> Buffer, 647 Optional<uint32_t> User, 648 Optional<uint32_t> Group, 649 Optional<llvm::sys::fs::file_type> Type, 650 Optional<llvm::sys::fs::perms> Perms, 651 const detail::InMemoryFile *HardLinkTarget) { 652 SmallString<128> Path; 653 P.toVector(Path); 654 655 // Fix up relative paths. This just prepends the current working directory. 656 std::error_code EC = makeAbsolute(Path); 657 assert(!EC); 658 (void)EC; 659 660 if (useNormalizedPaths()) 661 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 662 663 if (Path.empty()) 664 return false; 665 666 detail::InMemoryDirectory *Dir = Root.get(); 667 auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path); 668 const auto ResolvedUser = User.getValueOr(0); 669 const auto ResolvedGroup = Group.getValueOr(0); 670 const auto ResolvedType = Type.getValueOr(sys::fs::file_type::regular_file); 671 const auto ResolvedPerms = Perms.getValueOr(sys::fs::all_all); 672 assert(!(HardLinkTarget && Buffer) && "HardLink cannot have a buffer"); 673 // Any intermediate directories we create should be accessible by 674 // the owner, even if Perms says otherwise for the final path. 675 const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all; 676 while (true) { 677 StringRef Name = *I; 678 detail::InMemoryNode *Node = Dir->getChild(Name); 679 ++I; 680 if (!Node) { 681 if (I == E) { 682 // End of the path. 683 std::unique_ptr<detail::InMemoryNode> Child; 684 if (HardLinkTarget) 685 Child.reset(new detail::InMemoryHardLink(P.str(), *HardLinkTarget)); 686 else { 687 // Create a new file or directory. 688 Status Stat(P.str(), getNextVirtualUniqueID(), 689 llvm::sys::toTimePoint(ModificationTime), ResolvedUser, 690 ResolvedGroup, Buffer->getBufferSize(), ResolvedType, 691 ResolvedPerms); 692 if (ResolvedType == sys::fs::file_type::directory_file) { 693 Child.reset(new detail::InMemoryDirectory(std::move(Stat))); 694 } else { 695 Child.reset( 696 new detail::InMemoryFile(std::move(Stat), std::move(Buffer))); 697 } 698 } 699 Dir->addChild(Name, std::move(Child)); 700 return true; 701 } 702 703 // Create a new directory. Use the path up to here. 704 Status Stat( 705 StringRef(Path.str().begin(), Name.end() - Path.str().begin()), 706 getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime), 707 ResolvedUser, ResolvedGroup, 0, sys::fs::file_type::directory_file, 708 NewDirectoryPerms); 709 Dir = cast<detail::InMemoryDirectory>(Dir->addChild( 710 Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat)))); 711 continue; 712 } 713 714 if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) { 715 Dir = NewDir; 716 } else { 717 assert((isa<detail::InMemoryFile>(Node) || 718 isa<detail::InMemoryHardLink>(Node)) && 719 "Must be either file, hardlink or directory!"); 720 721 // Trying to insert a directory in place of a file. 722 if (I != E) 723 return false; 724 725 // Return false only if the new file is different from the existing one. 726 if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) { 727 return Link->getResolvedFile().getBuffer()->getBuffer() == 728 Buffer->getBuffer(); 729 } 730 return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() == 731 Buffer->getBuffer(); 732 } 733 } 734 } 735 736 bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime, 737 std::unique_ptr<llvm::MemoryBuffer> Buffer, 738 Optional<uint32_t> User, 739 Optional<uint32_t> Group, 740 Optional<llvm::sys::fs::file_type> Type, 741 Optional<llvm::sys::fs::perms> Perms) { 742 return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type, 743 Perms, /*HardLinkTarget=*/nullptr); 744 } 745 746 bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime, 747 llvm::MemoryBuffer *Buffer, 748 Optional<uint32_t> User, 749 Optional<uint32_t> Group, 750 Optional<llvm::sys::fs::file_type> Type, 751 Optional<llvm::sys::fs::perms> Perms) { 752 return addFile(P, ModificationTime, 753 llvm::MemoryBuffer::getMemBuffer( 754 Buffer->getBuffer(), Buffer->getBufferIdentifier()), 755 std::move(User), std::move(Group), std::move(Type), 756 std::move(Perms)); 757 } 758 759 static ErrorOr<const detail::InMemoryNode *> 760 lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir, 761 const Twine &P) { 762 SmallString<128> Path; 763 P.toVector(Path); 764 765 // Fix up relative paths. This just prepends the current working directory. 766 std::error_code EC = FS.makeAbsolute(Path); 767 assert(!EC); 768 (void)EC; 769 770 if (FS.useNormalizedPaths()) 771 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 772 773 if (Path.empty()) 774 return Dir; 775 776 auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path); 777 while (true) { 778 detail::InMemoryNode *Node = Dir->getChild(*I); 779 ++I; 780 if (!Node) 781 return errc::no_such_file_or_directory; 782 783 // Return the file if it's at the end of the path. 784 if (auto File = dyn_cast<detail::InMemoryFile>(Node)) { 785 if (I == E) 786 return File; 787 return errc::no_such_file_or_directory; 788 } 789 790 // If Node is HardLink then return the resolved file. 791 if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) { 792 if (I == E) 793 return &File->getResolvedFile(); 794 return errc::no_such_file_or_directory; 795 } 796 // Traverse directories. 797 Dir = cast<detail::InMemoryDirectory>(Node); 798 if (I == E) 799 return Dir; 800 } 801 } 802 803 bool InMemoryFileSystem::addHardLink(const Twine &FromPath, 804 const Twine &ToPath) { 805 auto FromNode = lookupInMemoryNode(*this, Root.get(), FromPath); 806 auto ToNode = lookupInMemoryNode(*this, Root.get(), ToPath); 807 // FromPath must not have been added before. ToPath must have been added 808 // before. Resolved ToPath must be a File. 809 if (!ToNode || FromNode || !isa<detail::InMemoryFile>(*ToNode)) 810 return false; 811 return this->addFile(FromPath, 0, nullptr, None, None, None, None, 812 cast<detail::InMemoryFile>(*ToNode)); 813 } 814 815 llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) { 816 auto Node = lookupInMemoryNode(*this, Root.get(), Path); 817 if (Node) 818 return detail::getNodeStatus(*Node, Path.str()); 819 return Node.getError(); 820 } 821 822 llvm::ErrorOr<std::unique_ptr<File>> 823 InMemoryFileSystem::openFileForRead(const Twine &Path) { 824 auto Node = lookupInMemoryNode(*this, Root.get(), Path); 825 if (!Node) 826 return Node.getError(); 827 828 // When we have a file provide a heap-allocated wrapper for the memory buffer 829 // to match the ownership semantics for File. 830 if (auto *F = dyn_cast<detail::InMemoryFile>(*Node)) 831 return std::unique_ptr<File>( 832 new detail::InMemoryFileAdaptor(*F, Path.str())); 833 834 // FIXME: errc::not_a_file? 835 return make_error_code(llvm::errc::invalid_argument); 836 } 837 838 namespace { 839 840 /// Adaptor from InMemoryDir::iterator to directory_iterator. 841 class InMemoryDirIterator : public llvm::vfs::detail::DirIterImpl { 842 detail::InMemoryDirectory::const_iterator I; 843 detail::InMemoryDirectory::const_iterator E; 844 std::string RequestedDirName; 845 846 void setCurrentEntry() { 847 if (I != E) { 848 SmallString<256> Path(RequestedDirName); 849 llvm::sys::path::append(Path, I->second->getFileName()); 850 sys::fs::file_type Type; 851 switch (I->second->getKind()) { 852 case detail::IME_File: 853 case detail::IME_HardLink: 854 Type = sys::fs::file_type::regular_file; 855 break; 856 case detail::IME_Directory: 857 Type = sys::fs::file_type::directory_file; 858 break; 859 } 860 CurrentEntry = directory_entry(Path.str(), Type); 861 } else { 862 // When we're at the end, make CurrentEntry invalid and DirIterImpl will 863 // do the rest. 864 CurrentEntry = directory_entry(); 865 } 866 } 867 868 public: 869 InMemoryDirIterator() = default; 870 871 explicit InMemoryDirIterator(const detail::InMemoryDirectory &Dir, 872 std::string RequestedDirName) 873 : I(Dir.begin()), E(Dir.end()), 874 RequestedDirName(std::move(RequestedDirName)) { 875 setCurrentEntry(); 876 } 877 878 std::error_code increment() override { 879 ++I; 880 setCurrentEntry(); 881 return {}; 882 } 883 }; 884 885 } // namespace 886 887 directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir, 888 std::error_code &EC) { 889 auto Node = lookupInMemoryNode(*this, Root.get(), Dir); 890 if (!Node) { 891 EC = Node.getError(); 892 return directory_iterator(std::make_shared<InMemoryDirIterator>()); 893 } 894 895 if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node)) 896 return directory_iterator( 897 std::make_shared<InMemoryDirIterator>(*DirNode, Dir.str())); 898 899 EC = make_error_code(llvm::errc::not_a_directory); 900 return directory_iterator(std::make_shared<InMemoryDirIterator>()); 901 } 902 903 std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) { 904 SmallString<128> Path; 905 P.toVector(Path); 906 907 // Fix up relative paths. This just prepends the current working directory. 908 std::error_code EC = makeAbsolute(Path); 909 assert(!EC); 910 (void)EC; 911 912 if (useNormalizedPaths()) 913 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 914 915 if (!Path.empty()) 916 WorkingDirectory = Path.str(); 917 return {}; 918 } 919 920 std::error_code InMemoryFileSystem::getRealPath(const Twine &Path, 921 SmallVectorImpl<char> &Output, 922 bool ExpandTilde) const { 923 auto CWD = getCurrentWorkingDirectory(); 924 if (!CWD || CWD->empty()) 925 return errc::operation_not_permitted; 926 Path.toVector(Output); 927 if (auto EC = makeAbsolute(Output)) 928 return EC; 929 llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true); 930 return {}; 931 } 932 933 std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) { 934 Result = false; 935 return {}; 936 } 937 938 } // namespace vfs 939 } // namespace llvm 940 941 //===-----------------------------------------------------------------------===/ 942 // RedirectingFileSystem implementation 943 //===-----------------------------------------------------------------------===/ 944 945 namespace { 946 947 enum EntryKind { EK_Directory, EK_File }; 948 949 /// A single file or directory in the VFS. 950 class Entry { 951 EntryKind Kind; 952 std::string Name; 953 954 public: 955 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {} 956 virtual ~Entry() = default; 957 958 StringRef getName() const { return Name; } 959 EntryKind getKind() const { return Kind; } 960 }; 961 962 class RedirectingDirectoryEntry : public Entry { 963 std::vector<std::unique_ptr<Entry>> Contents; 964 Status S; 965 966 public: 967 RedirectingDirectoryEntry(StringRef Name, 968 std::vector<std::unique_ptr<Entry>> Contents, 969 Status S) 970 : Entry(EK_Directory, Name), Contents(std::move(Contents)), 971 S(std::move(S)) {} 972 RedirectingDirectoryEntry(StringRef Name, Status S) 973 : Entry(EK_Directory, Name), S(std::move(S)) {} 974 975 Status getStatus() { return S; } 976 977 void addContent(std::unique_ptr<Entry> Content) { 978 Contents.push_back(std::move(Content)); 979 } 980 981 Entry *getLastContent() const { return Contents.back().get(); } 982 983 using iterator = decltype(Contents)::iterator; 984 985 iterator contents_begin() { return Contents.begin(); } 986 iterator contents_end() { return Contents.end(); } 987 988 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; } 989 }; 990 991 class RedirectingFileEntry : public Entry { 992 public: 993 enum NameKind { NK_NotSet, NK_External, NK_Virtual }; 994 995 private: 996 std::string ExternalContentsPath; 997 NameKind UseName; 998 999 public: 1000 RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath, 1001 NameKind UseName) 1002 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath), 1003 UseName(UseName) {} 1004 1005 StringRef getExternalContentsPath() const { return ExternalContentsPath; } 1006 1007 /// whether to use the external path as the name for this file. 1008 bool useExternalName(bool GlobalUseExternalName) const { 1009 return UseName == NK_NotSet ? GlobalUseExternalName 1010 : (UseName == NK_External); 1011 } 1012 1013 NameKind getUseName() const { return UseName; } 1014 1015 static bool classof(const Entry *E) { return E->getKind() == EK_File; } 1016 }; 1017 1018 // FIXME: reuse implementation common with OverlayFSDirIterImpl as these 1019 // iterators are conceptually similar. 1020 class VFSFromYamlDirIterImpl : public llvm::vfs::detail::DirIterImpl { 1021 std::string Dir; 1022 RedirectingDirectoryEntry::iterator Current, End; 1023 1024 // To handle 'fallthrough' mode we need to iterate at first through 1025 // RedirectingDirectoryEntry and then through ExternalFS. These operations are 1026 // done sequentially, we just need to keep a track of what kind of iteration 1027 // we are currently performing. 1028 1029 /// Flag telling if we should iterate through ExternalFS or stop at the last 1030 /// RedirectingDirectoryEntry::iterator. 1031 bool IterateExternalFS; 1032 /// Flag telling if we have switched to iterating through ExternalFS. 1033 bool IsExternalFSCurrent = false; 1034 FileSystem &ExternalFS; 1035 directory_iterator ExternalDirIter; 1036 llvm::StringSet<> SeenNames; 1037 1038 /// To combine multiple iterations, different methods are responsible for 1039 /// different iteration steps. 1040 /// @{ 1041 1042 /// Responsible for dispatching between RedirectingDirectoryEntry iteration 1043 /// and ExternalFS iteration. 1044 std::error_code incrementImpl(bool IsFirstTime); 1045 /// Responsible for RedirectingDirectoryEntry iteration. 1046 std::error_code incrementContent(bool IsFirstTime); 1047 /// Responsible for ExternalFS iteration. 1048 std::error_code incrementExternal(); 1049 /// @} 1050 1051 public: 1052 VFSFromYamlDirIterImpl(const Twine &Path, 1053 RedirectingDirectoryEntry::iterator Begin, 1054 RedirectingDirectoryEntry::iterator End, 1055 bool IterateExternalFS, FileSystem &ExternalFS, 1056 std::error_code &EC); 1057 1058 std::error_code increment() override; 1059 }; 1060 1061 /// A virtual file system parsed from a YAML file. 1062 /// 1063 /// Currently, this class allows creating virtual directories and mapping 1064 /// virtual file paths to existing external files, available in \c ExternalFS. 1065 /// 1066 /// The basic structure of the parsed file is: 1067 /// \verbatim 1068 /// { 1069 /// 'version': <version number>, 1070 /// <optional configuration> 1071 /// 'roots': [ 1072 /// <directory entries> 1073 /// ] 1074 /// } 1075 /// \endverbatim 1076 /// 1077 /// All configuration options are optional. 1078 /// 'case-sensitive': <boolean, default=true> 1079 /// 'use-external-names': <boolean, default=true> 1080 /// 'overlay-relative': <boolean, default=false> 1081 /// 'fallthrough': <boolean, default=true> 1082 /// 1083 /// Virtual directories are represented as 1084 /// \verbatim 1085 /// { 1086 /// 'type': 'directory', 1087 /// 'name': <string>, 1088 /// 'contents': [ <file or directory entries> ] 1089 /// } 1090 /// \endverbatim 1091 /// 1092 /// The default attributes for virtual directories are: 1093 /// \verbatim 1094 /// MTime = now() when created 1095 /// Perms = 0777 1096 /// User = Group = 0 1097 /// Size = 0 1098 /// UniqueID = unspecified unique value 1099 /// \endverbatim 1100 /// 1101 /// Re-mapped files are represented as 1102 /// \verbatim 1103 /// { 1104 /// 'type': 'file', 1105 /// 'name': <string>, 1106 /// 'use-external-name': <boolean> # Optional 1107 /// 'external-contents': <path to external file> 1108 /// } 1109 /// \endverbatim 1110 /// 1111 /// and inherit their attributes from the external contents. 1112 /// 1113 /// In both cases, the 'name' field may contain multiple path components (e.g. 1114 /// /path/to/file). However, any directory that contains more than one child 1115 /// must be uniquely represented by a directory entry. 1116 class RedirectingFileSystem : public vfs::FileSystem { 1117 friend class RedirectingFileSystemParser; 1118 1119 /// The root(s) of the virtual file system. 1120 std::vector<std::unique_ptr<Entry>> Roots; 1121 1122 /// The file system to use for external references. 1123 IntrusiveRefCntPtr<FileSystem> ExternalFS; 1124 1125 /// If IsRelativeOverlay is set, this represents the directory 1126 /// path that should be prefixed to each 'external-contents' entry 1127 /// when reading from YAML files. 1128 std::string ExternalContentsPrefixDir; 1129 1130 /// @name Configuration 1131 /// @{ 1132 1133 /// Whether to perform case-sensitive comparisons. 1134 /// 1135 /// Currently, case-insensitive matching only works correctly with ASCII. 1136 bool CaseSensitive = true; 1137 1138 /// IsRelativeOverlay marks whether a ExternalContentsPrefixDir path must 1139 /// be prefixed in every 'external-contents' when reading from YAML files. 1140 bool IsRelativeOverlay = false; 1141 1142 /// Whether to use to use the value of 'external-contents' for the 1143 /// names of files. This global value is overridable on a per-file basis. 1144 bool UseExternalNames = true; 1145 1146 /// Whether to attempt a file lookup in external file system after it wasn't 1147 /// found in VFS. 1148 bool IsFallthrough = true; 1149 /// @} 1150 1151 /// Virtual file paths and external files could be canonicalized without "..", 1152 /// "." and "./" in their paths. FIXME: some unittests currently fail on 1153 /// win32 when using remove_dots and remove_leading_dotslash on paths. 1154 bool UseCanonicalizedPaths = 1155 #ifdef _WIN32 1156 false; 1157 #else 1158 true; 1159 #endif 1160 1161 private: 1162 RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS) 1163 : ExternalFS(std::move(ExternalFS)) {} 1164 1165 /// Looks up the path <tt>[Start, End)</tt> in \p From, possibly 1166 /// recursing into the contents of \p From if it is a directory. 1167 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start, 1168 sys::path::const_iterator End, Entry *From); 1169 1170 /// Get the status of a given an \c Entry. 1171 ErrorOr<Status> status(const Twine &Path, Entry *E); 1172 1173 public: 1174 /// Looks up \p Path in \c Roots. 1175 ErrorOr<Entry *> lookupPath(const Twine &Path); 1176 1177 /// Parses \p Buffer, which is expected to be in YAML format and 1178 /// returns a virtual file system representing its contents. 1179 static RedirectingFileSystem * 1180 create(std::unique_ptr<MemoryBuffer> Buffer, 1181 SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, 1182 void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS); 1183 1184 ErrorOr<Status> status(const Twine &Path) override; 1185 ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override; 1186 1187 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 1188 return ExternalFS->getCurrentWorkingDirectory(); 1189 } 1190 1191 std::error_code setCurrentWorkingDirectory(const Twine &Path) override { 1192 return ExternalFS->setCurrentWorkingDirectory(Path); 1193 } 1194 1195 std::error_code isLocal(const Twine &Path, bool &Result) override { 1196 return ExternalFS->isLocal(Path, Result); 1197 } 1198 1199 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override { 1200 ErrorOr<Entry *> E = lookupPath(Dir); 1201 if (!E) { 1202 EC = E.getError(); 1203 if (IsFallthrough && EC == errc::no_such_file_or_directory) 1204 return ExternalFS->dir_begin(Dir, EC); 1205 return {}; 1206 } 1207 ErrorOr<Status> S = status(Dir, *E); 1208 if (!S) { 1209 EC = S.getError(); 1210 return {}; 1211 } 1212 if (!S->isDirectory()) { 1213 EC = std::error_code(static_cast<int>(errc::not_a_directory), 1214 std::system_category()); 1215 return {}; 1216 } 1217 1218 auto *D = cast<RedirectingDirectoryEntry>(*E); 1219 return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>( 1220 Dir, D->contents_begin(), D->contents_end(), 1221 /*IterateExternalFS=*/IsFallthrough, *ExternalFS, EC)); 1222 } 1223 1224 void setExternalContentsPrefixDir(StringRef PrefixDir) { 1225 ExternalContentsPrefixDir = PrefixDir.str(); 1226 } 1227 1228 StringRef getExternalContentsPrefixDir() const { 1229 return ExternalContentsPrefixDir; 1230 } 1231 1232 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 1233 LLVM_DUMP_METHOD void dump() const { 1234 for (const auto &Root : Roots) 1235 dumpEntry(Root.get()); 1236 } 1237 1238 LLVM_DUMP_METHOD void dumpEntry(Entry *E, int NumSpaces = 0) const { 1239 StringRef Name = E->getName(); 1240 for (int i = 0, e = NumSpaces; i < e; ++i) 1241 dbgs() << " "; 1242 dbgs() << "'" << Name.str().c_str() << "'" 1243 << "\n"; 1244 1245 if (E->getKind() == EK_Directory) { 1246 auto *DE = dyn_cast<RedirectingDirectoryEntry>(E); 1247 assert(DE && "Should be a directory"); 1248 1249 for (std::unique_ptr<Entry> &SubEntry : 1250 llvm::make_range(DE->contents_begin(), DE->contents_end())) 1251 dumpEntry(SubEntry.get(), NumSpaces + 2); 1252 } 1253 } 1254 #endif 1255 }; 1256 1257 /// A helper class to hold the common YAML parsing state. 1258 class RedirectingFileSystemParser { 1259 yaml::Stream &Stream; 1260 1261 void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); } 1262 1263 // false on error 1264 bool parseScalarString(yaml::Node *N, StringRef &Result, 1265 SmallVectorImpl<char> &Storage) { 1266 const auto *S = dyn_cast<yaml::ScalarNode>(N); 1267 1268 if (!S) { 1269 error(N, "expected string"); 1270 return false; 1271 } 1272 Result = S->getValue(Storage); 1273 return true; 1274 } 1275 1276 // false on error 1277 bool parseScalarBool(yaml::Node *N, bool &Result) { 1278 SmallString<5> Storage; 1279 StringRef Value; 1280 if (!parseScalarString(N, Value, Storage)) 1281 return false; 1282 1283 if (Value.equals_lower("true") || Value.equals_lower("on") || 1284 Value.equals_lower("yes") || Value == "1") { 1285 Result = true; 1286 return true; 1287 } else if (Value.equals_lower("false") || Value.equals_lower("off") || 1288 Value.equals_lower("no") || Value == "0") { 1289 Result = false; 1290 return true; 1291 } 1292 1293 error(N, "expected boolean value"); 1294 return false; 1295 } 1296 1297 struct KeyStatus { 1298 bool Required; 1299 bool Seen = false; 1300 1301 KeyStatus(bool Required = false) : Required(Required) {} 1302 }; 1303 1304 using KeyStatusPair = std::pair<StringRef, KeyStatus>; 1305 1306 // false on error 1307 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key, 1308 DenseMap<StringRef, KeyStatus> &Keys) { 1309 if (!Keys.count(Key)) { 1310 error(KeyNode, "unknown key"); 1311 return false; 1312 } 1313 KeyStatus &S = Keys[Key]; 1314 if (S.Seen) { 1315 error(KeyNode, Twine("duplicate key '") + Key + "'"); 1316 return false; 1317 } 1318 S.Seen = true; 1319 return true; 1320 } 1321 1322 // false on error 1323 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) { 1324 for (const auto &I : Keys) { 1325 if (I.second.Required && !I.second.Seen) { 1326 error(Obj, Twine("missing key '") + I.first + "'"); 1327 return false; 1328 } 1329 } 1330 return true; 1331 } 1332 1333 Entry *lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name, 1334 Entry *ParentEntry = nullptr) { 1335 if (!ParentEntry) { // Look for a existent root 1336 for (const auto &Root : FS->Roots) { 1337 if (Name.equals(Root->getName())) { 1338 ParentEntry = Root.get(); 1339 return ParentEntry; 1340 } 1341 } 1342 } else { // Advance to the next component 1343 auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry); 1344 for (std::unique_ptr<Entry> &Content : 1345 llvm::make_range(DE->contents_begin(), DE->contents_end())) { 1346 auto *DirContent = dyn_cast<RedirectingDirectoryEntry>(Content.get()); 1347 if (DirContent && Name.equals(Content->getName())) 1348 return DirContent; 1349 } 1350 } 1351 1352 // ... or create a new one 1353 std::unique_ptr<Entry> E = llvm::make_unique<RedirectingDirectoryEntry>( 1354 Name, 1355 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(), 1356 0, 0, 0, file_type::directory_file, sys::fs::all_all)); 1357 1358 if (!ParentEntry) { // Add a new root to the overlay 1359 FS->Roots.push_back(std::move(E)); 1360 ParentEntry = FS->Roots.back().get(); 1361 return ParentEntry; 1362 } 1363 1364 auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry); 1365 DE->addContent(std::move(E)); 1366 return DE->getLastContent(); 1367 } 1368 1369 void uniqueOverlayTree(RedirectingFileSystem *FS, Entry *SrcE, 1370 Entry *NewParentE = nullptr) { 1371 StringRef Name = SrcE->getName(); 1372 switch (SrcE->getKind()) { 1373 case EK_Directory: { 1374 auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE); 1375 assert(DE && "Must be a directory"); 1376 // Empty directories could be present in the YAML as a way to 1377 // describe a file for a current directory after some of its subdir 1378 // is parsed. This only leads to redundant walks, ignore it. 1379 if (!Name.empty()) 1380 NewParentE = lookupOrCreateEntry(FS, Name, NewParentE); 1381 for (std::unique_ptr<Entry> &SubEntry : 1382 llvm::make_range(DE->contents_begin(), DE->contents_end())) 1383 uniqueOverlayTree(FS, SubEntry.get(), NewParentE); 1384 break; 1385 } 1386 case EK_File: { 1387 auto *FE = dyn_cast<RedirectingFileEntry>(SrcE); 1388 assert(FE && "Must be a file"); 1389 assert(NewParentE && "Parent entry must exist"); 1390 auto *DE = dyn_cast<RedirectingDirectoryEntry>(NewParentE); 1391 DE->addContent(llvm::make_unique<RedirectingFileEntry>( 1392 Name, FE->getExternalContentsPath(), FE->getUseName())); 1393 break; 1394 } 1395 } 1396 } 1397 1398 std::unique_ptr<Entry> parseEntry(yaml::Node *N, RedirectingFileSystem *FS, 1399 bool IsRootEntry) { 1400 auto *M = dyn_cast<yaml::MappingNode>(N); 1401 if (!M) { 1402 error(N, "expected mapping node for file or directory entry"); 1403 return nullptr; 1404 } 1405 1406 KeyStatusPair Fields[] = { 1407 KeyStatusPair("name", true), 1408 KeyStatusPair("type", true), 1409 KeyStatusPair("contents", false), 1410 KeyStatusPair("external-contents", false), 1411 KeyStatusPair("use-external-name", false), 1412 }; 1413 1414 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields)); 1415 1416 bool HasContents = false; // external or otherwise 1417 std::vector<std::unique_ptr<Entry>> EntryArrayContents; 1418 std::string ExternalContentsPath; 1419 std::string Name; 1420 yaml::Node *NameValueNode; 1421 auto UseExternalName = RedirectingFileEntry::NK_NotSet; 1422 EntryKind Kind; 1423 1424 for (auto &I : *M) { 1425 StringRef Key; 1426 // Reuse the buffer for key and value, since we don't look at key after 1427 // parsing value. 1428 SmallString<256> Buffer; 1429 if (!parseScalarString(I.getKey(), Key, Buffer)) 1430 return nullptr; 1431 1432 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) 1433 return nullptr; 1434 1435 StringRef Value; 1436 if (Key == "name") { 1437 if (!parseScalarString(I.getValue(), Value, Buffer)) 1438 return nullptr; 1439 1440 NameValueNode = I.getValue(); 1441 if (FS->UseCanonicalizedPaths) { 1442 SmallString<256> Path(Value); 1443 // Guarantee that old YAML files containing paths with ".." and "." 1444 // are properly canonicalized before read into the VFS. 1445 Path = sys::path::remove_leading_dotslash(Path); 1446 sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 1447 Name = Path.str(); 1448 } else { 1449 Name = Value; 1450 } 1451 } else if (Key == "type") { 1452 if (!parseScalarString(I.getValue(), Value, Buffer)) 1453 return nullptr; 1454 if (Value == "file") 1455 Kind = EK_File; 1456 else if (Value == "directory") 1457 Kind = EK_Directory; 1458 else { 1459 error(I.getValue(), "unknown value for 'type'"); 1460 return nullptr; 1461 } 1462 } else if (Key == "contents") { 1463 if (HasContents) { 1464 error(I.getKey(), 1465 "entry already has 'contents' or 'external-contents'"); 1466 return nullptr; 1467 } 1468 HasContents = true; 1469 auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue()); 1470 if (!Contents) { 1471 // FIXME: this is only for directories, what about files? 1472 error(I.getValue(), "expected array"); 1473 return nullptr; 1474 } 1475 1476 for (auto &I : *Contents) { 1477 if (std::unique_ptr<Entry> E = 1478 parseEntry(&I, FS, /*IsRootEntry*/ false)) 1479 EntryArrayContents.push_back(std::move(E)); 1480 else 1481 return nullptr; 1482 } 1483 } else if (Key == "external-contents") { 1484 if (HasContents) { 1485 error(I.getKey(), 1486 "entry already has 'contents' or 'external-contents'"); 1487 return nullptr; 1488 } 1489 HasContents = true; 1490 if (!parseScalarString(I.getValue(), Value, Buffer)) 1491 return nullptr; 1492 1493 SmallString<256> FullPath; 1494 if (FS->IsRelativeOverlay) { 1495 FullPath = FS->getExternalContentsPrefixDir(); 1496 assert(!FullPath.empty() && 1497 "External contents prefix directory must exist"); 1498 llvm::sys::path::append(FullPath, Value); 1499 } else { 1500 FullPath = Value; 1501 } 1502 1503 if (FS->UseCanonicalizedPaths) { 1504 // Guarantee that old YAML files containing paths with ".." and "." 1505 // are properly canonicalized before read into the VFS. 1506 FullPath = sys::path::remove_leading_dotslash(FullPath); 1507 sys::path::remove_dots(FullPath, /*remove_dot_dot=*/true); 1508 } 1509 ExternalContentsPath = FullPath.str(); 1510 } else if (Key == "use-external-name") { 1511 bool Val; 1512 if (!parseScalarBool(I.getValue(), Val)) 1513 return nullptr; 1514 UseExternalName = Val ? RedirectingFileEntry::NK_External 1515 : RedirectingFileEntry::NK_Virtual; 1516 } else { 1517 llvm_unreachable("key missing from Keys"); 1518 } 1519 } 1520 1521 if (Stream.failed()) 1522 return nullptr; 1523 1524 // check for missing keys 1525 if (!HasContents) { 1526 error(N, "missing key 'contents' or 'external-contents'"); 1527 return nullptr; 1528 } 1529 if (!checkMissingKeys(N, Keys)) 1530 return nullptr; 1531 1532 // check invalid configuration 1533 if (Kind == EK_Directory && 1534 UseExternalName != RedirectingFileEntry::NK_NotSet) { 1535 error(N, "'use-external-name' is not supported for directories"); 1536 return nullptr; 1537 } 1538 1539 if (IsRootEntry && !sys::path::is_absolute(Name)) { 1540 assert(NameValueNode && "Name presence should be checked earlier"); 1541 error(NameValueNode, 1542 "entry with relative path at the root level is not discoverable"); 1543 return nullptr; 1544 } 1545 1546 // Remove trailing slash(es), being careful not to remove the root path 1547 StringRef Trimmed(Name); 1548 size_t RootPathLen = sys::path::root_path(Trimmed).size(); 1549 while (Trimmed.size() > RootPathLen && 1550 sys::path::is_separator(Trimmed.back())) 1551 Trimmed = Trimmed.slice(0, Trimmed.size() - 1); 1552 // Get the last component 1553 StringRef LastComponent = sys::path::filename(Trimmed); 1554 1555 std::unique_ptr<Entry> Result; 1556 switch (Kind) { 1557 case EK_File: 1558 Result = llvm::make_unique<RedirectingFileEntry>( 1559 LastComponent, std::move(ExternalContentsPath), UseExternalName); 1560 break; 1561 case EK_Directory: 1562 Result = llvm::make_unique<RedirectingDirectoryEntry>( 1563 LastComponent, std::move(EntryArrayContents), 1564 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(), 1565 0, 0, 0, file_type::directory_file, sys::fs::all_all)); 1566 break; 1567 } 1568 1569 StringRef Parent = sys::path::parent_path(Trimmed); 1570 if (Parent.empty()) 1571 return Result; 1572 1573 // if 'name' contains multiple components, create implicit directory entries 1574 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent), 1575 E = sys::path::rend(Parent); 1576 I != E; ++I) { 1577 std::vector<std::unique_ptr<Entry>> Entries; 1578 Entries.push_back(std::move(Result)); 1579 Result = llvm::make_unique<RedirectingDirectoryEntry>( 1580 *I, std::move(Entries), 1581 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(), 1582 0, 0, 0, file_type::directory_file, sys::fs::all_all)); 1583 } 1584 return Result; 1585 } 1586 1587 public: 1588 RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {} 1589 1590 // false on error 1591 bool parse(yaml::Node *Root, RedirectingFileSystem *FS) { 1592 auto *Top = dyn_cast<yaml::MappingNode>(Root); 1593 if (!Top) { 1594 error(Root, "expected mapping node"); 1595 return false; 1596 } 1597 1598 KeyStatusPair Fields[] = { 1599 KeyStatusPair("version", true), 1600 KeyStatusPair("case-sensitive", false), 1601 KeyStatusPair("use-external-names", false), 1602 KeyStatusPair("overlay-relative", false), 1603 KeyStatusPair("fallthrough", false), 1604 KeyStatusPair("roots", true), 1605 }; 1606 1607 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields)); 1608 std::vector<std::unique_ptr<Entry>> RootEntries; 1609 1610 // Parse configuration and 'roots' 1611 for (auto &I : *Top) { 1612 SmallString<10> KeyBuffer; 1613 StringRef Key; 1614 if (!parseScalarString(I.getKey(), Key, KeyBuffer)) 1615 return false; 1616 1617 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) 1618 return false; 1619 1620 if (Key == "roots") { 1621 auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue()); 1622 if (!Roots) { 1623 error(I.getValue(), "expected array"); 1624 return false; 1625 } 1626 1627 for (auto &I : *Roots) { 1628 if (std::unique_ptr<Entry> E = 1629 parseEntry(&I, FS, /*IsRootEntry*/ true)) 1630 RootEntries.push_back(std::move(E)); 1631 else 1632 return false; 1633 } 1634 } else if (Key == "version") { 1635 StringRef VersionString; 1636 SmallString<4> Storage; 1637 if (!parseScalarString(I.getValue(), VersionString, Storage)) 1638 return false; 1639 int Version; 1640 if (VersionString.getAsInteger<int>(10, Version)) { 1641 error(I.getValue(), "expected integer"); 1642 return false; 1643 } 1644 if (Version < 0) { 1645 error(I.getValue(), "invalid version number"); 1646 return false; 1647 } 1648 if (Version != 0) { 1649 error(I.getValue(), "version mismatch, expected 0"); 1650 return false; 1651 } 1652 } else if (Key == "case-sensitive") { 1653 if (!parseScalarBool(I.getValue(), FS->CaseSensitive)) 1654 return false; 1655 } else if (Key == "overlay-relative") { 1656 if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay)) 1657 return false; 1658 } else if (Key == "use-external-names") { 1659 if (!parseScalarBool(I.getValue(), FS->UseExternalNames)) 1660 return false; 1661 } else if (Key == "fallthrough") { 1662 if (!parseScalarBool(I.getValue(), FS->IsFallthrough)) 1663 return false; 1664 } else { 1665 llvm_unreachable("key missing from Keys"); 1666 } 1667 } 1668 1669 if (Stream.failed()) 1670 return false; 1671 1672 if (!checkMissingKeys(Top, Keys)) 1673 return false; 1674 1675 // Now that we sucessefully parsed the YAML file, canonicalize the internal 1676 // representation to a proper directory tree so that we can search faster 1677 // inside the VFS. 1678 for (auto &E : RootEntries) 1679 uniqueOverlayTree(FS, E.get()); 1680 1681 return true; 1682 } 1683 }; 1684 1685 } // namespace 1686 1687 RedirectingFileSystem * 1688 RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer, 1689 SourceMgr::DiagHandlerTy DiagHandler, 1690 StringRef YAMLFilePath, void *DiagContext, 1691 IntrusiveRefCntPtr<FileSystem> ExternalFS) { 1692 SourceMgr SM; 1693 yaml::Stream Stream(Buffer->getMemBufferRef(), SM); 1694 1695 SM.setDiagHandler(DiagHandler, DiagContext); 1696 yaml::document_iterator DI = Stream.begin(); 1697 yaml::Node *Root = DI->getRoot(); 1698 if (DI == Stream.end() || !Root) { 1699 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node"); 1700 return nullptr; 1701 } 1702 1703 RedirectingFileSystemParser P(Stream); 1704 1705 std::unique_ptr<RedirectingFileSystem> FS( 1706 new RedirectingFileSystem(std::move(ExternalFS))); 1707 1708 if (!YAMLFilePath.empty()) { 1709 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed 1710 // to each 'external-contents' path. 1711 // 1712 // Example: 1713 // -ivfsoverlay dummy.cache/vfs/vfs.yaml 1714 // yields: 1715 // FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs 1716 // 1717 SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath); 1718 std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir); 1719 assert(!EC && "Overlay dir final path must be absolute"); 1720 (void)EC; 1721 FS->setExternalContentsPrefixDir(OverlayAbsDir); 1722 } 1723 1724 if (!P.parse(Root, FS.get())) 1725 return nullptr; 1726 1727 return FS.release(); 1728 } 1729 1730 ErrorOr<Entry *> RedirectingFileSystem::lookupPath(const Twine &Path_) { 1731 SmallString<256> Path; 1732 Path_.toVector(Path); 1733 1734 // Handle relative paths 1735 if (std::error_code EC = makeAbsolute(Path)) 1736 return EC; 1737 1738 // Canonicalize path by removing ".", "..", "./", etc components. This is 1739 // a VFS request, do bot bother about symlinks in the path components 1740 // but canonicalize in order to perform the correct entry search. 1741 if (UseCanonicalizedPaths) { 1742 Path = sys::path::remove_leading_dotslash(Path); 1743 sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 1744 } 1745 1746 if (Path.empty()) 1747 return make_error_code(llvm::errc::invalid_argument); 1748 1749 sys::path::const_iterator Start = sys::path::begin(Path); 1750 sys::path::const_iterator End = sys::path::end(Path); 1751 for (const auto &Root : Roots) { 1752 ErrorOr<Entry *> Result = lookupPath(Start, End, Root.get()); 1753 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) 1754 return Result; 1755 } 1756 return make_error_code(llvm::errc::no_such_file_or_directory); 1757 } 1758 1759 ErrorOr<Entry *> 1760 RedirectingFileSystem::lookupPath(sys::path::const_iterator Start, 1761 sys::path::const_iterator End, Entry *From) { 1762 #ifndef _WIN32 1763 assert(!isTraversalComponent(*Start) && 1764 !isTraversalComponent(From->getName()) && 1765 "Paths should not contain traversal components"); 1766 #else 1767 // FIXME: this is here to support windows, remove it once canonicalized 1768 // paths become globally default. 1769 if (Start->equals(".")) 1770 ++Start; 1771 #endif 1772 1773 StringRef FromName = From->getName(); 1774 1775 // Forward the search to the next component in case this is an empty one. 1776 if (!FromName.empty()) { 1777 if (CaseSensitive ? !Start->equals(FromName) 1778 : !Start->equals_lower(FromName)) 1779 // failure to match 1780 return make_error_code(llvm::errc::no_such_file_or_directory); 1781 1782 ++Start; 1783 1784 if (Start == End) { 1785 // Match! 1786 return From; 1787 } 1788 } 1789 1790 auto *DE = dyn_cast<RedirectingDirectoryEntry>(From); 1791 if (!DE) 1792 return make_error_code(llvm::errc::not_a_directory); 1793 1794 for (const std::unique_ptr<Entry> &DirEntry : 1795 llvm::make_range(DE->contents_begin(), DE->contents_end())) { 1796 ErrorOr<Entry *> Result = lookupPath(Start, End, DirEntry.get()); 1797 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) 1798 return Result; 1799 } 1800 return make_error_code(llvm::errc::no_such_file_or_directory); 1801 } 1802 1803 static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames, 1804 Status ExternalStatus) { 1805 Status S = ExternalStatus; 1806 if (!UseExternalNames) 1807 S = Status::copyWithNewName(S, Path.str()); 1808 S.IsVFSMapped = true; 1809 return S; 1810 } 1811 1812 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) { 1813 assert(E != nullptr); 1814 if (auto *F = dyn_cast<RedirectingFileEntry>(E)) { 1815 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath()); 1816 assert(!S || S->getName() == F->getExternalContentsPath()); 1817 if (S) 1818 return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), 1819 *S); 1820 return S; 1821 } else { // directory 1822 auto *DE = cast<RedirectingDirectoryEntry>(E); 1823 return Status::copyWithNewName(DE->getStatus(), Path.str()); 1824 } 1825 } 1826 1827 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) { 1828 ErrorOr<Entry *> Result = lookupPath(Path); 1829 if (!Result) { 1830 if (IsFallthrough && 1831 Result.getError() == llvm::errc::no_such_file_or_directory) { 1832 return ExternalFS->status(Path); 1833 } 1834 return Result.getError(); 1835 } 1836 return status(Path, *Result); 1837 } 1838 1839 namespace { 1840 1841 /// Provide a file wrapper with an overriden status. 1842 class FileWithFixedStatus : public File { 1843 std::unique_ptr<File> InnerFile; 1844 Status S; 1845 1846 public: 1847 FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S) 1848 : InnerFile(std::move(InnerFile)), S(std::move(S)) {} 1849 1850 ErrorOr<Status> status() override { return S; } 1851 ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 1852 1853 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 1854 bool IsVolatile) override { 1855 return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator, 1856 IsVolatile); 1857 } 1858 1859 std::error_code close() override { return InnerFile->close(); } 1860 }; 1861 1862 } // namespace 1863 1864 ErrorOr<std::unique_ptr<File>> 1865 RedirectingFileSystem::openFileForRead(const Twine &Path) { 1866 ErrorOr<Entry *> E = lookupPath(Path); 1867 if (!E) { 1868 if (IsFallthrough && 1869 E.getError() == llvm::errc::no_such_file_or_directory) { 1870 return ExternalFS->openFileForRead(Path); 1871 } 1872 return E.getError(); 1873 } 1874 1875 auto *F = dyn_cast<RedirectingFileEntry>(*E); 1876 if (!F) // FIXME: errc::not_a_file? 1877 return make_error_code(llvm::errc::invalid_argument); 1878 1879 auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath()); 1880 if (!Result) 1881 return Result; 1882 1883 auto ExternalStatus = (*Result)->status(); 1884 if (!ExternalStatus) 1885 return ExternalStatus.getError(); 1886 1887 // FIXME: Update the status with the name and VFSMapped. 1888 Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), 1889 *ExternalStatus); 1890 return std::unique_ptr<File>( 1891 llvm::make_unique<FileWithFixedStatus>(std::move(*Result), S)); 1892 } 1893 1894 IntrusiveRefCntPtr<FileSystem> 1895 vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer, 1896 SourceMgr::DiagHandlerTy DiagHandler, 1897 StringRef YAMLFilePath, void *DiagContext, 1898 IntrusiveRefCntPtr<FileSystem> ExternalFS) { 1899 return RedirectingFileSystem::create(std::move(Buffer), DiagHandler, 1900 YAMLFilePath, DiagContext, 1901 std::move(ExternalFS)); 1902 } 1903 1904 static void getVFSEntries(Entry *SrcE, SmallVectorImpl<StringRef> &Path, 1905 SmallVectorImpl<YAMLVFSEntry> &Entries) { 1906 auto Kind = SrcE->getKind(); 1907 if (Kind == EK_Directory) { 1908 auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE); 1909 assert(DE && "Must be a directory"); 1910 for (std::unique_ptr<Entry> &SubEntry : 1911 llvm::make_range(DE->contents_begin(), DE->contents_end())) { 1912 Path.push_back(SubEntry->getName()); 1913 getVFSEntries(SubEntry.get(), Path, Entries); 1914 Path.pop_back(); 1915 } 1916 return; 1917 } 1918 1919 assert(Kind == EK_File && "Must be a EK_File"); 1920 auto *FE = dyn_cast<RedirectingFileEntry>(SrcE); 1921 assert(FE && "Must be a file"); 1922 SmallString<128> VPath; 1923 for (auto &Comp : Path) 1924 llvm::sys::path::append(VPath, Comp); 1925 Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath())); 1926 } 1927 1928 void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer, 1929 SourceMgr::DiagHandlerTy DiagHandler, 1930 StringRef YAMLFilePath, 1931 SmallVectorImpl<YAMLVFSEntry> &CollectedEntries, 1932 void *DiagContext, 1933 IntrusiveRefCntPtr<FileSystem> ExternalFS) { 1934 RedirectingFileSystem *VFS = RedirectingFileSystem::create( 1935 std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext, 1936 std::move(ExternalFS)); 1937 ErrorOr<Entry *> RootE = VFS->lookupPath("/"); 1938 if (!RootE) 1939 return; 1940 SmallVector<StringRef, 8> Components; 1941 Components.push_back("/"); 1942 getVFSEntries(*RootE, Components, CollectedEntries); 1943 } 1944 1945 UniqueID vfs::getNextVirtualUniqueID() { 1946 static std::atomic<unsigned> UID; 1947 unsigned ID = ++UID; 1948 // The following assumes that uint64_t max will never collide with a real 1949 // dev_t value from the OS. 1950 return UniqueID(std::numeric_limits<uint64_t>::max(), ID); 1951 } 1952 1953 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) { 1954 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute"); 1955 assert(sys::path::is_absolute(RealPath) && "real path not absolute"); 1956 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported"); 1957 Mappings.emplace_back(VirtualPath, RealPath); 1958 } 1959 1960 namespace { 1961 1962 class JSONWriter { 1963 llvm::raw_ostream &OS; 1964 SmallVector<StringRef, 16> DirStack; 1965 1966 unsigned getDirIndent() { return 4 * DirStack.size(); } 1967 unsigned getFileIndent() { return 4 * (DirStack.size() + 1); } 1968 bool containedIn(StringRef Parent, StringRef Path); 1969 StringRef containedPart(StringRef Parent, StringRef Path); 1970 void startDirectory(StringRef Path); 1971 void endDirectory(); 1972 void writeEntry(StringRef VPath, StringRef RPath); 1973 1974 public: 1975 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {} 1976 1977 void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> UseExternalNames, 1978 Optional<bool> IsCaseSensitive, Optional<bool> IsOverlayRelative, 1979 StringRef OverlayDir); 1980 }; 1981 1982 } // namespace 1983 1984 bool JSONWriter::containedIn(StringRef Parent, StringRef Path) { 1985 using namespace llvm::sys; 1986 1987 // Compare each path component. 1988 auto IParent = path::begin(Parent), EParent = path::end(Parent); 1989 for (auto IChild = path::begin(Path), EChild = path::end(Path); 1990 IParent != EParent && IChild != EChild; ++IParent, ++IChild) { 1991 if (*IParent != *IChild) 1992 return false; 1993 } 1994 // Have we exhausted the parent path? 1995 return IParent == EParent; 1996 } 1997 1998 StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) { 1999 assert(!Parent.empty()); 2000 assert(containedIn(Parent, Path)); 2001 return Path.slice(Parent.size() + 1, StringRef::npos); 2002 } 2003 2004 void JSONWriter::startDirectory(StringRef Path) { 2005 StringRef Name = 2006 DirStack.empty() ? Path : containedPart(DirStack.back(), Path); 2007 DirStack.push_back(Path); 2008 unsigned Indent = getDirIndent(); 2009 OS.indent(Indent) << "{\n"; 2010 OS.indent(Indent + 2) << "'type': 'directory',\n"; 2011 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n"; 2012 OS.indent(Indent + 2) << "'contents': [\n"; 2013 } 2014 2015 void JSONWriter::endDirectory() { 2016 unsigned Indent = getDirIndent(); 2017 OS.indent(Indent + 2) << "]\n"; 2018 OS.indent(Indent) << "}"; 2019 2020 DirStack.pop_back(); 2021 } 2022 2023 void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) { 2024 unsigned Indent = getFileIndent(); 2025 OS.indent(Indent) << "{\n"; 2026 OS.indent(Indent + 2) << "'type': 'file',\n"; 2027 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n"; 2028 OS.indent(Indent + 2) << "'external-contents': \"" 2029 << llvm::yaml::escape(RPath) << "\"\n"; 2030 OS.indent(Indent) << "}"; 2031 } 2032 2033 void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries, 2034 Optional<bool> UseExternalNames, 2035 Optional<bool> IsCaseSensitive, 2036 Optional<bool> IsOverlayRelative, 2037 StringRef OverlayDir) { 2038 using namespace llvm::sys; 2039 2040 OS << "{\n" 2041 " 'version': 0,\n"; 2042 if (IsCaseSensitive.hasValue()) 2043 OS << " 'case-sensitive': '" 2044 << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n"; 2045 if (UseExternalNames.hasValue()) 2046 OS << " 'use-external-names': '" 2047 << (UseExternalNames.getValue() ? "true" : "false") << "',\n"; 2048 bool UseOverlayRelative = false; 2049 if (IsOverlayRelative.hasValue()) { 2050 UseOverlayRelative = IsOverlayRelative.getValue(); 2051 OS << " 'overlay-relative': '" << (UseOverlayRelative ? "true" : "false") 2052 << "',\n"; 2053 } 2054 OS << " 'roots': [\n"; 2055 2056 if (!Entries.empty()) { 2057 const YAMLVFSEntry &Entry = Entries.front(); 2058 startDirectory(path::parent_path(Entry.VPath)); 2059 2060 StringRef RPath = Entry.RPath; 2061 if (UseOverlayRelative) { 2062 unsigned OverlayDirLen = OverlayDir.size(); 2063 assert(RPath.substr(0, OverlayDirLen) == OverlayDir && 2064 "Overlay dir must be contained in RPath"); 2065 RPath = RPath.slice(OverlayDirLen, RPath.size()); 2066 } 2067 2068 writeEntry(path::filename(Entry.VPath), RPath); 2069 2070 for (const auto &Entry : Entries.slice(1)) { 2071 StringRef Dir = path::parent_path(Entry.VPath); 2072 if (Dir == DirStack.back()) 2073 OS << ",\n"; 2074 else { 2075 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) { 2076 OS << "\n"; 2077 endDirectory(); 2078 } 2079 OS << ",\n"; 2080 startDirectory(Dir); 2081 } 2082 StringRef RPath = Entry.RPath; 2083 if (UseOverlayRelative) { 2084 unsigned OverlayDirLen = OverlayDir.size(); 2085 assert(RPath.substr(0, OverlayDirLen) == OverlayDir && 2086 "Overlay dir must be contained in RPath"); 2087 RPath = RPath.slice(OverlayDirLen, RPath.size()); 2088 } 2089 writeEntry(path::filename(Entry.VPath), RPath); 2090 } 2091 2092 while (!DirStack.empty()) { 2093 OS << "\n"; 2094 endDirectory(); 2095 } 2096 OS << "\n"; 2097 } 2098 2099 OS << " ]\n" 2100 << "}\n"; 2101 } 2102 2103 void YAMLVFSWriter::write(llvm::raw_ostream &OS) { 2104 llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) { 2105 return LHS.VPath < RHS.VPath; 2106 }); 2107 2108 JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive, 2109 IsOverlayRelative, OverlayDir); 2110 } 2111 2112 VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl( 2113 const Twine &_Path, RedirectingDirectoryEntry::iterator Begin, 2114 RedirectingDirectoryEntry::iterator End, bool IterateExternalFS, 2115 FileSystem &ExternalFS, std::error_code &EC) 2116 : Dir(_Path.str()), Current(Begin), End(End), 2117 IterateExternalFS(IterateExternalFS), ExternalFS(ExternalFS) { 2118 EC = incrementImpl(/*IsFirstTime=*/true); 2119 } 2120 2121 std::error_code VFSFromYamlDirIterImpl::increment() { 2122 return incrementImpl(/*IsFirstTime=*/false); 2123 } 2124 2125 std::error_code VFSFromYamlDirIterImpl::incrementExternal() { 2126 assert(!(IsExternalFSCurrent && ExternalDirIter == directory_iterator()) && 2127 "incrementing past end"); 2128 std::error_code EC; 2129 if (IsExternalFSCurrent) { 2130 ExternalDirIter.increment(EC); 2131 } else if (IterateExternalFS) { 2132 ExternalDirIter = ExternalFS.dir_begin(Dir, EC); 2133 IsExternalFSCurrent = true; 2134 if (EC && EC != errc::no_such_file_or_directory) 2135 return EC; 2136 EC = {}; 2137 } 2138 if (EC || ExternalDirIter == directory_iterator()) { 2139 CurrentEntry = directory_entry(); 2140 } else { 2141 CurrentEntry = *ExternalDirIter; 2142 } 2143 return EC; 2144 } 2145 2146 std::error_code VFSFromYamlDirIterImpl::incrementContent(bool IsFirstTime) { 2147 assert((IsFirstTime || Current != End) && "cannot iterate past end"); 2148 if (!IsFirstTime) 2149 ++Current; 2150 while (Current != End) { 2151 SmallString<128> PathStr(Dir); 2152 llvm::sys::path::append(PathStr, (*Current)->getName()); 2153 sys::fs::file_type Type; 2154 switch ((*Current)->getKind()) { 2155 case EK_Directory: 2156 Type = sys::fs::file_type::directory_file; 2157 break; 2158 case EK_File: 2159 Type = sys::fs::file_type::regular_file; 2160 break; 2161 } 2162 CurrentEntry = directory_entry(PathStr.str(), Type); 2163 return {}; 2164 } 2165 return incrementExternal(); 2166 } 2167 2168 std::error_code VFSFromYamlDirIterImpl::incrementImpl(bool IsFirstTime) { 2169 while (true) { 2170 std::error_code EC = IsExternalFSCurrent ? incrementExternal() 2171 : incrementContent(IsFirstTime); 2172 if (EC || CurrentEntry.path().empty()) 2173 return EC; 2174 StringRef Name = llvm::sys::path::filename(CurrentEntry.path()); 2175 if (SeenNames.insert(Name).second) 2176 return EC; // name not seen before 2177 } 2178 llvm_unreachable("returned above"); 2179 } 2180 2181 vfs::recursive_directory_iterator::recursive_directory_iterator( 2182 FileSystem &FS_, const Twine &Path, std::error_code &EC) 2183 : FS(&FS_) { 2184 directory_iterator I = FS->dir_begin(Path, EC); 2185 if (I != directory_iterator()) { 2186 State = std::make_shared<detail::RecDirIterState>(); 2187 State->Stack.push(I); 2188 } 2189 } 2190 2191 vfs::recursive_directory_iterator & 2192 recursive_directory_iterator::increment(std::error_code &EC) { 2193 assert(FS && State && !State->Stack.empty() && "incrementing past end"); 2194 assert(!State->Stack.top()->path().empty() && "non-canonical end iterator"); 2195 vfs::directory_iterator End; 2196 2197 if (State->HasNoPushRequest) 2198 State->HasNoPushRequest = false; 2199 else { 2200 if (State->Stack.top()->type() == sys::fs::file_type::directory_file) { 2201 vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC); 2202 if (I != End) { 2203 State->Stack.push(I); 2204 return *this; 2205 } 2206 } 2207 } 2208 2209 while (!State->Stack.empty() && State->Stack.top().increment(EC) == End) 2210 State->Stack.pop(); 2211 2212 if (State->Stack.empty()) 2213 State.reset(); // end iterator 2214 2215 return *this; 2216 } 2217