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) const; 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) const; 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 std::error_code getRealPath(const Twine &Path, 1187 SmallVectorImpl<char> &Output) const override; 1188 1189 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 1190 return ExternalFS->getCurrentWorkingDirectory(); 1191 } 1192 1193 std::error_code setCurrentWorkingDirectory(const Twine &Path) override { 1194 return ExternalFS->setCurrentWorkingDirectory(Path); 1195 } 1196 1197 std::error_code isLocal(const Twine &Path, bool &Result) override { 1198 return ExternalFS->isLocal(Path, Result); 1199 } 1200 1201 directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override { 1202 ErrorOr<Entry *> E = lookupPath(Dir); 1203 if (!E) { 1204 EC = E.getError(); 1205 if (IsFallthrough && EC == errc::no_such_file_or_directory) 1206 return ExternalFS->dir_begin(Dir, EC); 1207 return {}; 1208 } 1209 ErrorOr<Status> S = status(Dir, *E); 1210 if (!S) { 1211 EC = S.getError(); 1212 return {}; 1213 } 1214 if (!S->isDirectory()) { 1215 EC = std::error_code(static_cast<int>(errc::not_a_directory), 1216 std::system_category()); 1217 return {}; 1218 } 1219 1220 auto *D = cast<RedirectingDirectoryEntry>(*E); 1221 return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>( 1222 Dir, D->contents_begin(), D->contents_end(), 1223 /*IterateExternalFS=*/IsFallthrough, *ExternalFS, EC)); 1224 } 1225 1226 void setExternalContentsPrefixDir(StringRef PrefixDir) { 1227 ExternalContentsPrefixDir = PrefixDir.str(); 1228 } 1229 1230 StringRef getExternalContentsPrefixDir() const { 1231 return ExternalContentsPrefixDir; 1232 } 1233 1234 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 1235 LLVM_DUMP_METHOD void dump() const { 1236 for (const auto &Root : Roots) 1237 dumpEntry(Root.get()); 1238 } 1239 1240 LLVM_DUMP_METHOD void dumpEntry(Entry *E, int NumSpaces = 0) const { 1241 StringRef Name = E->getName(); 1242 for (int i = 0, e = NumSpaces; i < e; ++i) 1243 dbgs() << " "; 1244 dbgs() << "'" << Name.str().c_str() << "'" 1245 << "\n"; 1246 1247 if (E->getKind() == EK_Directory) { 1248 auto *DE = dyn_cast<RedirectingDirectoryEntry>(E); 1249 assert(DE && "Should be a directory"); 1250 1251 for (std::unique_ptr<Entry> &SubEntry : 1252 llvm::make_range(DE->contents_begin(), DE->contents_end())) 1253 dumpEntry(SubEntry.get(), NumSpaces + 2); 1254 } 1255 } 1256 #endif 1257 }; 1258 1259 /// A helper class to hold the common YAML parsing state. 1260 class RedirectingFileSystemParser { 1261 yaml::Stream &Stream; 1262 1263 void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); } 1264 1265 // false on error 1266 bool parseScalarString(yaml::Node *N, StringRef &Result, 1267 SmallVectorImpl<char> &Storage) { 1268 const auto *S = dyn_cast<yaml::ScalarNode>(N); 1269 1270 if (!S) { 1271 error(N, "expected string"); 1272 return false; 1273 } 1274 Result = S->getValue(Storage); 1275 return true; 1276 } 1277 1278 // false on error 1279 bool parseScalarBool(yaml::Node *N, bool &Result) { 1280 SmallString<5> Storage; 1281 StringRef Value; 1282 if (!parseScalarString(N, Value, Storage)) 1283 return false; 1284 1285 if (Value.equals_lower("true") || Value.equals_lower("on") || 1286 Value.equals_lower("yes") || Value == "1") { 1287 Result = true; 1288 return true; 1289 } else if (Value.equals_lower("false") || Value.equals_lower("off") || 1290 Value.equals_lower("no") || Value == "0") { 1291 Result = false; 1292 return true; 1293 } 1294 1295 error(N, "expected boolean value"); 1296 return false; 1297 } 1298 1299 struct KeyStatus { 1300 bool Required; 1301 bool Seen = false; 1302 1303 KeyStatus(bool Required = false) : Required(Required) {} 1304 }; 1305 1306 using KeyStatusPair = std::pair<StringRef, KeyStatus>; 1307 1308 // false on error 1309 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key, 1310 DenseMap<StringRef, KeyStatus> &Keys) { 1311 if (!Keys.count(Key)) { 1312 error(KeyNode, "unknown key"); 1313 return false; 1314 } 1315 KeyStatus &S = Keys[Key]; 1316 if (S.Seen) { 1317 error(KeyNode, Twine("duplicate key '") + Key + "'"); 1318 return false; 1319 } 1320 S.Seen = true; 1321 return true; 1322 } 1323 1324 // false on error 1325 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) { 1326 for (const auto &I : Keys) { 1327 if (I.second.Required && !I.second.Seen) { 1328 error(Obj, Twine("missing key '") + I.first + "'"); 1329 return false; 1330 } 1331 } 1332 return true; 1333 } 1334 1335 Entry *lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name, 1336 Entry *ParentEntry = nullptr) { 1337 if (!ParentEntry) { // Look for a existent root 1338 for (const auto &Root : FS->Roots) { 1339 if (Name.equals(Root->getName())) { 1340 ParentEntry = Root.get(); 1341 return ParentEntry; 1342 } 1343 } 1344 } else { // Advance to the next component 1345 auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry); 1346 for (std::unique_ptr<Entry> &Content : 1347 llvm::make_range(DE->contents_begin(), DE->contents_end())) { 1348 auto *DirContent = dyn_cast<RedirectingDirectoryEntry>(Content.get()); 1349 if (DirContent && Name.equals(Content->getName())) 1350 return DirContent; 1351 } 1352 } 1353 1354 // ... or create a new one 1355 std::unique_ptr<Entry> E = llvm::make_unique<RedirectingDirectoryEntry>( 1356 Name, 1357 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(), 1358 0, 0, 0, file_type::directory_file, sys::fs::all_all)); 1359 1360 if (!ParentEntry) { // Add a new root to the overlay 1361 FS->Roots.push_back(std::move(E)); 1362 ParentEntry = FS->Roots.back().get(); 1363 return ParentEntry; 1364 } 1365 1366 auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry); 1367 DE->addContent(std::move(E)); 1368 return DE->getLastContent(); 1369 } 1370 1371 void uniqueOverlayTree(RedirectingFileSystem *FS, Entry *SrcE, 1372 Entry *NewParentE = nullptr) { 1373 StringRef Name = SrcE->getName(); 1374 switch (SrcE->getKind()) { 1375 case EK_Directory: { 1376 auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE); 1377 assert(DE && "Must be a directory"); 1378 // Empty directories could be present in the YAML as a way to 1379 // describe a file for a current directory after some of its subdir 1380 // is parsed. This only leads to redundant walks, ignore it. 1381 if (!Name.empty()) 1382 NewParentE = lookupOrCreateEntry(FS, Name, NewParentE); 1383 for (std::unique_ptr<Entry> &SubEntry : 1384 llvm::make_range(DE->contents_begin(), DE->contents_end())) 1385 uniqueOverlayTree(FS, SubEntry.get(), NewParentE); 1386 break; 1387 } 1388 case EK_File: { 1389 auto *FE = dyn_cast<RedirectingFileEntry>(SrcE); 1390 assert(FE && "Must be a file"); 1391 assert(NewParentE && "Parent entry must exist"); 1392 auto *DE = dyn_cast<RedirectingDirectoryEntry>(NewParentE); 1393 DE->addContent(llvm::make_unique<RedirectingFileEntry>( 1394 Name, FE->getExternalContentsPath(), FE->getUseName())); 1395 break; 1396 } 1397 } 1398 } 1399 1400 std::unique_ptr<Entry> parseEntry(yaml::Node *N, RedirectingFileSystem *FS, 1401 bool IsRootEntry) { 1402 auto *M = dyn_cast<yaml::MappingNode>(N); 1403 if (!M) { 1404 error(N, "expected mapping node for file or directory entry"); 1405 return nullptr; 1406 } 1407 1408 KeyStatusPair Fields[] = { 1409 KeyStatusPair("name", true), 1410 KeyStatusPair("type", true), 1411 KeyStatusPair("contents", false), 1412 KeyStatusPair("external-contents", false), 1413 KeyStatusPair("use-external-name", false), 1414 }; 1415 1416 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields)); 1417 1418 bool HasContents = false; // external or otherwise 1419 std::vector<std::unique_ptr<Entry>> EntryArrayContents; 1420 std::string ExternalContentsPath; 1421 std::string Name; 1422 yaml::Node *NameValueNode; 1423 auto UseExternalName = RedirectingFileEntry::NK_NotSet; 1424 EntryKind Kind; 1425 1426 for (auto &I : *M) { 1427 StringRef Key; 1428 // Reuse the buffer for key and value, since we don't look at key after 1429 // parsing value. 1430 SmallString<256> Buffer; 1431 if (!parseScalarString(I.getKey(), Key, Buffer)) 1432 return nullptr; 1433 1434 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) 1435 return nullptr; 1436 1437 StringRef Value; 1438 if (Key == "name") { 1439 if (!parseScalarString(I.getValue(), Value, Buffer)) 1440 return nullptr; 1441 1442 NameValueNode = I.getValue(); 1443 if (FS->UseCanonicalizedPaths) { 1444 SmallString<256> Path(Value); 1445 // Guarantee that old YAML files containing paths with ".." and "." 1446 // are properly canonicalized before read into the VFS. 1447 Path = sys::path::remove_leading_dotslash(Path); 1448 sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 1449 Name = Path.str(); 1450 } else { 1451 Name = Value; 1452 } 1453 } else if (Key == "type") { 1454 if (!parseScalarString(I.getValue(), Value, Buffer)) 1455 return nullptr; 1456 if (Value == "file") 1457 Kind = EK_File; 1458 else if (Value == "directory") 1459 Kind = EK_Directory; 1460 else { 1461 error(I.getValue(), "unknown value for 'type'"); 1462 return nullptr; 1463 } 1464 } else if (Key == "contents") { 1465 if (HasContents) { 1466 error(I.getKey(), 1467 "entry already has 'contents' or 'external-contents'"); 1468 return nullptr; 1469 } 1470 HasContents = true; 1471 auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue()); 1472 if (!Contents) { 1473 // FIXME: this is only for directories, what about files? 1474 error(I.getValue(), "expected array"); 1475 return nullptr; 1476 } 1477 1478 for (auto &I : *Contents) { 1479 if (std::unique_ptr<Entry> E = 1480 parseEntry(&I, FS, /*IsRootEntry*/ false)) 1481 EntryArrayContents.push_back(std::move(E)); 1482 else 1483 return nullptr; 1484 } 1485 } else if (Key == "external-contents") { 1486 if (HasContents) { 1487 error(I.getKey(), 1488 "entry already has 'contents' or 'external-contents'"); 1489 return nullptr; 1490 } 1491 HasContents = true; 1492 if (!parseScalarString(I.getValue(), Value, Buffer)) 1493 return nullptr; 1494 1495 SmallString<256> FullPath; 1496 if (FS->IsRelativeOverlay) { 1497 FullPath = FS->getExternalContentsPrefixDir(); 1498 assert(!FullPath.empty() && 1499 "External contents prefix directory must exist"); 1500 llvm::sys::path::append(FullPath, Value); 1501 } else { 1502 FullPath = Value; 1503 } 1504 1505 if (FS->UseCanonicalizedPaths) { 1506 // Guarantee that old YAML files containing paths with ".." and "." 1507 // are properly canonicalized before read into the VFS. 1508 FullPath = sys::path::remove_leading_dotslash(FullPath); 1509 sys::path::remove_dots(FullPath, /*remove_dot_dot=*/true); 1510 } 1511 ExternalContentsPath = FullPath.str(); 1512 } else if (Key == "use-external-name") { 1513 bool Val; 1514 if (!parseScalarBool(I.getValue(), Val)) 1515 return nullptr; 1516 UseExternalName = Val ? RedirectingFileEntry::NK_External 1517 : RedirectingFileEntry::NK_Virtual; 1518 } else { 1519 llvm_unreachable("key missing from Keys"); 1520 } 1521 } 1522 1523 if (Stream.failed()) 1524 return nullptr; 1525 1526 // check for missing keys 1527 if (!HasContents) { 1528 error(N, "missing key 'contents' or 'external-contents'"); 1529 return nullptr; 1530 } 1531 if (!checkMissingKeys(N, Keys)) 1532 return nullptr; 1533 1534 // check invalid configuration 1535 if (Kind == EK_Directory && 1536 UseExternalName != RedirectingFileEntry::NK_NotSet) { 1537 error(N, "'use-external-name' is not supported for directories"); 1538 return nullptr; 1539 } 1540 1541 if (IsRootEntry && !sys::path::is_absolute(Name)) { 1542 assert(NameValueNode && "Name presence should be checked earlier"); 1543 error(NameValueNode, 1544 "entry with relative path at the root level is not discoverable"); 1545 return nullptr; 1546 } 1547 1548 // Remove trailing slash(es), being careful not to remove the root path 1549 StringRef Trimmed(Name); 1550 size_t RootPathLen = sys::path::root_path(Trimmed).size(); 1551 while (Trimmed.size() > RootPathLen && 1552 sys::path::is_separator(Trimmed.back())) 1553 Trimmed = Trimmed.slice(0, Trimmed.size() - 1); 1554 // Get the last component 1555 StringRef LastComponent = sys::path::filename(Trimmed); 1556 1557 std::unique_ptr<Entry> Result; 1558 switch (Kind) { 1559 case EK_File: 1560 Result = llvm::make_unique<RedirectingFileEntry>( 1561 LastComponent, std::move(ExternalContentsPath), UseExternalName); 1562 break; 1563 case EK_Directory: 1564 Result = llvm::make_unique<RedirectingDirectoryEntry>( 1565 LastComponent, std::move(EntryArrayContents), 1566 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(), 1567 0, 0, 0, file_type::directory_file, sys::fs::all_all)); 1568 break; 1569 } 1570 1571 StringRef Parent = sys::path::parent_path(Trimmed); 1572 if (Parent.empty()) 1573 return Result; 1574 1575 // if 'name' contains multiple components, create implicit directory entries 1576 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent), 1577 E = sys::path::rend(Parent); 1578 I != E; ++I) { 1579 std::vector<std::unique_ptr<Entry>> Entries; 1580 Entries.push_back(std::move(Result)); 1581 Result = llvm::make_unique<RedirectingDirectoryEntry>( 1582 *I, std::move(Entries), 1583 Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(), 1584 0, 0, 0, file_type::directory_file, sys::fs::all_all)); 1585 } 1586 return Result; 1587 } 1588 1589 public: 1590 RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {} 1591 1592 // false on error 1593 bool parse(yaml::Node *Root, RedirectingFileSystem *FS) { 1594 auto *Top = dyn_cast<yaml::MappingNode>(Root); 1595 if (!Top) { 1596 error(Root, "expected mapping node"); 1597 return false; 1598 } 1599 1600 KeyStatusPair Fields[] = { 1601 KeyStatusPair("version", true), 1602 KeyStatusPair("case-sensitive", false), 1603 KeyStatusPair("use-external-names", false), 1604 KeyStatusPair("overlay-relative", false), 1605 KeyStatusPair("fallthrough", false), 1606 KeyStatusPair("roots", true), 1607 }; 1608 1609 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields)); 1610 std::vector<std::unique_ptr<Entry>> RootEntries; 1611 1612 // Parse configuration and 'roots' 1613 for (auto &I : *Top) { 1614 SmallString<10> KeyBuffer; 1615 StringRef Key; 1616 if (!parseScalarString(I.getKey(), Key, KeyBuffer)) 1617 return false; 1618 1619 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) 1620 return false; 1621 1622 if (Key == "roots") { 1623 auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue()); 1624 if (!Roots) { 1625 error(I.getValue(), "expected array"); 1626 return false; 1627 } 1628 1629 for (auto &I : *Roots) { 1630 if (std::unique_ptr<Entry> E = 1631 parseEntry(&I, FS, /*IsRootEntry*/ true)) 1632 RootEntries.push_back(std::move(E)); 1633 else 1634 return false; 1635 } 1636 } else if (Key == "version") { 1637 StringRef VersionString; 1638 SmallString<4> Storage; 1639 if (!parseScalarString(I.getValue(), VersionString, Storage)) 1640 return false; 1641 int Version; 1642 if (VersionString.getAsInteger<int>(10, Version)) { 1643 error(I.getValue(), "expected integer"); 1644 return false; 1645 } 1646 if (Version < 0) { 1647 error(I.getValue(), "invalid version number"); 1648 return false; 1649 } 1650 if (Version != 0) { 1651 error(I.getValue(), "version mismatch, expected 0"); 1652 return false; 1653 } 1654 } else if (Key == "case-sensitive") { 1655 if (!parseScalarBool(I.getValue(), FS->CaseSensitive)) 1656 return false; 1657 } else if (Key == "overlay-relative") { 1658 if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay)) 1659 return false; 1660 } else if (Key == "use-external-names") { 1661 if (!parseScalarBool(I.getValue(), FS->UseExternalNames)) 1662 return false; 1663 } else if (Key == "fallthrough") { 1664 if (!parseScalarBool(I.getValue(), FS->IsFallthrough)) 1665 return false; 1666 } else { 1667 llvm_unreachable("key missing from Keys"); 1668 } 1669 } 1670 1671 if (Stream.failed()) 1672 return false; 1673 1674 if (!checkMissingKeys(Top, Keys)) 1675 return false; 1676 1677 // Now that we sucessefully parsed the YAML file, canonicalize the internal 1678 // representation to a proper directory tree so that we can search faster 1679 // inside the VFS. 1680 for (auto &E : RootEntries) 1681 uniqueOverlayTree(FS, E.get()); 1682 1683 return true; 1684 } 1685 }; 1686 1687 } // namespace 1688 1689 RedirectingFileSystem * 1690 RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer, 1691 SourceMgr::DiagHandlerTy DiagHandler, 1692 StringRef YAMLFilePath, void *DiagContext, 1693 IntrusiveRefCntPtr<FileSystem> ExternalFS) { 1694 SourceMgr SM; 1695 yaml::Stream Stream(Buffer->getMemBufferRef(), SM); 1696 1697 SM.setDiagHandler(DiagHandler, DiagContext); 1698 yaml::document_iterator DI = Stream.begin(); 1699 yaml::Node *Root = DI->getRoot(); 1700 if (DI == Stream.end() || !Root) { 1701 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node"); 1702 return nullptr; 1703 } 1704 1705 RedirectingFileSystemParser P(Stream); 1706 1707 std::unique_ptr<RedirectingFileSystem> FS( 1708 new RedirectingFileSystem(std::move(ExternalFS))); 1709 1710 if (!YAMLFilePath.empty()) { 1711 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed 1712 // to each 'external-contents' path. 1713 // 1714 // Example: 1715 // -ivfsoverlay dummy.cache/vfs/vfs.yaml 1716 // yields: 1717 // FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs 1718 // 1719 SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath); 1720 std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir); 1721 assert(!EC && "Overlay dir final path must be absolute"); 1722 (void)EC; 1723 FS->setExternalContentsPrefixDir(OverlayAbsDir); 1724 } 1725 1726 if (!P.parse(Root, FS.get())) 1727 return nullptr; 1728 1729 return FS.release(); 1730 } 1731 1732 ErrorOr<Entry *> RedirectingFileSystem::lookupPath(const Twine &Path_) const { 1733 SmallString<256> Path; 1734 Path_.toVector(Path); 1735 1736 // Handle relative paths 1737 if (std::error_code EC = makeAbsolute(Path)) 1738 return EC; 1739 1740 // Canonicalize path by removing ".", "..", "./", etc components. This is 1741 // a VFS request, do bot bother about symlinks in the path components 1742 // but canonicalize in order to perform the correct entry search. 1743 if (UseCanonicalizedPaths) { 1744 Path = sys::path::remove_leading_dotslash(Path); 1745 sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 1746 } 1747 1748 if (Path.empty()) 1749 return make_error_code(llvm::errc::invalid_argument); 1750 1751 sys::path::const_iterator Start = sys::path::begin(Path); 1752 sys::path::const_iterator End = sys::path::end(Path); 1753 for (const auto &Root : Roots) { 1754 ErrorOr<Entry *> Result = lookupPath(Start, End, Root.get()); 1755 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) 1756 return Result; 1757 } 1758 return make_error_code(llvm::errc::no_such_file_or_directory); 1759 } 1760 1761 ErrorOr<Entry *> 1762 RedirectingFileSystem::lookupPath(sys::path::const_iterator Start, 1763 sys::path::const_iterator End, 1764 Entry *From) const { 1765 #ifndef _WIN32 1766 assert(!isTraversalComponent(*Start) && 1767 !isTraversalComponent(From->getName()) && 1768 "Paths should not contain traversal components"); 1769 #else 1770 // FIXME: this is here to support windows, remove it once canonicalized 1771 // paths become globally default. 1772 if (Start->equals(".")) 1773 ++Start; 1774 #endif 1775 1776 StringRef FromName = From->getName(); 1777 1778 // Forward the search to the next component in case this is an empty one. 1779 if (!FromName.empty()) { 1780 if (CaseSensitive ? !Start->equals(FromName) 1781 : !Start->equals_lower(FromName)) 1782 // failure to match 1783 return make_error_code(llvm::errc::no_such_file_or_directory); 1784 1785 ++Start; 1786 1787 if (Start == End) { 1788 // Match! 1789 return From; 1790 } 1791 } 1792 1793 auto *DE = dyn_cast<RedirectingDirectoryEntry>(From); 1794 if (!DE) 1795 return make_error_code(llvm::errc::not_a_directory); 1796 1797 for (const std::unique_ptr<Entry> &DirEntry : 1798 llvm::make_range(DE->contents_begin(), DE->contents_end())) { 1799 ErrorOr<Entry *> Result = lookupPath(Start, End, DirEntry.get()); 1800 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) 1801 return Result; 1802 } 1803 return make_error_code(llvm::errc::no_such_file_or_directory); 1804 } 1805 1806 static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames, 1807 Status ExternalStatus) { 1808 Status S = ExternalStatus; 1809 if (!UseExternalNames) 1810 S = Status::copyWithNewName(S, Path.str()); 1811 S.IsVFSMapped = true; 1812 return S; 1813 } 1814 1815 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) { 1816 assert(E != nullptr); 1817 if (auto *F = dyn_cast<RedirectingFileEntry>(E)) { 1818 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath()); 1819 assert(!S || S->getName() == F->getExternalContentsPath()); 1820 if (S) 1821 return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), 1822 *S); 1823 return S; 1824 } else { // directory 1825 auto *DE = cast<RedirectingDirectoryEntry>(E); 1826 return Status::copyWithNewName(DE->getStatus(), Path.str()); 1827 } 1828 } 1829 1830 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) { 1831 ErrorOr<Entry *> Result = lookupPath(Path); 1832 if (!Result) { 1833 if (IsFallthrough && 1834 Result.getError() == llvm::errc::no_such_file_or_directory) { 1835 return ExternalFS->status(Path); 1836 } 1837 return Result.getError(); 1838 } 1839 return status(Path, *Result); 1840 } 1841 1842 namespace { 1843 1844 /// Provide a file wrapper with an overriden status. 1845 class FileWithFixedStatus : public File { 1846 std::unique_ptr<File> InnerFile; 1847 Status S; 1848 1849 public: 1850 FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S) 1851 : InnerFile(std::move(InnerFile)), S(std::move(S)) {} 1852 1853 ErrorOr<Status> status() override { return S; } 1854 ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 1855 1856 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 1857 bool IsVolatile) override { 1858 return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator, 1859 IsVolatile); 1860 } 1861 1862 std::error_code close() override { return InnerFile->close(); } 1863 }; 1864 1865 } // namespace 1866 1867 ErrorOr<std::unique_ptr<File>> 1868 RedirectingFileSystem::openFileForRead(const Twine &Path) { 1869 ErrorOr<Entry *> E = lookupPath(Path); 1870 if (!E) { 1871 if (IsFallthrough && 1872 E.getError() == llvm::errc::no_such_file_or_directory) { 1873 return ExternalFS->openFileForRead(Path); 1874 } 1875 return E.getError(); 1876 } 1877 1878 auto *F = dyn_cast<RedirectingFileEntry>(*E); 1879 if (!F) // FIXME: errc::not_a_file? 1880 return make_error_code(llvm::errc::invalid_argument); 1881 1882 auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath()); 1883 if (!Result) 1884 return Result; 1885 1886 auto ExternalStatus = (*Result)->status(); 1887 if (!ExternalStatus) 1888 return ExternalStatus.getError(); 1889 1890 // FIXME: Update the status with the name and VFSMapped. 1891 Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), 1892 *ExternalStatus); 1893 return std::unique_ptr<File>( 1894 llvm::make_unique<FileWithFixedStatus>(std::move(*Result), S)); 1895 } 1896 1897 std::error_code 1898 RedirectingFileSystem::getRealPath(const Twine &Path, 1899 SmallVectorImpl<char> &Output) const { 1900 ErrorOr<Entry *> Result = lookupPath(Path); 1901 if (!Result) { 1902 if (IsFallthrough && 1903 Result.getError() == llvm::errc::no_such_file_or_directory) { 1904 return ExternalFS->getRealPath(Path, Output); 1905 } 1906 return Result.getError(); 1907 } 1908 1909 if (auto *F = dyn_cast<RedirectingFileEntry>(*Result)) { 1910 return ExternalFS->getRealPath(F->getExternalContentsPath(), Output); 1911 } 1912 // Even if there is a directory entry, fall back to ExternalFS if allowed, 1913 // because directories don't have a single external contents path. 1914 return IsFallthrough ? ExternalFS->getRealPath(Path, Output) 1915 : llvm::errc::invalid_argument; 1916 } 1917 1918 IntrusiveRefCntPtr<FileSystem> 1919 vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer, 1920 SourceMgr::DiagHandlerTy DiagHandler, 1921 StringRef YAMLFilePath, void *DiagContext, 1922 IntrusiveRefCntPtr<FileSystem> ExternalFS) { 1923 return RedirectingFileSystem::create(std::move(Buffer), DiagHandler, 1924 YAMLFilePath, DiagContext, 1925 std::move(ExternalFS)); 1926 } 1927 1928 static void getVFSEntries(Entry *SrcE, SmallVectorImpl<StringRef> &Path, 1929 SmallVectorImpl<YAMLVFSEntry> &Entries) { 1930 auto Kind = SrcE->getKind(); 1931 if (Kind == EK_Directory) { 1932 auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE); 1933 assert(DE && "Must be a directory"); 1934 for (std::unique_ptr<Entry> &SubEntry : 1935 llvm::make_range(DE->contents_begin(), DE->contents_end())) { 1936 Path.push_back(SubEntry->getName()); 1937 getVFSEntries(SubEntry.get(), Path, Entries); 1938 Path.pop_back(); 1939 } 1940 return; 1941 } 1942 1943 assert(Kind == EK_File && "Must be a EK_File"); 1944 auto *FE = dyn_cast<RedirectingFileEntry>(SrcE); 1945 assert(FE && "Must be a file"); 1946 SmallString<128> VPath; 1947 for (auto &Comp : Path) 1948 llvm::sys::path::append(VPath, Comp); 1949 Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath())); 1950 } 1951 1952 void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer, 1953 SourceMgr::DiagHandlerTy DiagHandler, 1954 StringRef YAMLFilePath, 1955 SmallVectorImpl<YAMLVFSEntry> &CollectedEntries, 1956 void *DiagContext, 1957 IntrusiveRefCntPtr<FileSystem> ExternalFS) { 1958 RedirectingFileSystem *VFS = RedirectingFileSystem::create( 1959 std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext, 1960 std::move(ExternalFS)); 1961 ErrorOr<Entry *> RootE = VFS->lookupPath("/"); 1962 if (!RootE) 1963 return; 1964 SmallVector<StringRef, 8> Components; 1965 Components.push_back("/"); 1966 getVFSEntries(*RootE, Components, CollectedEntries); 1967 } 1968 1969 UniqueID vfs::getNextVirtualUniqueID() { 1970 static std::atomic<unsigned> UID; 1971 unsigned ID = ++UID; 1972 // The following assumes that uint64_t max will never collide with a real 1973 // dev_t value from the OS. 1974 return UniqueID(std::numeric_limits<uint64_t>::max(), ID); 1975 } 1976 1977 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) { 1978 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute"); 1979 assert(sys::path::is_absolute(RealPath) && "real path not absolute"); 1980 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported"); 1981 Mappings.emplace_back(VirtualPath, RealPath); 1982 } 1983 1984 namespace { 1985 1986 class JSONWriter { 1987 llvm::raw_ostream &OS; 1988 SmallVector<StringRef, 16> DirStack; 1989 1990 unsigned getDirIndent() { return 4 * DirStack.size(); } 1991 unsigned getFileIndent() { return 4 * (DirStack.size() + 1); } 1992 bool containedIn(StringRef Parent, StringRef Path); 1993 StringRef containedPart(StringRef Parent, StringRef Path); 1994 void startDirectory(StringRef Path); 1995 void endDirectory(); 1996 void writeEntry(StringRef VPath, StringRef RPath); 1997 1998 public: 1999 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {} 2000 2001 void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> UseExternalNames, 2002 Optional<bool> IsCaseSensitive, Optional<bool> IsOverlayRelative, 2003 StringRef OverlayDir); 2004 }; 2005 2006 } // namespace 2007 2008 bool JSONWriter::containedIn(StringRef Parent, StringRef Path) { 2009 using namespace llvm::sys; 2010 2011 // Compare each path component. 2012 auto IParent = path::begin(Parent), EParent = path::end(Parent); 2013 for (auto IChild = path::begin(Path), EChild = path::end(Path); 2014 IParent != EParent && IChild != EChild; ++IParent, ++IChild) { 2015 if (*IParent != *IChild) 2016 return false; 2017 } 2018 // Have we exhausted the parent path? 2019 return IParent == EParent; 2020 } 2021 2022 StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) { 2023 assert(!Parent.empty()); 2024 assert(containedIn(Parent, Path)); 2025 return Path.slice(Parent.size() + 1, StringRef::npos); 2026 } 2027 2028 void JSONWriter::startDirectory(StringRef Path) { 2029 StringRef Name = 2030 DirStack.empty() ? Path : containedPart(DirStack.back(), Path); 2031 DirStack.push_back(Path); 2032 unsigned Indent = getDirIndent(); 2033 OS.indent(Indent) << "{\n"; 2034 OS.indent(Indent + 2) << "'type': 'directory',\n"; 2035 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n"; 2036 OS.indent(Indent + 2) << "'contents': [\n"; 2037 } 2038 2039 void JSONWriter::endDirectory() { 2040 unsigned Indent = getDirIndent(); 2041 OS.indent(Indent + 2) << "]\n"; 2042 OS.indent(Indent) << "}"; 2043 2044 DirStack.pop_back(); 2045 } 2046 2047 void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) { 2048 unsigned Indent = getFileIndent(); 2049 OS.indent(Indent) << "{\n"; 2050 OS.indent(Indent + 2) << "'type': 'file',\n"; 2051 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n"; 2052 OS.indent(Indent + 2) << "'external-contents': \"" 2053 << llvm::yaml::escape(RPath) << "\"\n"; 2054 OS.indent(Indent) << "}"; 2055 } 2056 2057 void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries, 2058 Optional<bool> UseExternalNames, 2059 Optional<bool> IsCaseSensitive, 2060 Optional<bool> IsOverlayRelative, 2061 StringRef OverlayDir) { 2062 using namespace llvm::sys; 2063 2064 OS << "{\n" 2065 " 'version': 0,\n"; 2066 if (IsCaseSensitive.hasValue()) 2067 OS << " 'case-sensitive': '" 2068 << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n"; 2069 if (UseExternalNames.hasValue()) 2070 OS << " 'use-external-names': '" 2071 << (UseExternalNames.getValue() ? "true" : "false") << "',\n"; 2072 bool UseOverlayRelative = false; 2073 if (IsOverlayRelative.hasValue()) { 2074 UseOverlayRelative = IsOverlayRelative.getValue(); 2075 OS << " 'overlay-relative': '" << (UseOverlayRelative ? "true" : "false") 2076 << "',\n"; 2077 } 2078 OS << " 'roots': [\n"; 2079 2080 if (!Entries.empty()) { 2081 const YAMLVFSEntry &Entry = Entries.front(); 2082 startDirectory(path::parent_path(Entry.VPath)); 2083 2084 StringRef RPath = Entry.RPath; 2085 if (UseOverlayRelative) { 2086 unsigned OverlayDirLen = OverlayDir.size(); 2087 assert(RPath.substr(0, OverlayDirLen) == OverlayDir && 2088 "Overlay dir must be contained in RPath"); 2089 RPath = RPath.slice(OverlayDirLen, RPath.size()); 2090 } 2091 2092 writeEntry(path::filename(Entry.VPath), RPath); 2093 2094 for (const auto &Entry : Entries.slice(1)) { 2095 StringRef Dir = path::parent_path(Entry.VPath); 2096 if (Dir == DirStack.back()) 2097 OS << ",\n"; 2098 else { 2099 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) { 2100 OS << "\n"; 2101 endDirectory(); 2102 } 2103 OS << ",\n"; 2104 startDirectory(Dir); 2105 } 2106 StringRef RPath = Entry.RPath; 2107 if (UseOverlayRelative) { 2108 unsigned OverlayDirLen = OverlayDir.size(); 2109 assert(RPath.substr(0, OverlayDirLen) == OverlayDir && 2110 "Overlay dir must be contained in RPath"); 2111 RPath = RPath.slice(OverlayDirLen, RPath.size()); 2112 } 2113 writeEntry(path::filename(Entry.VPath), RPath); 2114 } 2115 2116 while (!DirStack.empty()) { 2117 OS << "\n"; 2118 endDirectory(); 2119 } 2120 OS << "\n"; 2121 } 2122 2123 OS << " ]\n" 2124 << "}\n"; 2125 } 2126 2127 void YAMLVFSWriter::write(llvm::raw_ostream &OS) { 2128 llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) { 2129 return LHS.VPath < RHS.VPath; 2130 }); 2131 2132 JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive, 2133 IsOverlayRelative, OverlayDir); 2134 } 2135 2136 VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl( 2137 const Twine &_Path, RedirectingDirectoryEntry::iterator Begin, 2138 RedirectingDirectoryEntry::iterator End, bool IterateExternalFS, 2139 FileSystem &ExternalFS, std::error_code &EC) 2140 : Dir(_Path.str()), Current(Begin), End(End), 2141 IterateExternalFS(IterateExternalFS), ExternalFS(ExternalFS) { 2142 EC = incrementImpl(/*IsFirstTime=*/true); 2143 } 2144 2145 std::error_code VFSFromYamlDirIterImpl::increment() { 2146 return incrementImpl(/*IsFirstTime=*/false); 2147 } 2148 2149 std::error_code VFSFromYamlDirIterImpl::incrementExternal() { 2150 assert(!(IsExternalFSCurrent && ExternalDirIter == directory_iterator()) && 2151 "incrementing past end"); 2152 std::error_code EC; 2153 if (IsExternalFSCurrent) { 2154 ExternalDirIter.increment(EC); 2155 } else if (IterateExternalFS) { 2156 ExternalDirIter = ExternalFS.dir_begin(Dir, EC); 2157 IsExternalFSCurrent = true; 2158 if (EC && EC != errc::no_such_file_or_directory) 2159 return EC; 2160 EC = {}; 2161 } 2162 if (EC || ExternalDirIter == directory_iterator()) { 2163 CurrentEntry = directory_entry(); 2164 } else { 2165 CurrentEntry = *ExternalDirIter; 2166 } 2167 return EC; 2168 } 2169 2170 std::error_code VFSFromYamlDirIterImpl::incrementContent(bool IsFirstTime) { 2171 assert((IsFirstTime || Current != End) && "cannot iterate past end"); 2172 if (!IsFirstTime) 2173 ++Current; 2174 while (Current != End) { 2175 SmallString<128> PathStr(Dir); 2176 llvm::sys::path::append(PathStr, (*Current)->getName()); 2177 sys::fs::file_type Type; 2178 switch ((*Current)->getKind()) { 2179 case EK_Directory: 2180 Type = sys::fs::file_type::directory_file; 2181 break; 2182 case EK_File: 2183 Type = sys::fs::file_type::regular_file; 2184 break; 2185 } 2186 CurrentEntry = directory_entry(PathStr.str(), Type); 2187 return {}; 2188 } 2189 return incrementExternal(); 2190 } 2191 2192 std::error_code VFSFromYamlDirIterImpl::incrementImpl(bool IsFirstTime) { 2193 while (true) { 2194 std::error_code EC = IsExternalFSCurrent ? incrementExternal() 2195 : incrementContent(IsFirstTime); 2196 if (EC || CurrentEntry.path().empty()) 2197 return EC; 2198 StringRef Name = llvm::sys::path::filename(CurrentEntry.path()); 2199 if (SeenNames.insert(Name).second) 2200 return EC; // name not seen before 2201 } 2202 llvm_unreachable("returned above"); 2203 } 2204 2205 vfs::recursive_directory_iterator::recursive_directory_iterator( 2206 FileSystem &FS_, const Twine &Path, std::error_code &EC) 2207 : FS(&FS_) { 2208 directory_iterator I = FS->dir_begin(Path, EC); 2209 if (I != directory_iterator()) { 2210 State = std::make_shared<detail::RecDirIterState>(); 2211 State->Stack.push(I); 2212 } 2213 } 2214 2215 vfs::recursive_directory_iterator & 2216 recursive_directory_iterator::increment(std::error_code &EC) { 2217 assert(FS && State && !State->Stack.empty() && "incrementing past end"); 2218 assert(!State->Stack.top()->path().empty() && "non-canonical end iterator"); 2219 vfs::directory_iterator End; 2220 2221 if (State->HasNoPushRequest) 2222 State->HasNoPushRequest = false; 2223 else { 2224 if (State->Stack.top()->type() == sys::fs::file_type::directory_file) { 2225 vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC); 2226 if (I != End) { 2227 State->Stack.push(I); 2228 return *this; 2229 } 2230 } 2231 } 2232 2233 while (!State->Stack.empty() && State->Stack.top().increment(EC) == End) 2234 State->Stack.pop(); 2235 2236 if (State->Stack.empty()) 2237 State.reset(); // end iterator 2238 2239 return *this; 2240 } 2241