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