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