1 //===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements the VirtualFileSystem interface. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/Support/VirtualFileSystem.h" 14 #include "llvm/ADT/ArrayRef.h" 15 #include "llvm/ADT/DenseMap.h" 16 #include "llvm/ADT/IntrusiveRefCntPtr.h" 17 #include "llvm/ADT/None.h" 18 #include "llvm/ADT/Optional.h" 19 #include "llvm/ADT/STLExtras.h" 20 #include "llvm/ADT/SmallString.h" 21 #include "llvm/ADT/SmallVector.h" 22 #include "llvm/ADT/StringRef.h" 23 #include "llvm/ADT/StringSet.h" 24 #include "llvm/ADT/Twine.h" 25 #include "llvm/ADT/iterator_range.h" 26 #include "llvm/Config/llvm-config.h" 27 #include "llvm/Support/Casting.h" 28 #include "llvm/Support/Chrono.h" 29 #include "llvm/Support/Compiler.h" 30 #include "llvm/Support/Debug.h" 31 #include "llvm/Support/Errc.h" 32 #include "llvm/Support/ErrorHandling.h" 33 #include "llvm/Support/ErrorOr.h" 34 #include "llvm/Support/FileSystem.h" 35 #include "llvm/Support/MemoryBuffer.h" 36 #include "llvm/Support/Path.h" 37 #include "llvm/Support/Process.h" 38 #include "llvm/Support/SMLoc.h" 39 #include "llvm/Support/SourceMgr.h" 40 #include "llvm/Support/YAMLParser.h" 41 #include "llvm/Support/raw_ostream.h" 42 #include <algorithm> 43 #include <atomic> 44 #include <cassert> 45 #include <cstdint> 46 #include <iterator> 47 #include <limits> 48 #include <map> 49 #include <memory> 50 #include <mutex> 51 #include <string> 52 #include <system_error> 53 #include <utility> 54 #include <vector> 55 56 using namespace llvm; 57 using namespace llvm::vfs; 58 59 using llvm::sys::fs::file_status; 60 using llvm::sys::fs::file_type; 61 using llvm::sys::fs::perms; 62 using llvm::sys::fs::UniqueID; 63 64 Status::Status(const file_status &Status) 65 : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()), 66 User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()), 67 Type(Status.type()), Perms(Status.permissions()) {} 68 69 Status::Status(StringRef Name, UniqueID UID, sys::TimePoint<> MTime, 70 uint32_t User, uint32_t Group, uint64_t Size, file_type Type, 71 perms Perms) 72 : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size), 73 Type(Type), Perms(Perms) {} 74 75 Status Status::copyWithNewName(const Status &In, StringRef NewName) { 76 return Status(NewName, In.getUniqueID(), In.getLastModificationTime(), 77 In.getUser(), In.getGroup(), In.getSize(), In.getType(), 78 In.getPermissions()); 79 } 80 81 Status Status::copyWithNewName(const file_status &In, StringRef NewName) { 82 return Status(NewName, In.getUniqueID(), In.getLastModificationTime(), 83 In.getUser(), In.getGroup(), In.getSize(), In.type(), 84 In.permissions()); 85 } 86 87 bool Status::equivalent(const Status &Other) const { 88 assert(isStatusKnown() && Other.isStatusKnown()); 89 return getUniqueID() == Other.getUniqueID(); 90 } 91 92 bool Status::isDirectory() const { return Type == file_type::directory_file; } 93 94 bool Status::isRegularFile() const { return Type == file_type::regular_file; } 95 96 bool Status::isOther() const { 97 return exists() && !isRegularFile() && !isDirectory() && !isSymlink(); 98 } 99 100 bool Status::isSymlink() const { return Type == file_type::symlink_file; } 101 102 bool Status::isStatusKnown() const { return Type != file_type::status_error; } 103 104 bool Status::exists() const { 105 return isStatusKnown() && Type != file_type::file_not_found; 106 } 107 108 File::~File() = default; 109 110 FileSystem::~FileSystem() = default; 111 112 ErrorOr<std::unique_ptr<MemoryBuffer>> 113 FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize, 114 bool RequiresNullTerminator, bool IsVolatile) { 115 auto F = openFileForRead(Name); 116 if (!F) 117 return F.getError(); 118 119 return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile); 120 } 121 122 std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const { 123 if (llvm::sys::path::is_absolute(Path)) 124 return {}; 125 126 auto WorkingDir = getCurrentWorkingDirectory(); 127 if (!WorkingDir) 128 return WorkingDir.getError(); 129 130 llvm::sys::fs::make_absolute(WorkingDir.get(), Path); 131 return {}; 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 void ProxyFileSystem::anchor() {} 476 477 namespace llvm { 478 namespace vfs { 479 480 namespace detail { 481 482 enum InMemoryNodeKind { IME_File, IME_Directory, IME_HardLink }; 483 484 /// The in memory file system is a tree of Nodes. Every node can either be a 485 /// file , hardlink or a directory. 486 class InMemoryNode { 487 InMemoryNodeKind Kind; 488 std::string FileName; 489 490 public: 491 InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind) 492 : Kind(Kind), FileName(llvm::sys::path::filename(FileName)) {} 493 virtual ~InMemoryNode() = default; 494 495 /// Get the filename of this node (the name without the directory part). 496 StringRef getFileName() const { return FileName; } 497 InMemoryNodeKind getKind() const { return Kind; } 498 virtual std::string toString(unsigned Indent) const = 0; 499 }; 500 501 class InMemoryFile : public InMemoryNode { 502 Status Stat; 503 std::unique_ptr<llvm::MemoryBuffer> Buffer; 504 505 public: 506 InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer) 507 : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)), 508 Buffer(std::move(Buffer)) {} 509 510 /// Return the \p Status for this node. \p RequestedName should be the name 511 /// through which the caller referred to this node. It will override 512 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile. 513 Status getStatus(StringRef RequestedName) const { 514 return Status::copyWithNewName(Stat, RequestedName); 515 } 516 llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); } 517 518 std::string toString(unsigned Indent) const override { 519 return (std::string(Indent, ' ') + Stat.getName() + "\n").str(); 520 } 521 522 static bool classof(const InMemoryNode *N) { 523 return N->getKind() == IME_File; 524 } 525 }; 526 527 namespace { 528 529 class InMemoryHardLink : public InMemoryNode { 530 const InMemoryFile &ResolvedFile; 531 532 public: 533 InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile) 534 : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {} 535 const InMemoryFile &getResolvedFile() const { return ResolvedFile; } 536 537 std::string toString(unsigned Indent) const override { 538 return std::string(Indent, ' ') + "HardLink to -> " + 539 ResolvedFile.toString(0); 540 } 541 542 static bool classof(const InMemoryNode *N) { 543 return N->getKind() == IME_HardLink; 544 } 545 }; 546 547 /// Adapt a InMemoryFile for VFS' File interface. The goal is to make 548 /// \p InMemoryFileAdaptor mimic as much as possible the behavior of 549 /// \p RealFile. 550 class InMemoryFileAdaptor : public File { 551 const InMemoryFile &Node; 552 /// The name to use when returning a Status for this file. 553 std::string RequestedName; 554 555 public: 556 explicit InMemoryFileAdaptor(const InMemoryFile &Node, 557 std::string RequestedName) 558 : Node(Node), RequestedName(std::move(RequestedName)) {} 559 560 llvm::ErrorOr<Status> status() override { 561 return Node.getStatus(RequestedName); 562 } 563 564 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 565 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 566 bool IsVolatile) override { 567 llvm::MemoryBuffer *Buf = Node.getBuffer(); 568 return llvm::MemoryBuffer::getMemBuffer( 569 Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator); 570 } 571 572 std::error_code close() override { return {}; } 573 }; 574 } // namespace 575 576 class InMemoryDirectory : public InMemoryNode { 577 Status Stat; 578 llvm::StringMap<std::unique_ptr<InMemoryNode>> Entries; 579 580 public: 581 InMemoryDirectory(Status Stat) 582 : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {} 583 584 /// Return the \p Status for this node. \p RequestedName should be the name 585 /// through which the caller referred to this node. It will override 586 /// \p Status::Name in the return value, to mimic the behavior of \p RealFile. 587 Status getStatus(StringRef RequestedName) const { 588 return Status::copyWithNewName(Stat, RequestedName); 589 } 590 InMemoryNode *getChild(StringRef Name) { 591 auto I = Entries.find(Name); 592 if (I != Entries.end()) 593 return I->second.get(); 594 return nullptr; 595 } 596 597 InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) { 598 return Entries.insert(make_pair(Name, std::move(Child))) 599 .first->second.get(); 600 } 601 602 using const_iterator = decltype(Entries)::const_iterator; 603 604 const_iterator begin() const { return Entries.begin(); } 605 const_iterator end() const { return Entries.end(); } 606 607 std::string toString(unsigned Indent) const override { 608 std::string Result = 609 (std::string(Indent, ' ') + Stat.getName() + "\n").str(); 610 for (const auto &Entry : Entries) 611 Result += Entry.second->toString(Indent + 2); 612 return Result; 613 } 614 615 static bool classof(const InMemoryNode *N) { 616 return N->getKind() == IME_Directory; 617 } 618 }; 619 620 namespace { 621 Status getNodeStatus(const InMemoryNode *Node, StringRef RequestedName) { 622 if (auto Dir = dyn_cast<detail::InMemoryDirectory>(Node)) 623 return Dir->getStatus(RequestedName); 624 if (auto File = dyn_cast<detail::InMemoryFile>(Node)) 625 return File->getStatus(RequestedName); 626 if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) 627 return Link->getResolvedFile().getStatus(RequestedName); 628 llvm_unreachable("Unknown node type"); 629 } 630 } // namespace 631 } // namespace detail 632 633 InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths) 634 : Root(new detail::InMemoryDirectory( 635 Status("", getNextVirtualUniqueID(), llvm::sys::TimePoint<>(), 0, 0, 636 0, llvm::sys::fs::file_type::directory_file, 637 llvm::sys::fs::perms::all_all))), 638 UseNormalizedPaths(UseNormalizedPaths) {} 639 640 InMemoryFileSystem::~InMemoryFileSystem() = default; 641 642 std::string InMemoryFileSystem::toString() const { 643 return Root->toString(/*Indent=*/0); 644 } 645 646 bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime, 647 std::unique_ptr<llvm::MemoryBuffer> Buffer, 648 Optional<uint32_t> User, 649 Optional<uint32_t> Group, 650 Optional<llvm::sys::fs::file_type> Type, 651 Optional<llvm::sys::fs::perms> Perms, 652 const detail::InMemoryFile *HardLinkTarget) { 653 SmallString<128> Path; 654 P.toVector(Path); 655 656 // Fix up relative paths. This just prepends the current working directory. 657 std::error_code EC = makeAbsolute(Path); 658 assert(!EC); 659 (void)EC; 660 661 if (useNormalizedPaths()) 662 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 663 664 if (Path.empty()) 665 return false; 666 667 detail::InMemoryDirectory *Dir = Root.get(); 668 auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path); 669 const auto ResolvedUser = User.getValueOr(0); 670 const auto ResolvedGroup = Group.getValueOr(0); 671 const auto ResolvedType = Type.getValueOr(sys::fs::file_type::regular_file); 672 const auto ResolvedPerms = Perms.getValueOr(sys::fs::all_all); 673 assert(!(HardLinkTarget && Buffer) && "HardLink cannot have a buffer"); 674 // Any intermediate directories we create should be accessible by 675 // the owner, even if Perms says otherwise for the final path. 676 const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all; 677 while (true) { 678 StringRef Name = *I; 679 detail::InMemoryNode *Node = Dir->getChild(Name); 680 ++I; 681 if (!Node) { 682 if (I == E) { 683 // End of the path. 684 std::unique_ptr<detail::InMemoryNode> Child; 685 if (HardLinkTarget) 686 Child.reset(new detail::InMemoryHardLink(P.str(), *HardLinkTarget)); 687 else { 688 // Create a new file or directory. 689 Status Stat(P.str(), getNextVirtualUniqueID(), 690 llvm::sys::toTimePoint(ModificationTime), ResolvedUser, 691 ResolvedGroup, Buffer->getBufferSize(), ResolvedType, 692 ResolvedPerms); 693 if (ResolvedType == sys::fs::file_type::directory_file) { 694 Child.reset(new detail::InMemoryDirectory(std::move(Stat))); 695 } else { 696 Child.reset( 697 new detail::InMemoryFile(std::move(Stat), std::move(Buffer))); 698 } 699 } 700 Dir->addChild(Name, std::move(Child)); 701 return true; 702 } 703 704 // Create a new directory. Use the path up to here. 705 Status Stat( 706 StringRef(Path.str().begin(), Name.end() - Path.str().begin()), 707 getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime), 708 ResolvedUser, ResolvedGroup, 0, sys::fs::file_type::directory_file, 709 NewDirectoryPerms); 710 Dir = cast<detail::InMemoryDirectory>(Dir->addChild( 711 Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat)))); 712 continue; 713 } 714 715 if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) { 716 Dir = NewDir; 717 } else { 718 assert((isa<detail::InMemoryFile>(Node) || 719 isa<detail::InMemoryHardLink>(Node)) && 720 "Must be either file, hardlink or directory!"); 721 722 // Trying to insert a directory in place of a file. 723 if (I != E) 724 return false; 725 726 // Return false only if the new file is different from the existing one. 727 if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) { 728 return Link->getResolvedFile().getBuffer()->getBuffer() == 729 Buffer->getBuffer(); 730 } 731 return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() == 732 Buffer->getBuffer(); 733 } 734 } 735 } 736 737 bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime, 738 std::unique_ptr<llvm::MemoryBuffer> Buffer, 739 Optional<uint32_t> User, 740 Optional<uint32_t> Group, 741 Optional<llvm::sys::fs::file_type> Type, 742 Optional<llvm::sys::fs::perms> Perms) { 743 return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type, 744 Perms, /*HardLinkTarget=*/nullptr); 745 } 746 747 bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime, 748 llvm::MemoryBuffer *Buffer, 749 Optional<uint32_t> User, 750 Optional<uint32_t> Group, 751 Optional<llvm::sys::fs::file_type> Type, 752 Optional<llvm::sys::fs::perms> Perms) { 753 return addFile(P, ModificationTime, 754 llvm::MemoryBuffer::getMemBuffer( 755 Buffer->getBuffer(), Buffer->getBufferIdentifier()), 756 std::move(User), std::move(Group), std::move(Type), 757 std::move(Perms)); 758 } 759 760 static ErrorOr<const detail::InMemoryNode *> 761 lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir, 762 const Twine &P) { 763 SmallString<128> Path; 764 P.toVector(Path); 765 766 // Fix up relative paths. This just prepends the current working directory. 767 std::error_code EC = FS.makeAbsolute(Path); 768 assert(!EC); 769 (void)EC; 770 771 if (FS.useNormalizedPaths()) 772 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 773 774 if (Path.empty()) 775 return Dir; 776 777 auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path); 778 while (true) { 779 detail::InMemoryNode *Node = Dir->getChild(*I); 780 ++I; 781 if (!Node) 782 return errc::no_such_file_or_directory; 783 784 // Return the file if it's at the end of the path. 785 if (auto File = dyn_cast<detail::InMemoryFile>(Node)) { 786 if (I == E) 787 return File; 788 return errc::no_such_file_or_directory; 789 } 790 791 // If Node is HardLink then return the resolved file. 792 if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) { 793 if (I == E) 794 return &File->getResolvedFile(); 795 return errc::no_such_file_or_directory; 796 } 797 // Traverse directories. 798 Dir = cast<detail::InMemoryDirectory>(Node); 799 if (I == E) 800 return Dir; 801 } 802 } 803 804 bool InMemoryFileSystem::addHardLink(const Twine &FromPath, 805 const Twine &ToPath) { 806 auto FromNode = lookupInMemoryNode(*this, Root.get(), FromPath); 807 auto ToNode = lookupInMemoryNode(*this, Root.get(), ToPath); 808 // FromPath must not have been added before. ToPath must have been added 809 // before. Resolved ToPath must be a File. 810 if (!ToNode || FromNode || !isa<detail::InMemoryFile>(*ToNode)) 811 return false; 812 return this->addFile(FromPath, 0, nullptr, None, None, None, None, 813 cast<detail::InMemoryFile>(*ToNode)); 814 } 815 816 llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) { 817 auto Node = lookupInMemoryNode(*this, Root.get(), Path); 818 if (Node) 819 return detail::getNodeStatus(*Node, Path.str()); 820 return Node.getError(); 821 } 822 823 llvm::ErrorOr<std::unique_ptr<File>> 824 InMemoryFileSystem::openFileForRead(const Twine &Path) { 825 auto Node = lookupInMemoryNode(*this, Root.get(), Path); 826 if (!Node) 827 return Node.getError(); 828 829 // When we have a file provide a heap-allocated wrapper for the memory buffer 830 // to match the ownership semantics for File. 831 if (auto *F = dyn_cast<detail::InMemoryFile>(*Node)) 832 return std::unique_ptr<File>( 833 new detail::InMemoryFileAdaptor(*F, Path.str())); 834 835 // FIXME: errc::not_a_file? 836 return make_error_code(llvm::errc::invalid_argument); 837 } 838 839 namespace { 840 841 /// Adaptor from InMemoryDir::iterator to directory_iterator. 842 class InMemoryDirIterator : public llvm::vfs::detail::DirIterImpl { 843 detail::InMemoryDirectory::const_iterator I; 844 detail::InMemoryDirectory::const_iterator E; 845 std::string RequestedDirName; 846 847 void setCurrentEntry() { 848 if (I != E) { 849 SmallString<256> Path(RequestedDirName); 850 llvm::sys::path::append(Path, I->second->getFileName()); 851 sys::fs::file_type Type; 852 switch (I->second->getKind()) { 853 case detail::IME_File: 854 case detail::IME_HardLink: 855 Type = sys::fs::file_type::regular_file; 856 break; 857 case detail::IME_Directory: 858 Type = sys::fs::file_type::directory_file; 859 break; 860 } 861 CurrentEntry = directory_entry(Path.str(), Type); 862 } else { 863 // When we're at the end, make CurrentEntry invalid and DirIterImpl will 864 // do the rest. 865 CurrentEntry = directory_entry(); 866 } 867 } 868 869 public: 870 InMemoryDirIterator() = default; 871 872 explicit InMemoryDirIterator(const detail::InMemoryDirectory &Dir, 873 std::string RequestedDirName) 874 : I(Dir.begin()), E(Dir.end()), 875 RequestedDirName(std::move(RequestedDirName)) { 876 setCurrentEntry(); 877 } 878 879 std::error_code increment() override { 880 ++I; 881 setCurrentEntry(); 882 return {}; 883 } 884 }; 885 886 } // namespace 887 888 directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir, 889 std::error_code &EC) { 890 auto Node = lookupInMemoryNode(*this, Root.get(), Dir); 891 if (!Node) { 892 EC = Node.getError(); 893 return directory_iterator(std::make_shared<InMemoryDirIterator>()); 894 } 895 896 if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node)) 897 return directory_iterator( 898 std::make_shared<InMemoryDirIterator>(*DirNode, Dir.str())); 899 900 EC = make_error_code(llvm::errc::not_a_directory); 901 return directory_iterator(std::make_shared<InMemoryDirIterator>()); 902 } 903 904 std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) { 905 SmallString<128> Path; 906 P.toVector(Path); 907 908 // Fix up relative paths. This just prepends the current working directory. 909 std::error_code EC = makeAbsolute(Path); 910 assert(!EC); 911 (void)EC; 912 913 if (useNormalizedPaths()) 914 llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 915 916 if (!Path.empty()) 917 WorkingDirectory = Path.str(); 918 return {}; 919 } 920 921 std::error_code 922 InMemoryFileSystem::getRealPath(const Twine &Path, 923 SmallVectorImpl<char> &Output) const { 924 auto CWD = getCurrentWorkingDirectory(); 925 if (!CWD || CWD->empty()) 926 return errc::operation_not_permitted; 927 Path.toVector(Output); 928 if (auto EC = makeAbsolute(Output)) 929 return EC; 930 llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true); 931 return {}; 932 } 933 934 std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) { 935 Result = false; 936 return {}; 937 } 938 939 } // namespace vfs 940 } // namespace llvm 941 942 //===-----------------------------------------------------------------------===/ 943 // RedirectingFileSystem implementation 944 //===-----------------------------------------------------------------------===/ 945 946 // FIXME: reuse implementation common with OverlayFSDirIterImpl as these 947 // iterators are conceptually similar. 948 class llvm::vfs::VFSFromYamlDirIterImpl 949 : public llvm::vfs::detail::DirIterImpl { 950 std::string Dir; 951 RedirectingFileSystem::RedirectingDirectoryEntry::iterator Current, End; 952 953 // To handle 'fallthrough' mode we need to iterate at first through 954 // RedirectingDirectoryEntry and then through ExternalFS. These operations are 955 // done sequentially, we just need to keep a track of what kind of iteration 956 // we are currently performing. 957 958 /// Flag telling if we should iterate through ExternalFS or stop at the last 959 /// RedirectingDirectoryEntry::iterator. 960 bool IterateExternalFS; 961 /// Flag telling if we have switched to iterating through ExternalFS. 962 bool IsExternalFSCurrent = false; 963 FileSystem &ExternalFS; 964 directory_iterator ExternalDirIter; 965 llvm::StringSet<> SeenNames; 966 967 /// To combine multiple iterations, different methods are responsible for 968 /// different iteration steps. 969 /// @{ 970 971 /// Responsible for dispatching between RedirectingDirectoryEntry iteration 972 /// and ExternalFS iteration. 973 std::error_code incrementImpl(bool IsFirstTime); 974 /// Responsible for RedirectingDirectoryEntry iteration. 975 std::error_code incrementContent(bool IsFirstTime); 976 /// Responsible for ExternalFS iteration. 977 std::error_code incrementExternal(); 978 /// @} 979 980 public: 981 VFSFromYamlDirIterImpl( 982 const Twine &Path, 983 RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin, 984 RedirectingFileSystem::RedirectingDirectoryEntry::iterator End, 985 bool IterateExternalFS, FileSystem &ExternalFS, std::error_code &EC); 986 987 std::error_code increment() override; 988 }; 989 990 llvm::ErrorOr<std::string> 991 RedirectingFileSystem::getCurrentWorkingDirectory() const { 992 return ExternalFS->getCurrentWorkingDirectory(); 993 } 994 995 std::error_code 996 RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) { 997 return ExternalFS->setCurrentWorkingDirectory(Path); 998 } 999 1000 std::error_code RedirectingFileSystem::isLocal(const Twine &Path, 1001 bool &Result) { 1002 return ExternalFS->isLocal(Path, Result); 1003 } 1004 1005 directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir, 1006 std::error_code &EC) { 1007 ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Dir); 1008 if (!E) { 1009 EC = E.getError(); 1010 if (IsFallthrough && EC == errc::no_such_file_or_directory) 1011 return ExternalFS->dir_begin(Dir, EC); 1012 return {}; 1013 } 1014 ErrorOr<Status> S = status(Dir, *E); 1015 if (!S) { 1016 EC = S.getError(); 1017 return {}; 1018 } 1019 if (!S->isDirectory()) { 1020 EC = std::error_code(static_cast<int>(errc::not_a_directory), 1021 std::system_category()); 1022 return {}; 1023 } 1024 1025 auto *D = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(*E); 1026 return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>( 1027 Dir, D->contents_begin(), D->contents_end(), 1028 /*IterateExternalFS=*/IsFallthrough, *ExternalFS, EC)); 1029 } 1030 1031 void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) { 1032 ExternalContentsPrefixDir = PrefixDir.str(); 1033 } 1034 1035 StringRef RedirectingFileSystem::getExternalContentsPrefixDir() const { 1036 return ExternalContentsPrefixDir; 1037 } 1038 1039 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 1040 LLVM_DUMP_METHOD void RedirectingFileSystem::dump() const { 1041 for (const auto &Root : Roots) 1042 dumpEntry(Root.get()); 1043 } 1044 1045 LLVM_DUMP_METHOD void 1046 RedirectingFileSystem::dumpEntry(RedirectingFileSystem::Entry *E, 1047 int NumSpaces) const { 1048 StringRef Name = E->getName(); 1049 for (int i = 0, e = NumSpaces; i < e; ++i) 1050 dbgs() << " "; 1051 dbgs() << "'" << Name.str().c_str() << "'" 1052 << "\n"; 1053 1054 if (E->getKind() == RedirectingFileSystem::EK_Directory) { 1055 auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(E); 1056 assert(DE && "Should be a directory"); 1057 1058 for (std::unique_ptr<Entry> &SubEntry : 1059 llvm::make_range(DE->contents_begin(), DE->contents_end())) 1060 dumpEntry(SubEntry.get(), NumSpaces + 2); 1061 } 1062 } 1063 #endif 1064 1065 /// A helper class to hold the common YAML parsing state. 1066 class llvm::vfs::RedirectingFileSystemParser { 1067 yaml::Stream &Stream; 1068 1069 void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); } 1070 1071 // false on error 1072 bool parseScalarString(yaml::Node *N, StringRef &Result, 1073 SmallVectorImpl<char> &Storage) { 1074 const auto *S = dyn_cast<yaml::ScalarNode>(N); 1075 1076 if (!S) { 1077 error(N, "expected string"); 1078 return false; 1079 } 1080 Result = S->getValue(Storage); 1081 return true; 1082 } 1083 1084 // false on error 1085 bool parseScalarBool(yaml::Node *N, bool &Result) { 1086 SmallString<5> Storage; 1087 StringRef Value; 1088 if (!parseScalarString(N, Value, Storage)) 1089 return false; 1090 1091 if (Value.equals_lower("true") || Value.equals_lower("on") || 1092 Value.equals_lower("yes") || Value == "1") { 1093 Result = true; 1094 return true; 1095 } else if (Value.equals_lower("false") || Value.equals_lower("off") || 1096 Value.equals_lower("no") || Value == "0") { 1097 Result = false; 1098 return true; 1099 } 1100 1101 error(N, "expected boolean value"); 1102 return false; 1103 } 1104 1105 struct KeyStatus { 1106 bool Required; 1107 bool Seen = false; 1108 1109 KeyStatus(bool Required = false) : Required(Required) {} 1110 }; 1111 1112 using KeyStatusPair = std::pair<StringRef, KeyStatus>; 1113 1114 // false on error 1115 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key, 1116 DenseMap<StringRef, KeyStatus> &Keys) { 1117 if (!Keys.count(Key)) { 1118 error(KeyNode, "unknown key"); 1119 return false; 1120 } 1121 KeyStatus &S = Keys[Key]; 1122 if (S.Seen) { 1123 error(KeyNode, Twine("duplicate key '") + Key + "'"); 1124 return false; 1125 } 1126 S.Seen = true; 1127 return true; 1128 } 1129 1130 // false on error 1131 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) { 1132 for (const auto &I : Keys) { 1133 if (I.second.Required && !I.second.Seen) { 1134 error(Obj, Twine("missing key '") + I.first + "'"); 1135 return false; 1136 } 1137 } 1138 return true; 1139 } 1140 1141 RedirectingFileSystem::Entry * 1142 lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name, 1143 RedirectingFileSystem::Entry *ParentEntry = nullptr) { 1144 if (!ParentEntry) { // Look for a existent root 1145 for (const auto &Root : FS->Roots) { 1146 if (Name.equals(Root->getName())) { 1147 ParentEntry = Root.get(); 1148 return ParentEntry; 1149 } 1150 } 1151 } else { // Advance to the next component 1152 auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>( 1153 ParentEntry); 1154 for (std::unique_ptr<RedirectingFileSystem::Entry> &Content : 1155 llvm::make_range(DE->contents_begin(), DE->contents_end())) { 1156 auto *DirContent = 1157 dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>( 1158 Content.get()); 1159 if (DirContent && Name.equals(Content->getName())) 1160 return DirContent; 1161 } 1162 } 1163 1164 // ... or create a new one 1165 std::unique_ptr<RedirectingFileSystem::Entry> E = 1166 llvm::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>( 1167 Name, Status("", getNextVirtualUniqueID(), 1168 std::chrono::system_clock::now(), 0, 0, 0, 1169 file_type::directory_file, sys::fs::all_all)); 1170 1171 if (!ParentEntry) { // Add a new root to the overlay 1172 FS->Roots.push_back(std::move(E)); 1173 ParentEntry = FS->Roots.back().get(); 1174 return ParentEntry; 1175 } 1176 1177 auto *DE = 1178 dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(ParentEntry); 1179 DE->addContent(std::move(E)); 1180 return DE->getLastContent(); 1181 } 1182 1183 void uniqueOverlayTree(RedirectingFileSystem *FS, 1184 RedirectingFileSystem::Entry *SrcE, 1185 RedirectingFileSystem::Entry *NewParentE = nullptr) { 1186 StringRef Name = SrcE->getName(); 1187 switch (SrcE->getKind()) { 1188 case RedirectingFileSystem::EK_Directory: { 1189 auto *DE = 1190 dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(SrcE); 1191 assert(DE && "Must be a directory"); 1192 // Empty directories could be present in the YAML as a way to 1193 // describe a file for a current directory after some of its subdir 1194 // is parsed. This only leads to redundant walks, ignore it. 1195 if (!Name.empty()) 1196 NewParentE = lookupOrCreateEntry(FS, Name, NewParentE); 1197 for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry : 1198 llvm::make_range(DE->contents_begin(), DE->contents_end())) 1199 uniqueOverlayTree(FS, SubEntry.get(), NewParentE); 1200 break; 1201 } 1202 case RedirectingFileSystem::EK_File: { 1203 auto *FE = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(SrcE); 1204 assert(FE && "Must be a file"); 1205 assert(NewParentE && "Parent entry must exist"); 1206 auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>( 1207 NewParentE); 1208 DE->addContent( 1209 llvm::make_unique<RedirectingFileSystem::RedirectingFileEntry>( 1210 Name, FE->getExternalContentsPath(), FE->getUseName())); 1211 break; 1212 } 1213 } 1214 } 1215 1216 std::unique_ptr<RedirectingFileSystem::Entry> 1217 parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) { 1218 auto *M = dyn_cast<yaml::MappingNode>(N); 1219 if (!M) { 1220 error(N, "expected mapping node for file or directory entry"); 1221 return nullptr; 1222 } 1223 1224 KeyStatusPair Fields[] = { 1225 KeyStatusPair("name", true), 1226 KeyStatusPair("type", true), 1227 KeyStatusPair("contents", false), 1228 KeyStatusPair("external-contents", false), 1229 KeyStatusPair("use-external-name", false), 1230 }; 1231 1232 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields)); 1233 1234 bool HasContents = false; // external or otherwise 1235 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> 1236 EntryArrayContents; 1237 std::string ExternalContentsPath; 1238 std::string Name; 1239 yaml::Node *NameValueNode; 1240 auto UseExternalName = 1241 RedirectingFileSystem::RedirectingFileEntry::NK_NotSet; 1242 RedirectingFileSystem::EntryKind Kind; 1243 1244 for (auto &I : *M) { 1245 StringRef Key; 1246 // Reuse the buffer for key and value, since we don't look at key after 1247 // parsing value. 1248 SmallString<256> Buffer; 1249 if (!parseScalarString(I.getKey(), Key, Buffer)) 1250 return nullptr; 1251 1252 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) 1253 return nullptr; 1254 1255 StringRef Value; 1256 if (Key == "name") { 1257 if (!parseScalarString(I.getValue(), Value, Buffer)) 1258 return nullptr; 1259 1260 NameValueNode = I.getValue(); 1261 if (FS->UseCanonicalizedPaths) { 1262 SmallString<256> Path(Value); 1263 // Guarantee that old YAML files containing paths with ".." and "." 1264 // are properly canonicalized before read into the VFS. 1265 Path = sys::path::remove_leading_dotslash(Path); 1266 sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 1267 Name = Path.str(); 1268 } else { 1269 Name = Value; 1270 } 1271 } else if (Key == "type") { 1272 if (!parseScalarString(I.getValue(), Value, Buffer)) 1273 return nullptr; 1274 if (Value == "file") 1275 Kind = RedirectingFileSystem::EK_File; 1276 else if (Value == "directory") 1277 Kind = RedirectingFileSystem::EK_Directory; 1278 else { 1279 error(I.getValue(), "unknown value for 'type'"); 1280 return nullptr; 1281 } 1282 } else if (Key == "contents") { 1283 if (HasContents) { 1284 error(I.getKey(), 1285 "entry already has 'contents' or 'external-contents'"); 1286 return nullptr; 1287 } 1288 HasContents = true; 1289 auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue()); 1290 if (!Contents) { 1291 // FIXME: this is only for directories, what about files? 1292 error(I.getValue(), "expected array"); 1293 return nullptr; 1294 } 1295 1296 for (auto &I : *Contents) { 1297 if (std::unique_ptr<RedirectingFileSystem::Entry> E = 1298 parseEntry(&I, FS, /*IsRootEntry*/ false)) 1299 EntryArrayContents.push_back(std::move(E)); 1300 else 1301 return nullptr; 1302 } 1303 } else if (Key == "external-contents") { 1304 if (HasContents) { 1305 error(I.getKey(), 1306 "entry already has 'contents' or 'external-contents'"); 1307 return nullptr; 1308 } 1309 HasContents = true; 1310 if (!parseScalarString(I.getValue(), Value, Buffer)) 1311 return nullptr; 1312 1313 SmallString<256> FullPath; 1314 if (FS->IsRelativeOverlay) { 1315 FullPath = FS->getExternalContentsPrefixDir(); 1316 assert(!FullPath.empty() && 1317 "External contents prefix directory must exist"); 1318 llvm::sys::path::append(FullPath, Value); 1319 } else { 1320 FullPath = Value; 1321 } 1322 1323 if (FS->UseCanonicalizedPaths) { 1324 // Guarantee that old YAML files containing paths with ".." and "." 1325 // are properly canonicalized before read into the VFS. 1326 FullPath = sys::path::remove_leading_dotslash(FullPath); 1327 sys::path::remove_dots(FullPath, /*remove_dot_dot=*/true); 1328 } 1329 ExternalContentsPath = FullPath.str(); 1330 } else if (Key == "use-external-name") { 1331 bool Val; 1332 if (!parseScalarBool(I.getValue(), Val)) 1333 return nullptr; 1334 UseExternalName = 1335 Val ? RedirectingFileSystem::RedirectingFileEntry::NK_External 1336 : RedirectingFileSystem::RedirectingFileEntry::NK_Virtual; 1337 } else { 1338 llvm_unreachable("key missing from Keys"); 1339 } 1340 } 1341 1342 if (Stream.failed()) 1343 return nullptr; 1344 1345 // check for missing keys 1346 if (!HasContents) { 1347 error(N, "missing key 'contents' or 'external-contents'"); 1348 return nullptr; 1349 } 1350 if (!checkMissingKeys(N, Keys)) 1351 return nullptr; 1352 1353 // check invalid configuration 1354 if (Kind == RedirectingFileSystem::EK_Directory && 1355 UseExternalName != 1356 RedirectingFileSystem::RedirectingFileEntry::NK_NotSet) { 1357 error(N, "'use-external-name' is not supported for directories"); 1358 return nullptr; 1359 } 1360 1361 if (IsRootEntry && !sys::path::is_absolute(Name)) { 1362 assert(NameValueNode && "Name presence should be checked earlier"); 1363 error(NameValueNode, 1364 "entry with relative path at the root level is not discoverable"); 1365 return nullptr; 1366 } 1367 1368 // Remove trailing slash(es), being careful not to remove the root path 1369 StringRef Trimmed(Name); 1370 size_t RootPathLen = sys::path::root_path(Trimmed).size(); 1371 while (Trimmed.size() > RootPathLen && 1372 sys::path::is_separator(Trimmed.back())) 1373 Trimmed = Trimmed.slice(0, Trimmed.size() - 1); 1374 // Get the last component 1375 StringRef LastComponent = sys::path::filename(Trimmed); 1376 1377 std::unique_ptr<RedirectingFileSystem::Entry> Result; 1378 switch (Kind) { 1379 case RedirectingFileSystem::EK_File: 1380 Result = llvm::make_unique<RedirectingFileSystem::RedirectingFileEntry>( 1381 LastComponent, std::move(ExternalContentsPath), UseExternalName); 1382 break; 1383 case RedirectingFileSystem::EK_Directory: 1384 Result = 1385 llvm::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>( 1386 LastComponent, std::move(EntryArrayContents), 1387 Status("", getNextVirtualUniqueID(), 1388 std::chrono::system_clock::now(), 0, 0, 0, 1389 file_type::directory_file, sys::fs::all_all)); 1390 break; 1391 } 1392 1393 StringRef Parent = sys::path::parent_path(Trimmed); 1394 if (Parent.empty()) 1395 return Result; 1396 1397 // if 'name' contains multiple components, create implicit directory entries 1398 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent), 1399 E = sys::path::rend(Parent); 1400 I != E; ++I) { 1401 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries; 1402 Entries.push_back(std::move(Result)); 1403 Result = 1404 llvm::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>( 1405 *I, std::move(Entries), 1406 Status("", getNextVirtualUniqueID(), 1407 std::chrono::system_clock::now(), 0, 0, 0, 1408 file_type::directory_file, sys::fs::all_all)); 1409 } 1410 return Result; 1411 } 1412 1413 public: 1414 RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {} 1415 1416 // false on error 1417 bool parse(yaml::Node *Root, RedirectingFileSystem *FS) { 1418 auto *Top = dyn_cast<yaml::MappingNode>(Root); 1419 if (!Top) { 1420 error(Root, "expected mapping node"); 1421 return false; 1422 } 1423 1424 KeyStatusPair Fields[] = { 1425 KeyStatusPair("version", true), 1426 KeyStatusPair("case-sensitive", false), 1427 KeyStatusPair("use-external-names", false), 1428 KeyStatusPair("overlay-relative", false), 1429 KeyStatusPair("fallthrough", false), 1430 KeyStatusPair("roots", true), 1431 }; 1432 1433 DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields)); 1434 std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries; 1435 1436 // Parse configuration and 'roots' 1437 for (auto &I : *Top) { 1438 SmallString<10> KeyBuffer; 1439 StringRef Key; 1440 if (!parseScalarString(I.getKey(), Key, KeyBuffer)) 1441 return false; 1442 1443 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) 1444 return false; 1445 1446 if (Key == "roots") { 1447 auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue()); 1448 if (!Roots) { 1449 error(I.getValue(), "expected array"); 1450 return false; 1451 } 1452 1453 for (auto &I : *Roots) { 1454 if (std::unique_ptr<RedirectingFileSystem::Entry> E = 1455 parseEntry(&I, FS, /*IsRootEntry*/ true)) 1456 RootEntries.push_back(std::move(E)); 1457 else 1458 return false; 1459 } 1460 } else if (Key == "version") { 1461 StringRef VersionString; 1462 SmallString<4> Storage; 1463 if (!parseScalarString(I.getValue(), VersionString, Storage)) 1464 return false; 1465 int Version; 1466 if (VersionString.getAsInteger<int>(10, Version)) { 1467 error(I.getValue(), "expected integer"); 1468 return false; 1469 } 1470 if (Version < 0) { 1471 error(I.getValue(), "invalid version number"); 1472 return false; 1473 } 1474 if (Version != 0) { 1475 error(I.getValue(), "version mismatch, expected 0"); 1476 return false; 1477 } 1478 } else if (Key == "case-sensitive") { 1479 if (!parseScalarBool(I.getValue(), FS->CaseSensitive)) 1480 return false; 1481 } else if (Key == "overlay-relative") { 1482 if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay)) 1483 return false; 1484 } else if (Key == "use-external-names") { 1485 if (!parseScalarBool(I.getValue(), FS->UseExternalNames)) 1486 return false; 1487 } else if (Key == "fallthrough") { 1488 if (!parseScalarBool(I.getValue(), FS->IsFallthrough)) 1489 return false; 1490 } else { 1491 llvm_unreachable("key missing from Keys"); 1492 } 1493 } 1494 1495 if (Stream.failed()) 1496 return false; 1497 1498 if (!checkMissingKeys(Top, Keys)) 1499 return false; 1500 1501 // Now that we sucessefully parsed the YAML file, canonicalize the internal 1502 // representation to a proper directory tree so that we can search faster 1503 // inside the VFS. 1504 for (auto &E : RootEntries) 1505 uniqueOverlayTree(FS, E.get()); 1506 1507 return true; 1508 } 1509 }; 1510 1511 RedirectingFileSystem * 1512 RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer, 1513 SourceMgr::DiagHandlerTy DiagHandler, 1514 StringRef YAMLFilePath, void *DiagContext, 1515 IntrusiveRefCntPtr<FileSystem> ExternalFS) { 1516 SourceMgr SM; 1517 yaml::Stream Stream(Buffer->getMemBufferRef(), SM); 1518 1519 SM.setDiagHandler(DiagHandler, DiagContext); 1520 yaml::document_iterator DI = Stream.begin(); 1521 yaml::Node *Root = DI->getRoot(); 1522 if (DI == Stream.end() || !Root) { 1523 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node"); 1524 return nullptr; 1525 } 1526 1527 RedirectingFileSystemParser P(Stream); 1528 1529 std::unique_ptr<RedirectingFileSystem> FS( 1530 new RedirectingFileSystem(std::move(ExternalFS))); 1531 1532 if (!YAMLFilePath.empty()) { 1533 // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed 1534 // to each 'external-contents' path. 1535 // 1536 // Example: 1537 // -ivfsoverlay dummy.cache/vfs/vfs.yaml 1538 // yields: 1539 // FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs 1540 // 1541 SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath); 1542 std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir); 1543 assert(!EC && "Overlay dir final path must be absolute"); 1544 (void)EC; 1545 FS->setExternalContentsPrefixDir(OverlayAbsDir); 1546 } 1547 1548 if (!P.parse(Root, FS.get())) 1549 return nullptr; 1550 1551 return FS.release(); 1552 } 1553 1554 ErrorOr<RedirectingFileSystem::Entry *> 1555 RedirectingFileSystem::lookupPath(const Twine &Path_) const { 1556 SmallString<256> Path; 1557 Path_.toVector(Path); 1558 1559 // Handle relative paths 1560 if (std::error_code EC = makeAbsolute(Path)) 1561 return EC; 1562 1563 // Canonicalize path by removing ".", "..", "./", etc components. This is 1564 // a VFS request, do bot bother about symlinks in the path components 1565 // but canonicalize in order to perform the correct entry search. 1566 if (UseCanonicalizedPaths) { 1567 Path = sys::path::remove_leading_dotslash(Path); 1568 sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 1569 } 1570 1571 if (Path.empty()) 1572 return make_error_code(llvm::errc::invalid_argument); 1573 1574 sys::path::const_iterator Start = sys::path::begin(Path); 1575 sys::path::const_iterator End = sys::path::end(Path); 1576 for (const auto &Root : Roots) { 1577 ErrorOr<RedirectingFileSystem::Entry *> Result = 1578 lookupPath(Start, End, Root.get()); 1579 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) 1580 return Result; 1581 } 1582 return make_error_code(llvm::errc::no_such_file_or_directory); 1583 } 1584 1585 ErrorOr<RedirectingFileSystem::Entry *> 1586 RedirectingFileSystem::lookupPath(sys::path::const_iterator Start, 1587 sys::path::const_iterator End, 1588 RedirectingFileSystem::Entry *From) const { 1589 #ifndef _WIN32 1590 assert(!isTraversalComponent(*Start) && 1591 !isTraversalComponent(From->getName()) && 1592 "Paths should not contain traversal components"); 1593 #else 1594 // FIXME: this is here to support windows, remove it once canonicalized 1595 // paths become globally default. 1596 if (Start->equals(".")) 1597 ++Start; 1598 #endif 1599 1600 StringRef FromName = From->getName(); 1601 1602 // Forward the search to the next component in case this is an empty one. 1603 if (!FromName.empty()) { 1604 if (CaseSensitive ? !Start->equals(FromName) 1605 : !Start->equals_lower(FromName)) 1606 // failure to match 1607 return make_error_code(llvm::errc::no_such_file_or_directory); 1608 1609 ++Start; 1610 1611 if (Start == End) { 1612 // Match! 1613 return From; 1614 } 1615 } 1616 1617 auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(From); 1618 if (!DE) 1619 return make_error_code(llvm::errc::not_a_directory); 1620 1621 for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry : 1622 llvm::make_range(DE->contents_begin(), DE->contents_end())) { 1623 ErrorOr<RedirectingFileSystem::Entry *> Result = 1624 lookupPath(Start, End, DirEntry.get()); 1625 if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) 1626 return Result; 1627 } 1628 return make_error_code(llvm::errc::no_such_file_or_directory); 1629 } 1630 1631 static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames, 1632 Status ExternalStatus) { 1633 Status S = ExternalStatus; 1634 if (!UseExternalNames) 1635 S = Status::copyWithNewName(S, Path.str()); 1636 S.IsVFSMapped = true; 1637 return S; 1638 } 1639 1640 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, 1641 RedirectingFileSystem::Entry *E) { 1642 assert(E != nullptr); 1643 if (auto *F = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(E)) { 1644 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath()); 1645 assert(!S || S->getName() == F->getExternalContentsPath()); 1646 if (S) 1647 return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), 1648 *S); 1649 return S; 1650 } else { // directory 1651 auto *DE = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(E); 1652 return Status::copyWithNewName(DE->getStatus(), Path.str()); 1653 } 1654 } 1655 1656 ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) { 1657 ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path); 1658 if (!Result) { 1659 if (IsFallthrough && 1660 Result.getError() == llvm::errc::no_such_file_or_directory) { 1661 return ExternalFS->status(Path); 1662 } 1663 return Result.getError(); 1664 } 1665 return status(Path, *Result); 1666 } 1667 1668 namespace { 1669 1670 /// Provide a file wrapper with an overriden status. 1671 class FileWithFixedStatus : public File { 1672 std::unique_ptr<File> InnerFile; 1673 Status S; 1674 1675 public: 1676 FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S) 1677 : InnerFile(std::move(InnerFile)), S(std::move(S)) {} 1678 1679 ErrorOr<Status> status() override { return S; } 1680 ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 1681 1682 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 1683 bool IsVolatile) override { 1684 return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator, 1685 IsVolatile); 1686 } 1687 1688 std::error_code close() override { return InnerFile->close(); } 1689 }; 1690 1691 } // namespace 1692 1693 ErrorOr<std::unique_ptr<File>> 1694 RedirectingFileSystem::openFileForRead(const Twine &Path) { 1695 ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path); 1696 if (!E) { 1697 if (IsFallthrough && 1698 E.getError() == llvm::errc::no_such_file_or_directory) { 1699 return ExternalFS->openFileForRead(Path); 1700 } 1701 return E.getError(); 1702 } 1703 1704 auto *F = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(*E); 1705 if (!F) // FIXME: errc::not_a_file? 1706 return make_error_code(llvm::errc::invalid_argument); 1707 1708 auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath()); 1709 if (!Result) 1710 return Result; 1711 1712 auto ExternalStatus = (*Result)->status(); 1713 if (!ExternalStatus) 1714 return ExternalStatus.getError(); 1715 1716 // FIXME: Update the status with the name and VFSMapped. 1717 Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), 1718 *ExternalStatus); 1719 return std::unique_ptr<File>( 1720 llvm::make_unique<FileWithFixedStatus>(std::move(*Result), S)); 1721 } 1722 1723 std::error_code 1724 RedirectingFileSystem::getRealPath(const Twine &Path, 1725 SmallVectorImpl<char> &Output) const { 1726 ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path); 1727 if (!Result) { 1728 if (IsFallthrough && 1729 Result.getError() == llvm::errc::no_such_file_or_directory) { 1730 return ExternalFS->getRealPath(Path, Output); 1731 } 1732 return Result.getError(); 1733 } 1734 1735 if (auto *F = 1736 dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(*Result)) { 1737 return ExternalFS->getRealPath(F->getExternalContentsPath(), Output); 1738 } 1739 // Even if there is a directory entry, fall back to ExternalFS if allowed, 1740 // because directories don't have a single external contents path. 1741 return IsFallthrough ? ExternalFS->getRealPath(Path, Output) 1742 : llvm::errc::invalid_argument; 1743 } 1744 1745 IntrusiveRefCntPtr<FileSystem> 1746 vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer, 1747 SourceMgr::DiagHandlerTy DiagHandler, 1748 StringRef YAMLFilePath, void *DiagContext, 1749 IntrusiveRefCntPtr<FileSystem> ExternalFS) { 1750 return RedirectingFileSystem::create(std::move(Buffer), DiagHandler, 1751 YAMLFilePath, DiagContext, 1752 std::move(ExternalFS)); 1753 } 1754 1755 static void getVFSEntries(RedirectingFileSystem::Entry *SrcE, 1756 SmallVectorImpl<StringRef> &Path, 1757 SmallVectorImpl<YAMLVFSEntry> &Entries) { 1758 auto Kind = SrcE->getKind(); 1759 if (Kind == RedirectingFileSystem::EK_Directory) { 1760 auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(SrcE); 1761 assert(DE && "Must be a directory"); 1762 for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry : 1763 llvm::make_range(DE->contents_begin(), DE->contents_end())) { 1764 Path.push_back(SubEntry->getName()); 1765 getVFSEntries(SubEntry.get(), Path, Entries); 1766 Path.pop_back(); 1767 } 1768 return; 1769 } 1770 1771 assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File"); 1772 auto *FE = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(SrcE); 1773 assert(FE && "Must be a file"); 1774 SmallString<128> VPath; 1775 for (auto &Comp : Path) 1776 llvm::sys::path::append(VPath, Comp); 1777 Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath())); 1778 } 1779 1780 void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer, 1781 SourceMgr::DiagHandlerTy DiagHandler, 1782 StringRef YAMLFilePath, 1783 SmallVectorImpl<YAMLVFSEntry> &CollectedEntries, 1784 void *DiagContext, 1785 IntrusiveRefCntPtr<FileSystem> ExternalFS) { 1786 RedirectingFileSystem *VFS = RedirectingFileSystem::create( 1787 std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext, 1788 std::move(ExternalFS)); 1789 ErrorOr<RedirectingFileSystem::Entry *> RootE = VFS->lookupPath("/"); 1790 if (!RootE) 1791 return; 1792 SmallVector<StringRef, 8> Components; 1793 Components.push_back("/"); 1794 getVFSEntries(*RootE, Components, CollectedEntries); 1795 } 1796 1797 UniqueID vfs::getNextVirtualUniqueID() { 1798 static std::atomic<unsigned> UID; 1799 unsigned ID = ++UID; 1800 // The following assumes that uint64_t max will never collide with a real 1801 // dev_t value from the OS. 1802 return UniqueID(std::numeric_limits<uint64_t>::max(), ID); 1803 } 1804 1805 void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) { 1806 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute"); 1807 assert(sys::path::is_absolute(RealPath) && "real path not absolute"); 1808 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported"); 1809 Mappings.emplace_back(VirtualPath, RealPath); 1810 } 1811 1812 namespace { 1813 1814 class JSONWriter { 1815 llvm::raw_ostream &OS; 1816 SmallVector<StringRef, 16> DirStack; 1817 1818 unsigned getDirIndent() { return 4 * DirStack.size(); } 1819 unsigned getFileIndent() { return 4 * (DirStack.size() + 1); } 1820 bool containedIn(StringRef Parent, StringRef Path); 1821 StringRef containedPart(StringRef Parent, StringRef Path); 1822 void startDirectory(StringRef Path); 1823 void endDirectory(); 1824 void writeEntry(StringRef VPath, StringRef RPath); 1825 1826 public: 1827 JSONWriter(llvm::raw_ostream &OS) : OS(OS) {} 1828 1829 void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> UseExternalNames, 1830 Optional<bool> IsCaseSensitive, Optional<bool> IsOverlayRelative, 1831 StringRef OverlayDir); 1832 }; 1833 1834 } // namespace 1835 1836 bool JSONWriter::containedIn(StringRef Parent, StringRef Path) { 1837 using namespace llvm::sys; 1838 1839 // Compare each path component. 1840 auto IParent = path::begin(Parent), EParent = path::end(Parent); 1841 for (auto IChild = path::begin(Path), EChild = path::end(Path); 1842 IParent != EParent && IChild != EChild; ++IParent, ++IChild) { 1843 if (*IParent != *IChild) 1844 return false; 1845 } 1846 // Have we exhausted the parent path? 1847 return IParent == EParent; 1848 } 1849 1850 StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) { 1851 assert(!Parent.empty()); 1852 assert(containedIn(Parent, Path)); 1853 return Path.slice(Parent.size() + 1, StringRef::npos); 1854 } 1855 1856 void JSONWriter::startDirectory(StringRef Path) { 1857 StringRef Name = 1858 DirStack.empty() ? Path : containedPart(DirStack.back(), Path); 1859 DirStack.push_back(Path); 1860 unsigned Indent = getDirIndent(); 1861 OS.indent(Indent) << "{\n"; 1862 OS.indent(Indent + 2) << "'type': 'directory',\n"; 1863 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n"; 1864 OS.indent(Indent + 2) << "'contents': [\n"; 1865 } 1866 1867 void JSONWriter::endDirectory() { 1868 unsigned Indent = getDirIndent(); 1869 OS.indent(Indent + 2) << "]\n"; 1870 OS.indent(Indent) << "}"; 1871 1872 DirStack.pop_back(); 1873 } 1874 1875 void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) { 1876 unsigned Indent = getFileIndent(); 1877 OS.indent(Indent) << "{\n"; 1878 OS.indent(Indent + 2) << "'type': 'file',\n"; 1879 OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n"; 1880 OS.indent(Indent + 2) << "'external-contents': \"" 1881 << llvm::yaml::escape(RPath) << "\"\n"; 1882 OS.indent(Indent) << "}"; 1883 } 1884 1885 void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries, 1886 Optional<bool> UseExternalNames, 1887 Optional<bool> IsCaseSensitive, 1888 Optional<bool> IsOverlayRelative, 1889 StringRef OverlayDir) { 1890 using namespace llvm::sys; 1891 1892 OS << "{\n" 1893 " 'version': 0,\n"; 1894 if (IsCaseSensitive.hasValue()) 1895 OS << " 'case-sensitive': '" 1896 << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n"; 1897 if (UseExternalNames.hasValue()) 1898 OS << " 'use-external-names': '" 1899 << (UseExternalNames.getValue() ? "true" : "false") << "',\n"; 1900 bool UseOverlayRelative = false; 1901 if (IsOverlayRelative.hasValue()) { 1902 UseOverlayRelative = IsOverlayRelative.getValue(); 1903 OS << " 'overlay-relative': '" << (UseOverlayRelative ? "true" : "false") 1904 << "',\n"; 1905 } 1906 OS << " 'roots': [\n"; 1907 1908 if (!Entries.empty()) { 1909 const YAMLVFSEntry &Entry = Entries.front(); 1910 startDirectory(path::parent_path(Entry.VPath)); 1911 1912 StringRef RPath = Entry.RPath; 1913 if (UseOverlayRelative) { 1914 unsigned OverlayDirLen = OverlayDir.size(); 1915 assert(RPath.substr(0, OverlayDirLen) == OverlayDir && 1916 "Overlay dir must be contained in RPath"); 1917 RPath = RPath.slice(OverlayDirLen, RPath.size()); 1918 } 1919 1920 writeEntry(path::filename(Entry.VPath), RPath); 1921 1922 for (const auto &Entry : Entries.slice(1)) { 1923 StringRef Dir = path::parent_path(Entry.VPath); 1924 if (Dir == DirStack.back()) 1925 OS << ",\n"; 1926 else { 1927 while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) { 1928 OS << "\n"; 1929 endDirectory(); 1930 } 1931 OS << ",\n"; 1932 startDirectory(Dir); 1933 } 1934 StringRef RPath = Entry.RPath; 1935 if (UseOverlayRelative) { 1936 unsigned OverlayDirLen = OverlayDir.size(); 1937 assert(RPath.substr(0, OverlayDirLen) == OverlayDir && 1938 "Overlay dir must be contained in RPath"); 1939 RPath = RPath.slice(OverlayDirLen, RPath.size()); 1940 } 1941 writeEntry(path::filename(Entry.VPath), RPath); 1942 } 1943 1944 while (!DirStack.empty()) { 1945 OS << "\n"; 1946 endDirectory(); 1947 } 1948 OS << "\n"; 1949 } 1950 1951 OS << " ]\n" 1952 << "}\n"; 1953 } 1954 1955 void YAMLVFSWriter::write(llvm::raw_ostream &OS) { 1956 llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) { 1957 return LHS.VPath < RHS.VPath; 1958 }); 1959 1960 JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive, 1961 IsOverlayRelative, OverlayDir); 1962 } 1963 1964 VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl( 1965 const Twine &_Path, 1966 RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin, 1967 RedirectingFileSystem::RedirectingDirectoryEntry::iterator End, 1968 bool IterateExternalFS, FileSystem &ExternalFS, std::error_code &EC) 1969 : Dir(_Path.str()), Current(Begin), End(End), 1970 IterateExternalFS(IterateExternalFS), ExternalFS(ExternalFS) { 1971 EC = incrementImpl(/*IsFirstTime=*/true); 1972 } 1973 1974 std::error_code VFSFromYamlDirIterImpl::increment() { 1975 return incrementImpl(/*IsFirstTime=*/false); 1976 } 1977 1978 std::error_code VFSFromYamlDirIterImpl::incrementExternal() { 1979 assert(!(IsExternalFSCurrent && ExternalDirIter == directory_iterator()) && 1980 "incrementing past end"); 1981 std::error_code EC; 1982 if (IsExternalFSCurrent) { 1983 ExternalDirIter.increment(EC); 1984 } else if (IterateExternalFS) { 1985 ExternalDirIter = ExternalFS.dir_begin(Dir, EC); 1986 IsExternalFSCurrent = true; 1987 if (EC && EC != errc::no_such_file_or_directory) 1988 return EC; 1989 EC = {}; 1990 } 1991 if (EC || ExternalDirIter == directory_iterator()) { 1992 CurrentEntry = directory_entry(); 1993 } else { 1994 CurrentEntry = *ExternalDirIter; 1995 } 1996 return EC; 1997 } 1998 1999 std::error_code VFSFromYamlDirIterImpl::incrementContent(bool IsFirstTime) { 2000 assert((IsFirstTime || Current != End) && "cannot iterate past end"); 2001 if (!IsFirstTime) 2002 ++Current; 2003 while (Current != End) { 2004 SmallString<128> PathStr(Dir); 2005 llvm::sys::path::append(PathStr, (*Current)->getName()); 2006 sys::fs::file_type Type; 2007 switch ((*Current)->getKind()) { 2008 case RedirectingFileSystem::EK_Directory: 2009 Type = sys::fs::file_type::directory_file; 2010 break; 2011 case RedirectingFileSystem::EK_File: 2012 Type = sys::fs::file_type::regular_file; 2013 break; 2014 } 2015 CurrentEntry = directory_entry(PathStr.str(), Type); 2016 return {}; 2017 } 2018 return incrementExternal(); 2019 } 2020 2021 std::error_code VFSFromYamlDirIterImpl::incrementImpl(bool IsFirstTime) { 2022 while (true) { 2023 std::error_code EC = IsExternalFSCurrent ? incrementExternal() 2024 : incrementContent(IsFirstTime); 2025 if (EC || CurrentEntry.path().empty()) 2026 return EC; 2027 StringRef Name = llvm::sys::path::filename(CurrentEntry.path()); 2028 if (SeenNames.insert(Name).second) 2029 return EC; // name not seen before 2030 } 2031 llvm_unreachable("returned above"); 2032 } 2033 2034 vfs::recursive_directory_iterator::recursive_directory_iterator( 2035 FileSystem &FS_, const Twine &Path, std::error_code &EC) 2036 : FS(&FS_) { 2037 directory_iterator I = FS->dir_begin(Path, EC); 2038 if (I != directory_iterator()) { 2039 State = std::make_shared<detail::RecDirIterState>(); 2040 State->Stack.push(I); 2041 } 2042 } 2043 2044 vfs::recursive_directory_iterator & 2045 recursive_directory_iterator::increment(std::error_code &EC) { 2046 assert(FS && State && !State->Stack.empty() && "incrementing past end"); 2047 assert(!State->Stack.top()->path().empty() && "non-canonical end iterator"); 2048 vfs::directory_iterator End; 2049 2050 if (State->HasNoPushRequest) 2051 State->HasNoPushRequest = false; 2052 else { 2053 if (State->Stack.top()->type() == sys::fs::file_type::directory_file) { 2054 vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC); 2055 if (I != End) { 2056 State->Stack.push(I); 2057 return *this; 2058 } 2059 } 2060 } 2061 2062 while (!State->Stack.empty() && State->Stack.top().increment(EC) == End) 2063 State->Stack.pop(); 2064 2065 if (State->Stack.empty()) 2066 State.reset(); // end iterator 2067 2068 return *this; 2069 } 2070