1*0b57cec5SDimitry Andric //===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This file implements the VirtualFileSystem interface. 10*0b57cec5SDimitry Andric // 11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 12*0b57cec5SDimitry Andric 13*0b57cec5SDimitry Andric #include "llvm/Support/VirtualFileSystem.h" 14*0b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h" 15*0b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h" 16*0b57cec5SDimitry Andric #include "llvm/ADT/IntrusiveRefCntPtr.h" 17*0b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 18*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 19*0b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 20*0b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 21*0b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h" 22*0b57cec5SDimitry Andric #include "llvm/ADT/Twine.h" 23*0b57cec5SDimitry Andric #include "llvm/ADT/iterator_range.h" 24*0b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h" 25*0b57cec5SDimitry Andric #include "llvm/Support/Casting.h" 26*0b57cec5SDimitry Andric #include "llvm/Support/Chrono.h" 27*0b57cec5SDimitry Andric #include "llvm/Support/Compiler.h" 28*0b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 29*0b57cec5SDimitry Andric #include "llvm/Support/Errc.h" 30*0b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h" 31*0b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h" 32*0b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 33349cc55cSDimitry Andric #include "llvm/Support/FileSystem/UniqueID.h" 34*0b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 35*0b57cec5SDimitry Andric #include "llvm/Support/Path.h" 36*0b57cec5SDimitry Andric #include "llvm/Support/SMLoc.h" 37*0b57cec5SDimitry Andric #include "llvm/Support/SourceMgr.h" 38*0b57cec5SDimitry Andric #include "llvm/Support/YAMLParser.h" 39*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 40*0b57cec5SDimitry Andric #include <algorithm> 41*0b57cec5SDimitry Andric #include <atomic> 42*0b57cec5SDimitry Andric #include <cassert> 43*0b57cec5SDimitry Andric #include <cstdint> 44*0b57cec5SDimitry Andric #include <iterator> 45*0b57cec5SDimitry Andric #include <limits> 46*0b57cec5SDimitry Andric #include <memory> 47bdd1243dSDimitry Andric #include <optional> 48*0b57cec5SDimitry Andric #include <string> 49*0b57cec5SDimitry Andric #include <system_error> 50*0b57cec5SDimitry Andric #include <utility> 51*0b57cec5SDimitry Andric #include <vector> 52*0b57cec5SDimitry Andric 53*0b57cec5SDimitry Andric using namespace llvm; 54*0b57cec5SDimitry Andric using namespace llvm::vfs; 55*0b57cec5SDimitry Andric 56*0b57cec5SDimitry Andric using llvm::sys::fs::file_t; 57*0b57cec5SDimitry Andric using llvm::sys::fs::file_status; 58*0b57cec5SDimitry Andric using llvm::sys::fs::file_type; 59*0b57cec5SDimitry Andric using llvm::sys::fs::kInvalidFile; 60*0b57cec5SDimitry Andric using llvm::sys::fs::perms; 61*0b57cec5SDimitry Andric using llvm::sys::fs::UniqueID; 62*0b57cec5SDimitry Andric 63*0b57cec5SDimitry Andric Status::Status(const file_status &Status) 64*0b57cec5SDimitry Andric : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()), 65*0b57cec5SDimitry Andric User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()), 66*0b57cec5SDimitry Andric Type(Status.type()), Perms(Status.permissions()) {} 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric Status::Status(const Twine &Name, UniqueID UID, sys::TimePoint<> MTime, 69*0b57cec5SDimitry Andric uint32_t User, uint32_t Group, uint64_t Size, file_type Type, 70*0b57cec5SDimitry Andric perms Perms) 71*0b57cec5SDimitry Andric : Name(Name.str()), UID(UID), MTime(MTime), User(User), Group(Group), 72*0b57cec5SDimitry Andric Size(Size), Type(Type), Perms(Perms) {} 73*0b57cec5SDimitry Andric 740eae32dcSDimitry Andric Status Status::copyWithNewSize(const Status &In, uint64_t NewSize) { 750eae32dcSDimitry Andric return Status(In.getName(), In.getUniqueID(), In.getLastModificationTime(), 760eae32dcSDimitry Andric In.getUser(), In.getGroup(), NewSize, In.getType(), 770eae32dcSDimitry Andric In.getPermissions()); 780eae32dcSDimitry Andric } 790eae32dcSDimitry Andric 80*0b57cec5SDimitry Andric Status Status::copyWithNewName(const Status &In, const Twine &NewName) { 81*0b57cec5SDimitry Andric return Status(NewName, In.getUniqueID(), In.getLastModificationTime(), 82*0b57cec5SDimitry Andric In.getUser(), In.getGroup(), In.getSize(), In.getType(), 83*0b57cec5SDimitry Andric In.getPermissions()); 84*0b57cec5SDimitry Andric } 85*0b57cec5SDimitry Andric 86*0b57cec5SDimitry Andric Status Status::copyWithNewName(const file_status &In, const Twine &NewName) { 87*0b57cec5SDimitry Andric return Status(NewName, In.getUniqueID(), In.getLastModificationTime(), 88*0b57cec5SDimitry Andric In.getUser(), In.getGroup(), In.getSize(), In.type(), 89*0b57cec5SDimitry Andric In.permissions()); 90*0b57cec5SDimitry Andric } 91*0b57cec5SDimitry Andric 92*0b57cec5SDimitry Andric bool Status::equivalent(const Status &Other) const { 93*0b57cec5SDimitry Andric assert(isStatusKnown() && Other.isStatusKnown()); 94*0b57cec5SDimitry Andric return getUniqueID() == Other.getUniqueID(); 95*0b57cec5SDimitry Andric } 96*0b57cec5SDimitry Andric 97*0b57cec5SDimitry Andric bool Status::isDirectory() const { return Type == file_type::directory_file; } 98*0b57cec5SDimitry Andric 99*0b57cec5SDimitry Andric bool Status::isRegularFile() const { return Type == file_type::regular_file; } 100*0b57cec5SDimitry Andric 101*0b57cec5SDimitry Andric bool Status::isOther() const { 102*0b57cec5SDimitry Andric return exists() && !isRegularFile() && !isDirectory() && !isSymlink(); 103*0b57cec5SDimitry Andric } 104*0b57cec5SDimitry Andric 105*0b57cec5SDimitry Andric bool Status::isSymlink() const { return Type == file_type::symlink_file; } 106*0b57cec5SDimitry Andric 107*0b57cec5SDimitry Andric bool Status::isStatusKnown() const { return Type != file_type::status_error; } 108*0b57cec5SDimitry Andric 109*0b57cec5SDimitry Andric bool Status::exists() const { 110*0b57cec5SDimitry Andric return isStatusKnown() && Type != file_type::file_not_found; 111*0b57cec5SDimitry Andric } 112*0b57cec5SDimitry Andric 113*0b57cec5SDimitry Andric File::~File() = default; 114*0b57cec5SDimitry Andric 115*0b57cec5SDimitry Andric FileSystem::~FileSystem() = default; 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> 118*0b57cec5SDimitry Andric FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize, 119*0b57cec5SDimitry Andric bool RequiresNullTerminator, bool IsVolatile) { 120*0b57cec5SDimitry Andric auto F = openFileForRead(Name); 121*0b57cec5SDimitry Andric if (!F) 122*0b57cec5SDimitry Andric return F.getError(); 123*0b57cec5SDimitry Andric 124*0b57cec5SDimitry Andric return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile); 125*0b57cec5SDimitry Andric } 126*0b57cec5SDimitry Andric 127*0b57cec5SDimitry Andric std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const { 128*0b57cec5SDimitry Andric if (llvm::sys::path::is_absolute(Path)) 129*0b57cec5SDimitry Andric return {}; 130*0b57cec5SDimitry Andric 131*0b57cec5SDimitry Andric auto WorkingDir = getCurrentWorkingDirectory(); 132*0b57cec5SDimitry Andric if (!WorkingDir) 133*0b57cec5SDimitry Andric return WorkingDir.getError(); 134*0b57cec5SDimitry Andric 135*0b57cec5SDimitry Andric llvm::sys::fs::make_absolute(WorkingDir.get(), Path); 136*0b57cec5SDimitry Andric return {}; 137*0b57cec5SDimitry Andric } 138*0b57cec5SDimitry Andric 139*0b57cec5SDimitry Andric std::error_code FileSystem::getRealPath(const Twine &Path, 140*0b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const { 141*0b57cec5SDimitry Andric return errc::operation_not_permitted; 142*0b57cec5SDimitry Andric } 143*0b57cec5SDimitry Andric 144*0b57cec5SDimitry Andric std::error_code FileSystem::isLocal(const Twine &Path, bool &Result) { 145*0b57cec5SDimitry Andric return errc::operation_not_permitted; 146*0b57cec5SDimitry Andric } 147*0b57cec5SDimitry Andric 148*0b57cec5SDimitry Andric bool FileSystem::exists(const Twine &Path) { 149*0b57cec5SDimitry Andric auto Status = status(Path); 150*0b57cec5SDimitry Andric return Status && Status->exists(); 151*0b57cec5SDimitry Andric } 152*0b57cec5SDimitry Andric 15381ad6265SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 15481ad6265SDimitry Andric void FileSystem::dump() const { print(dbgs(), PrintType::RecursiveContents); } 15581ad6265SDimitry Andric #endif 15681ad6265SDimitry Andric 157*0b57cec5SDimitry Andric #ifndef NDEBUG 158*0b57cec5SDimitry Andric static bool isTraversalComponent(StringRef Component) { 159*0b57cec5SDimitry Andric return Component.equals("..") || Component.equals("."); 160*0b57cec5SDimitry Andric } 161*0b57cec5SDimitry Andric 162*0b57cec5SDimitry Andric static bool pathHasTraversal(StringRef Path) { 163*0b57cec5SDimitry Andric using namespace llvm::sys; 164*0b57cec5SDimitry Andric 165*0b57cec5SDimitry Andric for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path))) 166*0b57cec5SDimitry Andric if (isTraversalComponent(Comp)) 167*0b57cec5SDimitry Andric return true; 168*0b57cec5SDimitry Andric return false; 169*0b57cec5SDimitry Andric } 170*0b57cec5SDimitry Andric #endif 171*0b57cec5SDimitry Andric 172*0b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/ 173*0b57cec5SDimitry Andric // RealFileSystem implementation 174*0b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/ 175*0b57cec5SDimitry Andric 176*0b57cec5SDimitry Andric namespace { 177*0b57cec5SDimitry Andric 178*0b57cec5SDimitry Andric /// Wrapper around a raw file descriptor. 179*0b57cec5SDimitry Andric class RealFile : public File { 180*0b57cec5SDimitry Andric friend class RealFileSystem; 181*0b57cec5SDimitry Andric 182*0b57cec5SDimitry Andric file_t FD; 183*0b57cec5SDimitry Andric Status S; 184*0b57cec5SDimitry Andric std::string RealName; 185*0b57cec5SDimitry Andric 1868bcb0991SDimitry Andric RealFile(file_t RawFD, StringRef NewName, StringRef NewRealPathName) 1878bcb0991SDimitry Andric : FD(RawFD), S(NewName, {}, {}, {}, {}, {}, 188*0b57cec5SDimitry Andric llvm::sys::fs::file_type::status_error, {}), 189*0b57cec5SDimitry Andric RealName(NewRealPathName.str()) { 190*0b57cec5SDimitry Andric assert(FD != kInvalidFile && "Invalid or inactive file descriptor"); 191*0b57cec5SDimitry Andric } 192*0b57cec5SDimitry Andric 193*0b57cec5SDimitry Andric public: 194*0b57cec5SDimitry Andric ~RealFile() override; 195*0b57cec5SDimitry Andric 196*0b57cec5SDimitry Andric ErrorOr<Status> status() override; 197*0b57cec5SDimitry Andric ErrorOr<std::string> getName() override; 198*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name, 199*0b57cec5SDimitry Andric int64_t FileSize, 200*0b57cec5SDimitry Andric bool RequiresNullTerminator, 201*0b57cec5SDimitry Andric bool IsVolatile) override; 202*0b57cec5SDimitry Andric std::error_code close() override; 203349cc55cSDimitry Andric void setPath(const Twine &Path) override; 204*0b57cec5SDimitry Andric }; 205*0b57cec5SDimitry Andric 206*0b57cec5SDimitry Andric } // namespace 207*0b57cec5SDimitry Andric 208*0b57cec5SDimitry Andric RealFile::~RealFile() { close(); } 209*0b57cec5SDimitry Andric 210*0b57cec5SDimitry Andric ErrorOr<Status> RealFile::status() { 211*0b57cec5SDimitry Andric assert(FD != kInvalidFile && "cannot stat closed file"); 212*0b57cec5SDimitry Andric if (!S.isStatusKnown()) { 213*0b57cec5SDimitry Andric file_status RealStatus; 214*0b57cec5SDimitry Andric if (std::error_code EC = sys::fs::status(FD, RealStatus)) 215*0b57cec5SDimitry Andric return EC; 216*0b57cec5SDimitry Andric S = Status::copyWithNewName(RealStatus, S.getName()); 217*0b57cec5SDimitry Andric } 218*0b57cec5SDimitry Andric return S; 219*0b57cec5SDimitry Andric } 220*0b57cec5SDimitry Andric 221*0b57cec5SDimitry Andric ErrorOr<std::string> RealFile::getName() { 222*0b57cec5SDimitry Andric return RealName.empty() ? S.getName().str() : RealName; 223*0b57cec5SDimitry Andric } 224*0b57cec5SDimitry Andric 225*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> 226*0b57cec5SDimitry Andric RealFile::getBuffer(const Twine &Name, int64_t FileSize, 227*0b57cec5SDimitry Andric bool RequiresNullTerminator, bool IsVolatile) { 228*0b57cec5SDimitry Andric assert(FD != kInvalidFile && "cannot get buffer for closed file"); 229*0b57cec5SDimitry Andric return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator, 230*0b57cec5SDimitry Andric IsVolatile); 231*0b57cec5SDimitry Andric } 232*0b57cec5SDimitry Andric 233*0b57cec5SDimitry Andric std::error_code RealFile::close() { 234*0b57cec5SDimitry Andric std::error_code EC = sys::fs::closeFile(FD); 235*0b57cec5SDimitry Andric FD = kInvalidFile; 236*0b57cec5SDimitry Andric return EC; 237*0b57cec5SDimitry Andric } 238*0b57cec5SDimitry Andric 239349cc55cSDimitry Andric void RealFile::setPath(const Twine &Path) { 240349cc55cSDimitry Andric RealName = Path.str(); 241349cc55cSDimitry Andric if (auto Status = status()) 242349cc55cSDimitry Andric S = Status.get().copyWithNewName(Status.get(), Path); 243349cc55cSDimitry Andric } 244349cc55cSDimitry Andric 245*0b57cec5SDimitry Andric namespace { 246*0b57cec5SDimitry Andric 247*0b57cec5SDimitry Andric /// A file system according to your operating system. 248*0b57cec5SDimitry Andric /// This may be linked to the process's working directory, or maintain its own. 249*0b57cec5SDimitry Andric /// 250*0b57cec5SDimitry Andric /// Currently, its own working directory is emulated by storing the path and 251*0b57cec5SDimitry Andric /// sending absolute paths to llvm::sys::fs:: functions. 252*0b57cec5SDimitry Andric /// A more principled approach would be to push this down a level, modelling 253*0b57cec5SDimitry Andric /// the working dir as an llvm::sys::fs::WorkingDir or similar. 254*0b57cec5SDimitry Andric /// This would enable the use of openat()-style functions on some platforms. 255*0b57cec5SDimitry Andric class RealFileSystem : public FileSystem { 256*0b57cec5SDimitry Andric public: 257*0b57cec5SDimitry Andric explicit RealFileSystem(bool LinkCWDToProcess) { 258*0b57cec5SDimitry Andric if (!LinkCWDToProcess) { 259*0b57cec5SDimitry Andric SmallString<128> PWD, RealPWD; 260*0b57cec5SDimitry Andric if (llvm::sys::fs::current_path(PWD)) 261*0b57cec5SDimitry Andric return; // Awful, but nothing to do here. 262*0b57cec5SDimitry Andric if (llvm::sys::fs::real_path(PWD, RealPWD)) 263*0b57cec5SDimitry Andric WD = {PWD, PWD}; 264*0b57cec5SDimitry Andric else 265*0b57cec5SDimitry Andric WD = {PWD, RealPWD}; 266*0b57cec5SDimitry Andric } 267*0b57cec5SDimitry Andric } 268*0b57cec5SDimitry Andric 269*0b57cec5SDimitry Andric ErrorOr<Status> status(const Twine &Path) override; 270*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override; 271*0b57cec5SDimitry Andric directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; 272*0b57cec5SDimitry Andric 273*0b57cec5SDimitry Andric llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; 274*0b57cec5SDimitry Andric std::error_code setCurrentWorkingDirectory(const Twine &Path) override; 275*0b57cec5SDimitry Andric std::error_code isLocal(const Twine &Path, bool &Result) override; 276*0b57cec5SDimitry Andric std::error_code getRealPath(const Twine &Path, 277*0b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const override; 278*0b57cec5SDimitry Andric 27981ad6265SDimitry Andric protected: 28081ad6265SDimitry Andric void printImpl(raw_ostream &OS, PrintType Type, 28181ad6265SDimitry Andric unsigned IndentLevel) const override; 28281ad6265SDimitry Andric 283*0b57cec5SDimitry Andric private: 284*0b57cec5SDimitry Andric // If this FS has its own working dir, use it to make Path absolute. 285*0b57cec5SDimitry Andric // The returned twine is safe to use as long as both Storage and Path live. 286*0b57cec5SDimitry Andric Twine adjustPath(const Twine &Path, SmallVectorImpl<char> &Storage) const { 287*0b57cec5SDimitry Andric if (!WD) 288*0b57cec5SDimitry Andric return Path; 289*0b57cec5SDimitry Andric Path.toVector(Storage); 290*0b57cec5SDimitry Andric sys::fs::make_absolute(WD->Resolved, Storage); 291*0b57cec5SDimitry Andric return Storage; 292*0b57cec5SDimitry Andric } 293*0b57cec5SDimitry Andric 294*0b57cec5SDimitry Andric struct WorkingDirectory { 295*0b57cec5SDimitry Andric // The current working directory, without symlinks resolved. (echo $PWD). 296*0b57cec5SDimitry Andric SmallString<128> Specified; 297*0b57cec5SDimitry Andric // The current working directory, with links resolved. (readlink .). 298*0b57cec5SDimitry Andric SmallString<128> Resolved; 299*0b57cec5SDimitry Andric }; 300bdd1243dSDimitry Andric std::optional<WorkingDirectory> WD; 301*0b57cec5SDimitry Andric }; 302*0b57cec5SDimitry Andric 303*0b57cec5SDimitry Andric } // namespace 304*0b57cec5SDimitry Andric 305*0b57cec5SDimitry Andric ErrorOr<Status> RealFileSystem::status(const Twine &Path) { 306*0b57cec5SDimitry Andric SmallString<256> Storage; 307*0b57cec5SDimitry Andric sys::fs::file_status RealStatus; 308*0b57cec5SDimitry Andric if (std::error_code EC = 309*0b57cec5SDimitry Andric sys::fs::status(adjustPath(Path, Storage), RealStatus)) 310*0b57cec5SDimitry Andric return EC; 311*0b57cec5SDimitry Andric return Status::copyWithNewName(RealStatus, Path); 312*0b57cec5SDimitry Andric } 313*0b57cec5SDimitry Andric 314*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>> 315*0b57cec5SDimitry Andric RealFileSystem::openFileForRead(const Twine &Name) { 316*0b57cec5SDimitry Andric SmallString<256> RealName, Storage; 317*0b57cec5SDimitry Andric Expected<file_t> FDOrErr = sys::fs::openNativeFileForRead( 318*0b57cec5SDimitry Andric adjustPath(Name, Storage), sys::fs::OF_None, &RealName); 319*0b57cec5SDimitry Andric if (!FDOrErr) 320*0b57cec5SDimitry Andric return errorToErrorCode(FDOrErr.takeError()); 321*0b57cec5SDimitry Andric return std::unique_ptr<File>( 322*0b57cec5SDimitry Andric new RealFile(*FDOrErr, Name.str(), RealName.str())); 323*0b57cec5SDimitry Andric } 324*0b57cec5SDimitry Andric 325*0b57cec5SDimitry Andric llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const { 326*0b57cec5SDimitry Andric if (WD) 3275ffd83dbSDimitry Andric return std::string(WD->Specified.str()); 328*0b57cec5SDimitry Andric 329*0b57cec5SDimitry Andric SmallString<128> Dir; 330*0b57cec5SDimitry Andric if (std::error_code EC = llvm::sys::fs::current_path(Dir)) 331*0b57cec5SDimitry Andric return EC; 3325ffd83dbSDimitry Andric return std::string(Dir.str()); 333*0b57cec5SDimitry Andric } 334*0b57cec5SDimitry Andric 335*0b57cec5SDimitry Andric std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) { 336*0b57cec5SDimitry Andric if (!WD) 337*0b57cec5SDimitry Andric return llvm::sys::fs::set_current_path(Path); 338*0b57cec5SDimitry Andric 339*0b57cec5SDimitry Andric SmallString<128> Absolute, Resolved, Storage; 340*0b57cec5SDimitry Andric adjustPath(Path, Storage).toVector(Absolute); 341*0b57cec5SDimitry Andric bool IsDir; 342*0b57cec5SDimitry Andric if (auto Err = llvm::sys::fs::is_directory(Absolute, IsDir)) 343*0b57cec5SDimitry Andric return Err; 344*0b57cec5SDimitry Andric if (!IsDir) 345*0b57cec5SDimitry Andric return std::make_error_code(std::errc::not_a_directory); 346*0b57cec5SDimitry Andric if (auto Err = llvm::sys::fs::real_path(Absolute, Resolved)) 347*0b57cec5SDimitry Andric return Err; 348*0b57cec5SDimitry Andric WD = {Absolute, Resolved}; 349*0b57cec5SDimitry Andric return std::error_code(); 350*0b57cec5SDimitry Andric } 351*0b57cec5SDimitry Andric 352*0b57cec5SDimitry Andric std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) { 353*0b57cec5SDimitry Andric SmallString<256> Storage; 354*0b57cec5SDimitry Andric return llvm::sys::fs::is_local(adjustPath(Path, Storage), Result); 355*0b57cec5SDimitry Andric } 356*0b57cec5SDimitry Andric 357*0b57cec5SDimitry Andric std::error_code 358*0b57cec5SDimitry Andric RealFileSystem::getRealPath(const Twine &Path, 359*0b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const { 360*0b57cec5SDimitry Andric SmallString<256> Storage; 361*0b57cec5SDimitry Andric return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output); 362*0b57cec5SDimitry Andric } 363*0b57cec5SDimitry Andric 36481ad6265SDimitry Andric void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type, 36581ad6265SDimitry Andric unsigned IndentLevel) const { 36681ad6265SDimitry Andric printIndent(OS, IndentLevel); 36781ad6265SDimitry Andric OS << "RealFileSystem using "; 36881ad6265SDimitry Andric if (WD) 36981ad6265SDimitry Andric OS << "own"; 37081ad6265SDimitry Andric else 37181ad6265SDimitry Andric OS << "process"; 37281ad6265SDimitry Andric OS << " CWD\n"; 37381ad6265SDimitry Andric } 37481ad6265SDimitry Andric 375*0b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() { 376*0b57cec5SDimitry Andric static IntrusiveRefCntPtr<FileSystem> FS(new RealFileSystem(true)); 377*0b57cec5SDimitry Andric return FS; 378*0b57cec5SDimitry Andric } 379*0b57cec5SDimitry Andric 380*0b57cec5SDimitry Andric std::unique_ptr<FileSystem> vfs::createPhysicalFileSystem() { 3818bcb0991SDimitry Andric return std::make_unique<RealFileSystem>(false); 382*0b57cec5SDimitry Andric } 383*0b57cec5SDimitry Andric 384*0b57cec5SDimitry Andric namespace { 385*0b57cec5SDimitry Andric 386*0b57cec5SDimitry Andric class RealFSDirIter : public llvm::vfs::detail::DirIterImpl { 387*0b57cec5SDimitry Andric llvm::sys::fs::directory_iterator Iter; 388*0b57cec5SDimitry Andric 389*0b57cec5SDimitry Andric public: 390*0b57cec5SDimitry Andric RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) { 391*0b57cec5SDimitry Andric if (Iter != llvm::sys::fs::directory_iterator()) 392*0b57cec5SDimitry Andric CurrentEntry = directory_entry(Iter->path(), Iter->type()); 393*0b57cec5SDimitry Andric } 394*0b57cec5SDimitry Andric 395*0b57cec5SDimitry Andric std::error_code increment() override { 396*0b57cec5SDimitry Andric std::error_code EC; 397*0b57cec5SDimitry Andric Iter.increment(EC); 398*0b57cec5SDimitry Andric CurrentEntry = (Iter == llvm::sys::fs::directory_iterator()) 399*0b57cec5SDimitry Andric ? directory_entry() 400*0b57cec5SDimitry Andric : directory_entry(Iter->path(), Iter->type()); 401*0b57cec5SDimitry Andric return EC; 402*0b57cec5SDimitry Andric } 403*0b57cec5SDimitry Andric }; 404*0b57cec5SDimitry Andric 405*0b57cec5SDimitry Andric } // namespace 406*0b57cec5SDimitry Andric 407*0b57cec5SDimitry Andric directory_iterator RealFileSystem::dir_begin(const Twine &Dir, 408*0b57cec5SDimitry Andric std::error_code &EC) { 409*0b57cec5SDimitry Andric SmallString<128> Storage; 410*0b57cec5SDimitry Andric return directory_iterator( 411*0b57cec5SDimitry Andric std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC)); 412*0b57cec5SDimitry Andric } 413*0b57cec5SDimitry Andric 414*0b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/ 415*0b57cec5SDimitry Andric // OverlayFileSystem implementation 416*0b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/ 417*0b57cec5SDimitry Andric 418*0b57cec5SDimitry Andric OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) { 419*0b57cec5SDimitry Andric FSList.push_back(std::move(BaseFS)); 420*0b57cec5SDimitry Andric } 421*0b57cec5SDimitry Andric 422*0b57cec5SDimitry Andric void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) { 423*0b57cec5SDimitry Andric FSList.push_back(FS); 424*0b57cec5SDimitry Andric // Synchronize added file systems by duplicating the working directory from 425*0b57cec5SDimitry Andric // the first one in the list. 426*0b57cec5SDimitry Andric FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get()); 427*0b57cec5SDimitry Andric } 428*0b57cec5SDimitry Andric 429*0b57cec5SDimitry Andric ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) { 430*0b57cec5SDimitry Andric // FIXME: handle symlinks that cross file systems 431*0b57cec5SDimitry Andric for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) { 432*0b57cec5SDimitry Andric ErrorOr<Status> Status = (*I)->status(Path); 433*0b57cec5SDimitry Andric if (Status || Status.getError() != llvm::errc::no_such_file_or_directory) 434*0b57cec5SDimitry Andric return Status; 435*0b57cec5SDimitry Andric } 436*0b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory); 437*0b57cec5SDimitry Andric } 438*0b57cec5SDimitry Andric 439*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>> 440*0b57cec5SDimitry Andric OverlayFileSystem::openFileForRead(const llvm::Twine &Path) { 441*0b57cec5SDimitry Andric // FIXME: handle symlinks that cross file systems 442*0b57cec5SDimitry Andric for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) { 443*0b57cec5SDimitry Andric auto Result = (*I)->openFileForRead(Path); 444*0b57cec5SDimitry Andric if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) 445*0b57cec5SDimitry Andric return Result; 446*0b57cec5SDimitry Andric } 447*0b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory); 448*0b57cec5SDimitry Andric } 449*0b57cec5SDimitry Andric 450*0b57cec5SDimitry Andric llvm::ErrorOr<std::string> 451*0b57cec5SDimitry Andric OverlayFileSystem::getCurrentWorkingDirectory() const { 452*0b57cec5SDimitry Andric // All file systems are synchronized, just take the first working directory. 453*0b57cec5SDimitry Andric return FSList.front()->getCurrentWorkingDirectory(); 454*0b57cec5SDimitry Andric } 455*0b57cec5SDimitry Andric 456*0b57cec5SDimitry Andric std::error_code 457*0b57cec5SDimitry Andric OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) { 458*0b57cec5SDimitry Andric for (auto &FS : FSList) 459*0b57cec5SDimitry Andric if (std::error_code EC = FS->setCurrentWorkingDirectory(Path)) 460*0b57cec5SDimitry Andric return EC; 461*0b57cec5SDimitry Andric return {}; 462*0b57cec5SDimitry Andric } 463*0b57cec5SDimitry Andric 464*0b57cec5SDimitry Andric std::error_code OverlayFileSystem::isLocal(const Twine &Path, bool &Result) { 465*0b57cec5SDimitry Andric for (auto &FS : FSList) 466*0b57cec5SDimitry Andric if (FS->exists(Path)) 467*0b57cec5SDimitry Andric return FS->isLocal(Path, Result); 468*0b57cec5SDimitry Andric return errc::no_such_file_or_directory; 469*0b57cec5SDimitry Andric } 470*0b57cec5SDimitry Andric 471*0b57cec5SDimitry Andric std::error_code 472*0b57cec5SDimitry Andric OverlayFileSystem::getRealPath(const Twine &Path, 473*0b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const { 474349cc55cSDimitry Andric for (const auto &FS : FSList) 475*0b57cec5SDimitry Andric if (FS->exists(Path)) 476*0b57cec5SDimitry Andric return FS->getRealPath(Path, Output); 477*0b57cec5SDimitry Andric return errc::no_such_file_or_directory; 478*0b57cec5SDimitry Andric } 479*0b57cec5SDimitry Andric 48081ad6265SDimitry Andric void OverlayFileSystem::printImpl(raw_ostream &OS, PrintType Type, 48181ad6265SDimitry Andric unsigned IndentLevel) const { 48281ad6265SDimitry Andric printIndent(OS, IndentLevel); 48381ad6265SDimitry Andric OS << "OverlayFileSystem\n"; 48481ad6265SDimitry Andric if (Type == PrintType::Summary) 48581ad6265SDimitry Andric return; 48681ad6265SDimitry Andric 48781ad6265SDimitry Andric if (Type == PrintType::Contents) 48881ad6265SDimitry Andric Type = PrintType::Summary; 48981ad6265SDimitry Andric for (auto FS : overlays_range()) 49081ad6265SDimitry Andric FS->print(OS, Type, IndentLevel + 1); 49181ad6265SDimitry Andric } 49281ad6265SDimitry Andric 493*0b57cec5SDimitry Andric llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default; 494*0b57cec5SDimitry Andric 495*0b57cec5SDimitry Andric namespace { 496*0b57cec5SDimitry Andric 497fe6060f1SDimitry Andric /// Combines and deduplicates directory entries across multiple file systems. 498fe6060f1SDimitry Andric class CombiningDirIterImpl : public llvm::vfs::detail::DirIterImpl { 499fe6060f1SDimitry Andric using FileSystemPtr = llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>; 500fe6060f1SDimitry Andric 50181ad6265SDimitry Andric /// Iterators to combine, processed in reverse order. 50281ad6265SDimitry Andric SmallVector<directory_iterator, 8> IterList; 50381ad6265SDimitry Andric /// The iterator currently being traversed. 504*0b57cec5SDimitry Andric directory_iterator CurrentDirIter; 505fe6060f1SDimitry Andric /// The set of names already returned as entries. 506*0b57cec5SDimitry Andric llvm::StringSet<> SeenNames; 507*0b57cec5SDimitry Andric 50881ad6265SDimitry Andric /// Sets \c CurrentDirIter to the next iterator in the list, or leaves it as 50981ad6265SDimitry Andric /// is (at its end position) if we've already gone through them all. 51081ad6265SDimitry Andric std::error_code incrementIter(bool IsFirstTime) { 51181ad6265SDimitry Andric while (!IterList.empty()) { 51281ad6265SDimitry Andric CurrentDirIter = IterList.back(); 51381ad6265SDimitry Andric IterList.pop_back(); 514*0b57cec5SDimitry Andric if (CurrentDirIter != directory_iterator()) 515*0b57cec5SDimitry Andric break; // found 516*0b57cec5SDimitry Andric } 51781ad6265SDimitry Andric 51881ad6265SDimitry Andric if (IsFirstTime && CurrentDirIter == directory_iterator()) 51981ad6265SDimitry Andric return errc::no_such_file_or_directory; 520*0b57cec5SDimitry Andric return {}; 521*0b57cec5SDimitry Andric } 522*0b57cec5SDimitry Andric 523*0b57cec5SDimitry Andric std::error_code incrementDirIter(bool IsFirstTime) { 524*0b57cec5SDimitry Andric assert((IsFirstTime || CurrentDirIter != directory_iterator()) && 525*0b57cec5SDimitry Andric "incrementing past end"); 526*0b57cec5SDimitry Andric std::error_code EC; 527*0b57cec5SDimitry Andric if (!IsFirstTime) 528*0b57cec5SDimitry Andric CurrentDirIter.increment(EC); 529*0b57cec5SDimitry Andric if (!EC && CurrentDirIter == directory_iterator()) 53081ad6265SDimitry Andric EC = incrementIter(IsFirstTime); 531*0b57cec5SDimitry Andric return EC; 532*0b57cec5SDimitry Andric } 533*0b57cec5SDimitry Andric 534*0b57cec5SDimitry Andric std::error_code incrementImpl(bool IsFirstTime) { 535*0b57cec5SDimitry Andric while (true) { 536*0b57cec5SDimitry Andric std::error_code EC = incrementDirIter(IsFirstTime); 537*0b57cec5SDimitry Andric if (EC || CurrentDirIter == directory_iterator()) { 538*0b57cec5SDimitry Andric CurrentEntry = directory_entry(); 539*0b57cec5SDimitry Andric return EC; 540*0b57cec5SDimitry Andric } 541*0b57cec5SDimitry Andric CurrentEntry = *CurrentDirIter; 542*0b57cec5SDimitry Andric StringRef Name = llvm::sys::path::filename(CurrentEntry.path()); 543*0b57cec5SDimitry Andric if (SeenNames.insert(Name).second) 544*0b57cec5SDimitry Andric return EC; // name not seen before 545*0b57cec5SDimitry Andric } 546*0b57cec5SDimitry Andric llvm_unreachable("returned above"); 547*0b57cec5SDimitry Andric } 548*0b57cec5SDimitry Andric 549*0b57cec5SDimitry Andric public: 550fe6060f1SDimitry Andric CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems, std::string Dir, 55181ad6265SDimitry Andric std::error_code &EC) { 55281ad6265SDimitry Andric for (auto FS : FileSystems) { 55381ad6265SDimitry Andric std::error_code FEC; 55481ad6265SDimitry Andric directory_iterator Iter = FS->dir_begin(Dir, FEC); 55581ad6265SDimitry Andric if (FEC && FEC != errc::no_such_file_or_directory) { 55681ad6265SDimitry Andric EC = FEC; 55781ad6265SDimitry Andric return; 55881ad6265SDimitry Andric } 55981ad6265SDimitry Andric if (!FEC) 56081ad6265SDimitry Andric IterList.push_back(Iter); 56181ad6265SDimitry Andric } 562fe6060f1SDimitry Andric EC = incrementImpl(true); 563fe6060f1SDimitry Andric } 564fe6060f1SDimitry Andric 56581ad6265SDimitry Andric CombiningDirIterImpl(ArrayRef<directory_iterator> DirIters, 56681ad6265SDimitry Andric std::error_code &EC) 56781ad6265SDimitry Andric : IterList(DirIters.begin(), DirIters.end()) { 568*0b57cec5SDimitry Andric EC = incrementImpl(true); 569*0b57cec5SDimitry Andric } 570*0b57cec5SDimitry Andric 571*0b57cec5SDimitry Andric std::error_code increment() override { return incrementImpl(false); } 572*0b57cec5SDimitry Andric }; 573*0b57cec5SDimitry Andric 574*0b57cec5SDimitry Andric } // namespace 575*0b57cec5SDimitry Andric 576*0b57cec5SDimitry Andric directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir, 577*0b57cec5SDimitry Andric std::error_code &EC) { 57881ad6265SDimitry Andric directory_iterator Combined = directory_iterator( 579fe6060f1SDimitry Andric std::make_shared<CombiningDirIterImpl>(FSList, Dir.str(), EC)); 58081ad6265SDimitry Andric if (EC) 58181ad6265SDimitry Andric return {}; 58281ad6265SDimitry Andric return Combined; 583*0b57cec5SDimitry Andric } 584*0b57cec5SDimitry Andric 585*0b57cec5SDimitry Andric void ProxyFileSystem::anchor() {} 586*0b57cec5SDimitry Andric 587*0b57cec5SDimitry Andric namespace llvm { 588*0b57cec5SDimitry Andric namespace vfs { 589*0b57cec5SDimitry Andric 590*0b57cec5SDimitry Andric namespace detail { 591*0b57cec5SDimitry Andric 59281ad6265SDimitry Andric enum InMemoryNodeKind { 59381ad6265SDimitry Andric IME_File, 59481ad6265SDimitry Andric IME_Directory, 59581ad6265SDimitry Andric IME_HardLink, 59681ad6265SDimitry Andric IME_SymbolicLink, 59781ad6265SDimitry Andric }; 598*0b57cec5SDimitry Andric 599*0b57cec5SDimitry Andric /// The in memory file system is a tree of Nodes. Every node can either be a 60081ad6265SDimitry Andric /// file, symlink, hardlink or a directory. 601*0b57cec5SDimitry Andric class InMemoryNode { 602*0b57cec5SDimitry Andric InMemoryNodeKind Kind; 603*0b57cec5SDimitry Andric std::string FileName; 604*0b57cec5SDimitry Andric 605*0b57cec5SDimitry Andric public: 606*0b57cec5SDimitry Andric InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind) 6075ffd83dbSDimitry Andric : Kind(Kind), FileName(std::string(llvm::sys::path::filename(FileName))) { 6085ffd83dbSDimitry Andric } 609*0b57cec5SDimitry Andric virtual ~InMemoryNode() = default; 610*0b57cec5SDimitry Andric 61104eeddc0SDimitry Andric /// Return the \p Status for this node. \p RequestedName should be the name 61204eeddc0SDimitry Andric /// through which the caller referred to this node. It will override 61304eeddc0SDimitry Andric /// \p Status::Name in the return value, to mimic the behavior of \p RealFile. 61404eeddc0SDimitry Andric virtual Status getStatus(const Twine &RequestedName) const = 0; 61504eeddc0SDimitry Andric 616*0b57cec5SDimitry Andric /// Get the filename of this node (the name without the directory part). 617*0b57cec5SDimitry Andric StringRef getFileName() const { return FileName; } 618*0b57cec5SDimitry Andric InMemoryNodeKind getKind() const { return Kind; } 619*0b57cec5SDimitry Andric virtual std::string toString(unsigned Indent) const = 0; 620*0b57cec5SDimitry Andric }; 621*0b57cec5SDimitry Andric 622*0b57cec5SDimitry Andric class InMemoryFile : public InMemoryNode { 623*0b57cec5SDimitry Andric Status Stat; 624*0b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer; 625*0b57cec5SDimitry Andric 626*0b57cec5SDimitry Andric public: 627*0b57cec5SDimitry Andric InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer) 628*0b57cec5SDimitry Andric : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)), 629*0b57cec5SDimitry Andric Buffer(std::move(Buffer)) {} 630*0b57cec5SDimitry Andric 63104eeddc0SDimitry Andric Status getStatus(const Twine &RequestedName) const override { 632*0b57cec5SDimitry Andric return Status::copyWithNewName(Stat, RequestedName); 633*0b57cec5SDimitry Andric } 634*0b57cec5SDimitry Andric llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); } 635*0b57cec5SDimitry Andric 636*0b57cec5SDimitry Andric std::string toString(unsigned Indent) const override { 637*0b57cec5SDimitry Andric return (std::string(Indent, ' ') + Stat.getName() + "\n").str(); 638*0b57cec5SDimitry Andric } 639*0b57cec5SDimitry Andric 640*0b57cec5SDimitry Andric static bool classof(const InMemoryNode *N) { 641*0b57cec5SDimitry Andric return N->getKind() == IME_File; 642*0b57cec5SDimitry Andric } 643*0b57cec5SDimitry Andric }; 644*0b57cec5SDimitry Andric 645*0b57cec5SDimitry Andric namespace { 646*0b57cec5SDimitry Andric 647*0b57cec5SDimitry Andric class InMemoryHardLink : public InMemoryNode { 648*0b57cec5SDimitry Andric const InMemoryFile &ResolvedFile; 649*0b57cec5SDimitry Andric 650*0b57cec5SDimitry Andric public: 651*0b57cec5SDimitry Andric InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile) 652*0b57cec5SDimitry Andric : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {} 653*0b57cec5SDimitry Andric const InMemoryFile &getResolvedFile() const { return ResolvedFile; } 654*0b57cec5SDimitry Andric 65504eeddc0SDimitry Andric Status getStatus(const Twine &RequestedName) const override { 65604eeddc0SDimitry Andric return ResolvedFile.getStatus(RequestedName); 65704eeddc0SDimitry Andric } 65804eeddc0SDimitry Andric 659*0b57cec5SDimitry Andric std::string toString(unsigned Indent) const override { 660*0b57cec5SDimitry Andric return std::string(Indent, ' ') + "HardLink to -> " + 661*0b57cec5SDimitry Andric ResolvedFile.toString(0); 662*0b57cec5SDimitry Andric } 663*0b57cec5SDimitry Andric 664*0b57cec5SDimitry Andric static bool classof(const InMemoryNode *N) { 665*0b57cec5SDimitry Andric return N->getKind() == IME_HardLink; 666*0b57cec5SDimitry Andric } 667*0b57cec5SDimitry Andric }; 668*0b57cec5SDimitry Andric 66981ad6265SDimitry Andric class InMemorySymbolicLink : public InMemoryNode { 67081ad6265SDimitry Andric std::string TargetPath; 67181ad6265SDimitry Andric Status Stat; 67281ad6265SDimitry Andric 67381ad6265SDimitry Andric public: 67481ad6265SDimitry Andric InMemorySymbolicLink(StringRef Path, StringRef TargetPath, Status Stat) 67581ad6265SDimitry Andric : InMemoryNode(Path, IME_SymbolicLink), TargetPath(std::move(TargetPath)), 67681ad6265SDimitry Andric Stat(Stat) {} 67781ad6265SDimitry Andric 67881ad6265SDimitry Andric std::string toString(unsigned Indent) const override { 67981ad6265SDimitry Andric return std::string(Indent, ' ') + "SymbolicLink to -> " + TargetPath; 68081ad6265SDimitry Andric } 68181ad6265SDimitry Andric 68281ad6265SDimitry Andric Status getStatus(const Twine &RequestedName) const override { 68381ad6265SDimitry Andric return Status::copyWithNewName(Stat, RequestedName); 68481ad6265SDimitry Andric } 68581ad6265SDimitry Andric 68681ad6265SDimitry Andric StringRef getTargetPath() const { return TargetPath; } 68781ad6265SDimitry Andric 68881ad6265SDimitry Andric static bool classof(const InMemoryNode *N) { 68981ad6265SDimitry Andric return N->getKind() == IME_SymbolicLink; 69081ad6265SDimitry Andric } 69181ad6265SDimitry Andric }; 69281ad6265SDimitry Andric 693*0b57cec5SDimitry Andric /// Adapt a InMemoryFile for VFS' File interface. The goal is to make 694*0b57cec5SDimitry Andric /// \p InMemoryFileAdaptor mimic as much as possible the behavior of 695*0b57cec5SDimitry Andric /// \p RealFile. 696*0b57cec5SDimitry Andric class InMemoryFileAdaptor : public File { 697*0b57cec5SDimitry Andric const InMemoryFile &Node; 698*0b57cec5SDimitry Andric /// The name to use when returning a Status for this file. 699*0b57cec5SDimitry Andric std::string RequestedName; 700*0b57cec5SDimitry Andric 701*0b57cec5SDimitry Andric public: 702*0b57cec5SDimitry Andric explicit InMemoryFileAdaptor(const InMemoryFile &Node, 703*0b57cec5SDimitry Andric std::string RequestedName) 704*0b57cec5SDimitry Andric : Node(Node), RequestedName(std::move(RequestedName)) {} 705*0b57cec5SDimitry Andric 706*0b57cec5SDimitry Andric llvm::ErrorOr<Status> status() override { 707*0b57cec5SDimitry Andric return Node.getStatus(RequestedName); 708*0b57cec5SDimitry Andric } 709*0b57cec5SDimitry Andric 710*0b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 711*0b57cec5SDimitry Andric getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 712*0b57cec5SDimitry Andric bool IsVolatile) override { 713*0b57cec5SDimitry Andric llvm::MemoryBuffer *Buf = Node.getBuffer(); 714*0b57cec5SDimitry Andric return llvm::MemoryBuffer::getMemBuffer( 715*0b57cec5SDimitry Andric Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator); 716*0b57cec5SDimitry Andric } 717*0b57cec5SDimitry Andric 718*0b57cec5SDimitry Andric std::error_code close() override { return {}; } 719349cc55cSDimitry Andric 720349cc55cSDimitry Andric void setPath(const Twine &Path) override { RequestedName = Path.str(); } 721*0b57cec5SDimitry Andric }; 722*0b57cec5SDimitry Andric } // namespace 723*0b57cec5SDimitry Andric 724*0b57cec5SDimitry Andric class InMemoryDirectory : public InMemoryNode { 725*0b57cec5SDimitry Andric Status Stat; 726*0b57cec5SDimitry Andric llvm::StringMap<std::unique_ptr<InMemoryNode>> Entries; 727*0b57cec5SDimitry Andric 728*0b57cec5SDimitry Andric public: 729*0b57cec5SDimitry Andric InMemoryDirectory(Status Stat) 730*0b57cec5SDimitry Andric : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {} 731*0b57cec5SDimitry Andric 732*0b57cec5SDimitry Andric /// Return the \p Status for this node. \p RequestedName should be the name 733*0b57cec5SDimitry Andric /// through which the caller referred to this node. It will override 734*0b57cec5SDimitry Andric /// \p Status::Name in the return value, to mimic the behavior of \p RealFile. 73504eeddc0SDimitry Andric Status getStatus(const Twine &RequestedName) const override { 736*0b57cec5SDimitry Andric return Status::copyWithNewName(Stat, RequestedName); 737*0b57cec5SDimitry Andric } 738349cc55cSDimitry Andric 739349cc55cSDimitry Andric UniqueID getUniqueID() const { return Stat.getUniqueID(); } 740349cc55cSDimitry Andric 74181ad6265SDimitry Andric InMemoryNode *getChild(StringRef Name) const { 742*0b57cec5SDimitry Andric auto I = Entries.find(Name); 743*0b57cec5SDimitry Andric if (I != Entries.end()) 744*0b57cec5SDimitry Andric return I->second.get(); 745*0b57cec5SDimitry Andric return nullptr; 746*0b57cec5SDimitry Andric } 747*0b57cec5SDimitry Andric 748*0b57cec5SDimitry Andric InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) { 749*0b57cec5SDimitry Andric return Entries.insert(make_pair(Name, std::move(Child))) 750*0b57cec5SDimitry Andric .first->second.get(); 751*0b57cec5SDimitry Andric } 752*0b57cec5SDimitry Andric 753*0b57cec5SDimitry Andric using const_iterator = decltype(Entries)::const_iterator; 754*0b57cec5SDimitry Andric 755*0b57cec5SDimitry Andric const_iterator begin() const { return Entries.begin(); } 756*0b57cec5SDimitry Andric const_iterator end() const { return Entries.end(); } 757*0b57cec5SDimitry Andric 758*0b57cec5SDimitry Andric std::string toString(unsigned Indent) const override { 759*0b57cec5SDimitry Andric std::string Result = 760*0b57cec5SDimitry Andric (std::string(Indent, ' ') + Stat.getName() + "\n").str(); 761*0b57cec5SDimitry Andric for (const auto &Entry : Entries) 762*0b57cec5SDimitry Andric Result += Entry.second->toString(Indent + 2); 763*0b57cec5SDimitry Andric return Result; 764*0b57cec5SDimitry Andric } 765*0b57cec5SDimitry Andric 766*0b57cec5SDimitry Andric static bool classof(const InMemoryNode *N) { 767*0b57cec5SDimitry Andric return N->getKind() == IME_Directory; 768*0b57cec5SDimitry Andric } 769*0b57cec5SDimitry Andric }; 770*0b57cec5SDimitry Andric 771*0b57cec5SDimitry Andric } // namespace detail 772*0b57cec5SDimitry Andric 773349cc55cSDimitry Andric // The UniqueID of in-memory files is derived from path and content. 774349cc55cSDimitry Andric // This avoids difficulties in creating exactly equivalent in-memory FSes, 775349cc55cSDimitry Andric // as often needed in multithreaded programs. 776349cc55cSDimitry Andric static sys::fs::UniqueID getUniqueID(hash_code Hash) { 777349cc55cSDimitry Andric return sys::fs::UniqueID(std::numeric_limits<uint64_t>::max(), 778349cc55cSDimitry Andric uint64_t(size_t(Hash))); 779349cc55cSDimitry Andric } 780349cc55cSDimitry Andric static sys::fs::UniqueID getFileID(sys::fs::UniqueID Parent, 781349cc55cSDimitry Andric llvm::StringRef Name, 782349cc55cSDimitry Andric llvm::StringRef Contents) { 783349cc55cSDimitry Andric return getUniqueID(llvm::hash_combine(Parent.getFile(), Name, Contents)); 784349cc55cSDimitry Andric } 785349cc55cSDimitry Andric static sys::fs::UniqueID getDirectoryID(sys::fs::UniqueID Parent, 786349cc55cSDimitry Andric llvm::StringRef Name) { 787349cc55cSDimitry Andric return getUniqueID(llvm::hash_combine(Parent.getFile(), Name)); 788349cc55cSDimitry Andric } 789349cc55cSDimitry Andric 79004eeddc0SDimitry Andric Status detail::NewInMemoryNodeInfo::makeStatus() const { 79104eeddc0SDimitry Andric UniqueID UID = 79204eeddc0SDimitry Andric (Type == sys::fs::file_type::directory_file) 79304eeddc0SDimitry Andric ? getDirectoryID(DirUID, Name) 79404eeddc0SDimitry Andric : getFileID(DirUID, Name, Buffer ? Buffer->getBuffer() : ""); 79504eeddc0SDimitry Andric 79604eeddc0SDimitry Andric return Status(Path, UID, llvm::sys::toTimePoint(ModificationTime), User, 79704eeddc0SDimitry Andric Group, Buffer ? Buffer->getBufferSize() : 0, Type, Perms); 79804eeddc0SDimitry Andric } 79904eeddc0SDimitry Andric 800*0b57cec5SDimitry Andric InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths) 801*0b57cec5SDimitry Andric : Root(new detail::InMemoryDirectory( 802349cc55cSDimitry Andric Status("", getDirectoryID(llvm::sys::fs::UniqueID(), ""), 803349cc55cSDimitry Andric llvm::sys::TimePoint<>(), 0, 0, 0, 804349cc55cSDimitry Andric llvm::sys::fs::file_type::directory_file, 805*0b57cec5SDimitry Andric llvm::sys::fs::perms::all_all))), 806*0b57cec5SDimitry Andric UseNormalizedPaths(UseNormalizedPaths) {} 807*0b57cec5SDimitry Andric 808*0b57cec5SDimitry Andric InMemoryFileSystem::~InMemoryFileSystem() = default; 809*0b57cec5SDimitry Andric 810*0b57cec5SDimitry Andric std::string InMemoryFileSystem::toString() const { 811*0b57cec5SDimitry Andric return Root->toString(/*Indent=*/0); 812*0b57cec5SDimitry Andric } 813*0b57cec5SDimitry Andric 814*0b57cec5SDimitry Andric bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime, 815*0b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer, 816bdd1243dSDimitry Andric std::optional<uint32_t> User, 817bdd1243dSDimitry Andric std::optional<uint32_t> Group, 818bdd1243dSDimitry Andric std::optional<llvm::sys::fs::file_type> Type, 819bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms, 82004eeddc0SDimitry Andric MakeNodeFn MakeNode) { 821*0b57cec5SDimitry Andric SmallString<128> Path; 822*0b57cec5SDimitry Andric P.toVector(Path); 823*0b57cec5SDimitry Andric 824*0b57cec5SDimitry Andric // Fix up relative paths. This just prepends the current working directory. 825*0b57cec5SDimitry Andric std::error_code EC = makeAbsolute(Path); 826*0b57cec5SDimitry Andric assert(!EC); 827*0b57cec5SDimitry Andric (void)EC; 828*0b57cec5SDimitry Andric 829*0b57cec5SDimitry Andric if (useNormalizedPaths()) 830*0b57cec5SDimitry Andric llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 831*0b57cec5SDimitry Andric 832*0b57cec5SDimitry Andric if (Path.empty()) 833*0b57cec5SDimitry Andric return false; 834*0b57cec5SDimitry Andric 835*0b57cec5SDimitry Andric detail::InMemoryDirectory *Dir = Root.get(); 836*0b57cec5SDimitry Andric auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path); 83781ad6265SDimitry Andric const auto ResolvedUser = User.value_or(0); 83881ad6265SDimitry Andric const auto ResolvedGroup = Group.value_or(0); 83981ad6265SDimitry Andric const auto ResolvedType = Type.value_or(sys::fs::file_type::regular_file); 84081ad6265SDimitry Andric const auto ResolvedPerms = Perms.value_or(sys::fs::all_all); 841*0b57cec5SDimitry Andric // Any intermediate directories we create should be accessible by 842*0b57cec5SDimitry Andric // the owner, even if Perms says otherwise for the final path. 843*0b57cec5SDimitry Andric const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all; 844*0b57cec5SDimitry Andric while (true) { 845*0b57cec5SDimitry Andric StringRef Name = *I; 846*0b57cec5SDimitry Andric detail::InMemoryNode *Node = Dir->getChild(Name); 847*0b57cec5SDimitry Andric ++I; 848*0b57cec5SDimitry Andric if (!Node) { 849*0b57cec5SDimitry Andric if (I == E) { 850*0b57cec5SDimitry Andric // End of the path. 85104eeddc0SDimitry Andric Dir->addChild( 85204eeddc0SDimitry Andric Name, MakeNode({Dir->getUniqueID(), Path, Name, ModificationTime, 85304eeddc0SDimitry Andric std::move(Buffer), ResolvedUser, ResolvedGroup, 85404eeddc0SDimitry Andric ResolvedType, ResolvedPerms})); 855*0b57cec5SDimitry Andric return true; 856*0b57cec5SDimitry Andric } 857*0b57cec5SDimitry Andric 858*0b57cec5SDimitry Andric // Create a new directory. Use the path up to here. 859*0b57cec5SDimitry Andric Status Stat( 860*0b57cec5SDimitry Andric StringRef(Path.str().begin(), Name.end() - Path.str().begin()), 861349cc55cSDimitry Andric getDirectoryID(Dir->getUniqueID(), Name), 862349cc55cSDimitry Andric llvm::sys::toTimePoint(ModificationTime), ResolvedUser, ResolvedGroup, 863349cc55cSDimitry Andric 0, sys::fs::file_type::directory_file, NewDirectoryPerms); 864*0b57cec5SDimitry Andric Dir = cast<detail::InMemoryDirectory>(Dir->addChild( 8658bcb0991SDimitry Andric Name, std::make_unique<detail::InMemoryDirectory>(std::move(Stat)))); 866*0b57cec5SDimitry Andric continue; 867*0b57cec5SDimitry Andric } 868*0b57cec5SDimitry Andric 869*0b57cec5SDimitry Andric if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) { 870*0b57cec5SDimitry Andric Dir = NewDir; 871*0b57cec5SDimitry Andric } else { 872*0b57cec5SDimitry Andric assert((isa<detail::InMemoryFile>(Node) || 873*0b57cec5SDimitry Andric isa<detail::InMemoryHardLink>(Node)) && 874*0b57cec5SDimitry Andric "Must be either file, hardlink or directory!"); 875*0b57cec5SDimitry Andric 876*0b57cec5SDimitry Andric // Trying to insert a directory in place of a file. 877*0b57cec5SDimitry Andric if (I != E) 878*0b57cec5SDimitry Andric return false; 879*0b57cec5SDimitry Andric 880*0b57cec5SDimitry Andric // Return false only if the new file is different from the existing one. 881*0b57cec5SDimitry Andric if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) { 882*0b57cec5SDimitry Andric return Link->getResolvedFile().getBuffer()->getBuffer() == 883*0b57cec5SDimitry Andric Buffer->getBuffer(); 884*0b57cec5SDimitry Andric } 885*0b57cec5SDimitry Andric return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() == 886*0b57cec5SDimitry Andric Buffer->getBuffer(); 887*0b57cec5SDimitry Andric } 888*0b57cec5SDimitry Andric } 889*0b57cec5SDimitry Andric } 890*0b57cec5SDimitry Andric 891*0b57cec5SDimitry Andric bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime, 892*0b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer, 893bdd1243dSDimitry Andric std::optional<uint32_t> User, 894bdd1243dSDimitry Andric std::optional<uint32_t> Group, 895bdd1243dSDimitry Andric std::optional<llvm::sys::fs::file_type> Type, 896bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms) { 897*0b57cec5SDimitry Andric return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type, 89804eeddc0SDimitry Andric Perms, 89904eeddc0SDimitry Andric [](detail::NewInMemoryNodeInfo NNI) 90004eeddc0SDimitry Andric -> std::unique_ptr<detail::InMemoryNode> { 90104eeddc0SDimitry Andric Status Stat = NNI.makeStatus(); 90204eeddc0SDimitry Andric if (Stat.getType() == sys::fs::file_type::directory_file) 90304eeddc0SDimitry Andric return std::make_unique<detail::InMemoryDirectory>(Stat); 90404eeddc0SDimitry Andric return std::make_unique<detail::InMemoryFile>( 90504eeddc0SDimitry Andric Stat, std::move(NNI.Buffer)); 90604eeddc0SDimitry Andric }); 907*0b57cec5SDimitry Andric } 908*0b57cec5SDimitry Andric 909bdd1243dSDimitry Andric bool InMemoryFileSystem::addFileNoOwn( 910bdd1243dSDimitry Andric const Twine &P, time_t ModificationTime, 911bdd1243dSDimitry Andric const llvm::MemoryBufferRef &Buffer, std::optional<uint32_t> User, 912bdd1243dSDimitry Andric std::optional<uint32_t> Group, std::optional<llvm::sys::fs::file_type> Type, 913bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms) { 914e8d8bef9SDimitry Andric return addFile(P, ModificationTime, llvm::MemoryBuffer::getMemBuffer(Buffer), 915*0b57cec5SDimitry Andric std::move(User), std::move(Group), std::move(Type), 91604eeddc0SDimitry Andric std::move(Perms), 91704eeddc0SDimitry Andric [](detail::NewInMemoryNodeInfo NNI) 91804eeddc0SDimitry Andric -> std::unique_ptr<detail::InMemoryNode> { 91904eeddc0SDimitry Andric Status Stat = NNI.makeStatus(); 92004eeddc0SDimitry Andric if (Stat.getType() == sys::fs::file_type::directory_file) 92104eeddc0SDimitry Andric return std::make_unique<detail::InMemoryDirectory>(Stat); 92204eeddc0SDimitry Andric return std::make_unique<detail::InMemoryFile>( 92304eeddc0SDimitry Andric Stat, std::move(NNI.Buffer)); 92404eeddc0SDimitry Andric }); 925*0b57cec5SDimitry Andric } 926*0b57cec5SDimitry Andric 92781ad6265SDimitry Andric detail::NamedNodeOrError 92881ad6265SDimitry Andric InMemoryFileSystem::lookupNode(const Twine &P, bool FollowFinalSymlink, 92981ad6265SDimitry Andric size_t SymlinkDepth) const { 930*0b57cec5SDimitry Andric SmallString<128> Path; 931*0b57cec5SDimitry Andric P.toVector(Path); 932*0b57cec5SDimitry Andric 933*0b57cec5SDimitry Andric // Fix up relative paths. This just prepends the current working directory. 93481ad6265SDimitry Andric std::error_code EC = makeAbsolute(Path); 935*0b57cec5SDimitry Andric assert(!EC); 936*0b57cec5SDimitry Andric (void)EC; 937*0b57cec5SDimitry Andric 93881ad6265SDimitry Andric if (useNormalizedPaths()) 939*0b57cec5SDimitry Andric llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 940*0b57cec5SDimitry Andric 94181ad6265SDimitry Andric const detail::InMemoryDirectory *Dir = Root.get(); 942*0b57cec5SDimitry Andric if (Path.empty()) 94381ad6265SDimitry Andric return detail::NamedNodeOrError(Path, Dir); 944*0b57cec5SDimitry Andric 945*0b57cec5SDimitry Andric auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path); 946*0b57cec5SDimitry Andric while (true) { 947*0b57cec5SDimitry Andric detail::InMemoryNode *Node = Dir->getChild(*I); 948*0b57cec5SDimitry Andric ++I; 949*0b57cec5SDimitry Andric if (!Node) 950*0b57cec5SDimitry Andric return errc::no_such_file_or_directory; 951*0b57cec5SDimitry Andric 95281ad6265SDimitry Andric if (auto Symlink = dyn_cast<detail::InMemorySymbolicLink>(Node)) { 95381ad6265SDimitry Andric // If we're at the end of the path, and we're not following through 95481ad6265SDimitry Andric // terminal symlinks, then we're done. 95581ad6265SDimitry Andric if (I == E && !FollowFinalSymlink) 95681ad6265SDimitry Andric return detail::NamedNodeOrError(Path, Symlink); 95781ad6265SDimitry Andric 95881ad6265SDimitry Andric if (SymlinkDepth > InMemoryFileSystem::MaxSymlinkDepth) 95981ad6265SDimitry Andric return errc::no_such_file_or_directory; 96081ad6265SDimitry Andric 96181ad6265SDimitry Andric SmallString<128> TargetPath = Symlink->getTargetPath(); 96281ad6265SDimitry Andric if (std::error_code EC = makeAbsolute(TargetPath)) 96381ad6265SDimitry Andric return EC; 96481ad6265SDimitry Andric 96581ad6265SDimitry Andric // Keep going with the target. We always want to follow symlinks here 96681ad6265SDimitry Andric // because we're either at the end of a path that we want to follow, or 96781ad6265SDimitry Andric // not at the end of a path, in which case we need to follow the symlink 96881ad6265SDimitry Andric // regardless. 96981ad6265SDimitry Andric auto Target = 97081ad6265SDimitry Andric lookupNode(TargetPath, /*FollowFinalSymlink=*/true, SymlinkDepth + 1); 97181ad6265SDimitry Andric if (!Target || I == E) 97281ad6265SDimitry Andric return Target; 97381ad6265SDimitry Andric 97481ad6265SDimitry Andric if (!isa<detail::InMemoryDirectory>(*Target)) 97581ad6265SDimitry Andric return errc::no_such_file_or_directory; 97681ad6265SDimitry Andric 97781ad6265SDimitry Andric // Otherwise, continue on the search in the symlinked directory. 97881ad6265SDimitry Andric Dir = cast<detail::InMemoryDirectory>(*Target); 97981ad6265SDimitry Andric continue; 98081ad6265SDimitry Andric } 98181ad6265SDimitry Andric 982*0b57cec5SDimitry Andric // Return the file if it's at the end of the path. 983*0b57cec5SDimitry Andric if (auto File = dyn_cast<detail::InMemoryFile>(Node)) { 984*0b57cec5SDimitry Andric if (I == E) 98581ad6265SDimitry Andric return detail::NamedNodeOrError(Path, File); 986*0b57cec5SDimitry Andric return errc::no_such_file_or_directory; 987*0b57cec5SDimitry Andric } 988*0b57cec5SDimitry Andric 989*0b57cec5SDimitry Andric // If Node is HardLink then return the resolved file. 990*0b57cec5SDimitry Andric if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) { 991*0b57cec5SDimitry Andric if (I == E) 99281ad6265SDimitry Andric return detail::NamedNodeOrError(Path, &File->getResolvedFile()); 993*0b57cec5SDimitry Andric return errc::no_such_file_or_directory; 994*0b57cec5SDimitry Andric } 995*0b57cec5SDimitry Andric // Traverse directories. 996*0b57cec5SDimitry Andric Dir = cast<detail::InMemoryDirectory>(Node); 997*0b57cec5SDimitry Andric if (I == E) 99881ad6265SDimitry Andric return detail::NamedNodeOrError(Path, Dir); 999*0b57cec5SDimitry Andric } 1000*0b57cec5SDimitry Andric } 1001*0b57cec5SDimitry Andric 100281ad6265SDimitry Andric bool InMemoryFileSystem::addHardLink(const Twine &NewLink, 100381ad6265SDimitry Andric const Twine &Target) { 100481ad6265SDimitry Andric auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false); 100581ad6265SDimitry Andric // Whether symlinks in the hardlink target are followed is 100681ad6265SDimitry Andric // implementation-defined in POSIX. 100781ad6265SDimitry Andric // We're following symlinks here to be consistent with macOS. 100881ad6265SDimitry Andric auto TargetNode = lookupNode(Target, /*FollowFinalSymlink=*/true); 1009*0b57cec5SDimitry Andric // FromPath must not have been added before. ToPath must have been added 1010*0b57cec5SDimitry Andric // before. Resolved ToPath must be a File. 101181ad6265SDimitry Andric if (!TargetNode || NewLinkNode || !isa<detail::InMemoryFile>(*TargetNode)) 1012*0b57cec5SDimitry Andric return false; 1013bdd1243dSDimitry Andric return addFile(NewLink, 0, nullptr, std::nullopt, std::nullopt, std::nullopt, 1014bdd1243dSDimitry Andric std::nullopt, [&](detail::NewInMemoryNodeInfo NNI) { 101504eeddc0SDimitry Andric return std::make_unique<detail::InMemoryHardLink>( 101681ad6265SDimitry Andric NNI.Path.str(), 101781ad6265SDimitry Andric *cast<detail::InMemoryFile>(*TargetNode)); 101881ad6265SDimitry Andric }); 101981ad6265SDimitry Andric } 102081ad6265SDimitry Andric 1021bdd1243dSDimitry Andric bool InMemoryFileSystem::addSymbolicLink( 1022bdd1243dSDimitry Andric const Twine &NewLink, const Twine &Target, time_t ModificationTime, 1023bdd1243dSDimitry Andric std::optional<uint32_t> User, std::optional<uint32_t> Group, 1024bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms) { 102581ad6265SDimitry Andric auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false); 102681ad6265SDimitry Andric if (NewLinkNode) 102781ad6265SDimitry Andric return false; 102881ad6265SDimitry Andric 102981ad6265SDimitry Andric SmallString<128> NewLinkStr, TargetStr; 103081ad6265SDimitry Andric NewLink.toVector(NewLinkStr); 103181ad6265SDimitry Andric Target.toVector(TargetStr); 103281ad6265SDimitry Andric 103381ad6265SDimitry Andric return addFile(NewLinkStr, ModificationTime, nullptr, User, Group, 103481ad6265SDimitry Andric sys::fs::file_type::symlink_file, Perms, 103581ad6265SDimitry Andric [&](detail::NewInMemoryNodeInfo NNI) { 103681ad6265SDimitry Andric return std::make_unique<detail::InMemorySymbolicLink>( 103781ad6265SDimitry Andric NewLinkStr, TargetStr, NNI.makeStatus()); 103804eeddc0SDimitry Andric }); 1039*0b57cec5SDimitry Andric } 1040*0b57cec5SDimitry Andric 1041*0b57cec5SDimitry Andric llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) { 104281ad6265SDimitry Andric auto Node = lookupNode(Path, /*FollowFinalSymlink=*/true); 1043*0b57cec5SDimitry Andric if (Node) 104404eeddc0SDimitry Andric return (*Node)->getStatus(Path); 1045*0b57cec5SDimitry Andric return Node.getError(); 1046*0b57cec5SDimitry Andric } 1047*0b57cec5SDimitry Andric 1048*0b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<File>> 1049*0b57cec5SDimitry Andric InMemoryFileSystem::openFileForRead(const Twine &Path) { 105081ad6265SDimitry Andric auto Node = lookupNode(Path,/*FollowFinalSymlink=*/true); 1051*0b57cec5SDimitry Andric if (!Node) 1052*0b57cec5SDimitry Andric return Node.getError(); 1053*0b57cec5SDimitry Andric 1054*0b57cec5SDimitry Andric // When we have a file provide a heap-allocated wrapper for the memory buffer 1055*0b57cec5SDimitry Andric // to match the ownership semantics for File. 1056*0b57cec5SDimitry Andric if (auto *F = dyn_cast<detail::InMemoryFile>(*Node)) 1057*0b57cec5SDimitry Andric return std::unique_ptr<File>( 1058*0b57cec5SDimitry Andric new detail::InMemoryFileAdaptor(*F, Path.str())); 1059*0b57cec5SDimitry Andric 1060*0b57cec5SDimitry Andric // FIXME: errc::not_a_file? 1061*0b57cec5SDimitry Andric return make_error_code(llvm::errc::invalid_argument); 1062*0b57cec5SDimitry Andric } 1063*0b57cec5SDimitry Andric 1064*0b57cec5SDimitry Andric /// Adaptor from InMemoryDir::iterator to directory_iterator. 106581ad6265SDimitry Andric class InMemoryFileSystem::DirIterator : public llvm::vfs::detail::DirIterImpl { 106681ad6265SDimitry Andric const InMemoryFileSystem *FS; 1067*0b57cec5SDimitry Andric detail::InMemoryDirectory::const_iterator I; 1068*0b57cec5SDimitry Andric detail::InMemoryDirectory::const_iterator E; 1069*0b57cec5SDimitry Andric std::string RequestedDirName; 1070*0b57cec5SDimitry Andric 1071*0b57cec5SDimitry Andric void setCurrentEntry() { 1072*0b57cec5SDimitry Andric if (I != E) { 1073*0b57cec5SDimitry Andric SmallString<256> Path(RequestedDirName); 1074*0b57cec5SDimitry Andric llvm::sys::path::append(Path, I->second->getFileName()); 1075480093f4SDimitry Andric sys::fs::file_type Type = sys::fs::file_type::type_unknown; 1076*0b57cec5SDimitry Andric switch (I->second->getKind()) { 1077*0b57cec5SDimitry Andric case detail::IME_File: 1078*0b57cec5SDimitry Andric case detail::IME_HardLink: 1079*0b57cec5SDimitry Andric Type = sys::fs::file_type::regular_file; 1080*0b57cec5SDimitry Andric break; 1081*0b57cec5SDimitry Andric case detail::IME_Directory: 1082*0b57cec5SDimitry Andric Type = sys::fs::file_type::directory_file; 1083*0b57cec5SDimitry Andric break; 108481ad6265SDimitry Andric case detail::IME_SymbolicLink: 108581ad6265SDimitry Andric if (auto SymlinkTarget = 108681ad6265SDimitry Andric FS->lookupNode(Path, /*FollowFinalSymlink=*/true)) { 108781ad6265SDimitry Andric Path = SymlinkTarget.getName(); 108881ad6265SDimitry Andric Type = (*SymlinkTarget)->getStatus(Path).getType(); 108981ad6265SDimitry Andric } 109081ad6265SDimitry Andric break; 1091*0b57cec5SDimitry Andric } 10925ffd83dbSDimitry Andric CurrentEntry = directory_entry(std::string(Path.str()), Type); 1093*0b57cec5SDimitry Andric } else { 1094*0b57cec5SDimitry Andric // When we're at the end, make CurrentEntry invalid and DirIterImpl will 1095*0b57cec5SDimitry Andric // do the rest. 1096*0b57cec5SDimitry Andric CurrentEntry = directory_entry(); 1097*0b57cec5SDimitry Andric } 1098*0b57cec5SDimitry Andric } 1099*0b57cec5SDimitry Andric 1100*0b57cec5SDimitry Andric public: 110181ad6265SDimitry Andric DirIterator() = default; 1102*0b57cec5SDimitry Andric 110381ad6265SDimitry Andric DirIterator(const InMemoryFileSystem *FS, 110481ad6265SDimitry Andric const detail::InMemoryDirectory &Dir, 1105*0b57cec5SDimitry Andric std::string RequestedDirName) 110681ad6265SDimitry Andric : FS(FS), I(Dir.begin()), E(Dir.end()), 1107*0b57cec5SDimitry Andric RequestedDirName(std::move(RequestedDirName)) { 1108*0b57cec5SDimitry Andric setCurrentEntry(); 1109*0b57cec5SDimitry Andric } 1110*0b57cec5SDimitry Andric 1111*0b57cec5SDimitry Andric std::error_code increment() override { 1112*0b57cec5SDimitry Andric ++I; 1113*0b57cec5SDimitry Andric setCurrentEntry(); 1114*0b57cec5SDimitry Andric return {}; 1115*0b57cec5SDimitry Andric } 1116*0b57cec5SDimitry Andric }; 1117*0b57cec5SDimitry Andric 1118*0b57cec5SDimitry Andric directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir, 1119*0b57cec5SDimitry Andric std::error_code &EC) { 112081ad6265SDimitry Andric auto Node = lookupNode(Dir, /*FollowFinalSymlink=*/true); 1121*0b57cec5SDimitry Andric if (!Node) { 1122*0b57cec5SDimitry Andric EC = Node.getError(); 112381ad6265SDimitry Andric return directory_iterator(std::make_shared<DirIterator>()); 1124*0b57cec5SDimitry Andric } 1125*0b57cec5SDimitry Andric 1126*0b57cec5SDimitry Andric if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node)) 1127*0b57cec5SDimitry Andric return directory_iterator( 112881ad6265SDimitry Andric std::make_shared<DirIterator>(this, *DirNode, Dir.str())); 1129*0b57cec5SDimitry Andric 1130*0b57cec5SDimitry Andric EC = make_error_code(llvm::errc::not_a_directory); 113181ad6265SDimitry Andric return directory_iterator(std::make_shared<DirIterator>()); 1132*0b57cec5SDimitry Andric } 1133*0b57cec5SDimitry Andric 1134*0b57cec5SDimitry Andric std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) { 1135*0b57cec5SDimitry Andric SmallString<128> Path; 1136*0b57cec5SDimitry Andric P.toVector(Path); 1137*0b57cec5SDimitry Andric 1138*0b57cec5SDimitry Andric // Fix up relative paths. This just prepends the current working directory. 1139*0b57cec5SDimitry Andric std::error_code EC = makeAbsolute(Path); 1140*0b57cec5SDimitry Andric assert(!EC); 1141*0b57cec5SDimitry Andric (void)EC; 1142*0b57cec5SDimitry Andric 1143*0b57cec5SDimitry Andric if (useNormalizedPaths()) 1144*0b57cec5SDimitry Andric llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); 1145*0b57cec5SDimitry Andric 1146*0b57cec5SDimitry Andric if (!Path.empty()) 11475ffd83dbSDimitry Andric WorkingDirectory = std::string(Path.str()); 1148*0b57cec5SDimitry Andric return {}; 1149*0b57cec5SDimitry Andric } 1150*0b57cec5SDimitry Andric 1151*0b57cec5SDimitry Andric std::error_code 1152*0b57cec5SDimitry Andric InMemoryFileSystem::getRealPath(const Twine &Path, 1153*0b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const { 1154*0b57cec5SDimitry Andric auto CWD = getCurrentWorkingDirectory(); 1155*0b57cec5SDimitry Andric if (!CWD || CWD->empty()) 1156*0b57cec5SDimitry Andric return errc::operation_not_permitted; 1157*0b57cec5SDimitry Andric Path.toVector(Output); 1158*0b57cec5SDimitry Andric if (auto EC = makeAbsolute(Output)) 1159*0b57cec5SDimitry Andric return EC; 1160*0b57cec5SDimitry Andric llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true); 1161*0b57cec5SDimitry Andric return {}; 1162*0b57cec5SDimitry Andric } 1163*0b57cec5SDimitry Andric 1164*0b57cec5SDimitry Andric std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) { 1165*0b57cec5SDimitry Andric Result = false; 1166*0b57cec5SDimitry Andric return {}; 1167*0b57cec5SDimitry Andric } 1168*0b57cec5SDimitry Andric 116981ad6265SDimitry Andric void InMemoryFileSystem::printImpl(raw_ostream &OS, PrintType PrintContents, 117081ad6265SDimitry Andric unsigned IndentLevel) const { 117181ad6265SDimitry Andric printIndent(OS, IndentLevel); 117281ad6265SDimitry Andric OS << "InMemoryFileSystem\n"; 117381ad6265SDimitry Andric } 117481ad6265SDimitry Andric 1175*0b57cec5SDimitry Andric } // namespace vfs 1176*0b57cec5SDimitry Andric } // namespace llvm 1177*0b57cec5SDimitry Andric 1178*0b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/ 1179*0b57cec5SDimitry Andric // RedirectingFileSystem implementation 1180*0b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/ 1181*0b57cec5SDimitry Andric 11825ffd83dbSDimitry Andric namespace { 11835ffd83dbSDimitry Andric 1184fe6060f1SDimitry Andric static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) { 1185fe6060f1SDimitry Andric // Detect the path style in use by checking the first separator. 11865ffd83dbSDimitry Andric llvm::sys::path::Style style = llvm::sys::path::Style::native; 11875ffd83dbSDimitry Andric const size_t n = Path.find_first_of("/\\"); 1188349cc55cSDimitry Andric // Can't distinguish between posix and windows_slash here. 11895ffd83dbSDimitry Andric if (n != static_cast<size_t>(-1)) 11905ffd83dbSDimitry Andric style = (Path[n] == '/') ? llvm::sys::path::Style::posix 1191349cc55cSDimitry Andric : llvm::sys::path::Style::windows_backslash; 1192fe6060f1SDimitry Andric return style; 1193fe6060f1SDimitry Andric } 1194fe6060f1SDimitry Andric 1195fe6060f1SDimitry Andric /// Removes leading "./" as well as path components like ".." and ".". 1196fe6060f1SDimitry Andric static llvm::SmallString<256> canonicalize(llvm::StringRef Path) { 1197fe6060f1SDimitry Andric // First detect the path style in use by checking the first separator. 1198fe6060f1SDimitry Andric llvm::sys::path::Style style = getExistingStyle(Path); 11995ffd83dbSDimitry Andric 12005ffd83dbSDimitry Andric // Now remove the dots. Explicitly specifying the path style prevents the 12015ffd83dbSDimitry Andric // direction of the slashes from changing. 12025ffd83dbSDimitry Andric llvm::SmallString<256> result = 12035ffd83dbSDimitry Andric llvm::sys::path::remove_leading_dotslash(Path, style); 12045ffd83dbSDimitry Andric llvm::sys::path::remove_dots(result, /*remove_dot_dot=*/true, style); 12055ffd83dbSDimitry Andric return result; 12065ffd83dbSDimitry Andric } 12075ffd83dbSDimitry Andric 120881ad6265SDimitry Andric /// Whether the error and entry specify a file/directory that was not found. 120981ad6265SDimitry Andric static bool isFileNotFound(std::error_code EC, 121081ad6265SDimitry Andric RedirectingFileSystem::Entry *E = nullptr) { 121181ad6265SDimitry Andric if (E && !isa<RedirectingFileSystem::DirectoryRemapEntry>(E)) 121281ad6265SDimitry Andric return false; 121381ad6265SDimitry Andric return EC == llvm::errc::no_such_file_or_directory; 121481ad6265SDimitry Andric } 121581ad6265SDimitry Andric 12165ffd83dbSDimitry Andric } // anonymous namespace 12175ffd83dbSDimitry Andric 12185ffd83dbSDimitry Andric 12198bcb0991SDimitry Andric RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS) 12208bcb0991SDimitry Andric : ExternalFS(std::move(FS)) { 12218bcb0991SDimitry Andric if (ExternalFS) 12228bcb0991SDimitry Andric if (auto ExternalWorkingDirectory = 12238bcb0991SDimitry Andric ExternalFS->getCurrentWorkingDirectory()) { 12248bcb0991SDimitry Andric WorkingDirectory = *ExternalWorkingDirectory; 12258bcb0991SDimitry Andric } 12268bcb0991SDimitry Andric } 12278bcb0991SDimitry Andric 1228fe6060f1SDimitry Andric /// Directory iterator implementation for \c RedirectingFileSystem's 1229fe6060f1SDimitry Andric /// directory entries. 1230fe6060f1SDimitry Andric class llvm::vfs::RedirectingFSDirIterImpl 1231*0b57cec5SDimitry Andric : public llvm::vfs::detail::DirIterImpl { 1232*0b57cec5SDimitry Andric std::string Dir; 1233fe6060f1SDimitry Andric RedirectingFileSystem::DirectoryEntry::iterator Current, End; 1234*0b57cec5SDimitry Andric 1235fe6060f1SDimitry Andric std::error_code incrementImpl(bool IsFirstTime) { 1236fe6060f1SDimitry Andric assert((IsFirstTime || Current != End) && "cannot iterate past end"); 1237fe6060f1SDimitry Andric if (!IsFirstTime) 1238fe6060f1SDimitry Andric ++Current; 1239fe6060f1SDimitry Andric if (Current != End) { 1240fe6060f1SDimitry Andric SmallString<128> PathStr(Dir); 1241fe6060f1SDimitry Andric llvm::sys::path::append(PathStr, (*Current)->getName()); 1242fe6060f1SDimitry Andric sys::fs::file_type Type = sys::fs::file_type::type_unknown; 1243fe6060f1SDimitry Andric switch ((*Current)->getKind()) { 1244fe6060f1SDimitry Andric case RedirectingFileSystem::EK_Directory: 1245bdd1243dSDimitry Andric [[fallthrough]]; 1246fe6060f1SDimitry Andric case RedirectingFileSystem::EK_DirectoryRemap: 1247fe6060f1SDimitry Andric Type = sys::fs::file_type::directory_file; 1248fe6060f1SDimitry Andric break; 1249fe6060f1SDimitry Andric case RedirectingFileSystem::EK_File: 1250fe6060f1SDimitry Andric Type = sys::fs::file_type::regular_file; 1251fe6060f1SDimitry Andric break; 1252fe6060f1SDimitry Andric } 1253fe6060f1SDimitry Andric CurrentEntry = directory_entry(std::string(PathStr.str()), Type); 1254fe6060f1SDimitry Andric } else { 1255fe6060f1SDimitry Andric CurrentEntry = directory_entry(); 1256fe6060f1SDimitry Andric } 1257fe6060f1SDimitry Andric return {}; 1258fe6060f1SDimitry Andric }; 1259*0b57cec5SDimitry Andric 1260*0b57cec5SDimitry Andric public: 1261fe6060f1SDimitry Andric RedirectingFSDirIterImpl( 1262fe6060f1SDimitry Andric const Twine &Path, RedirectingFileSystem::DirectoryEntry::iterator Begin, 1263fe6060f1SDimitry Andric RedirectingFileSystem::DirectoryEntry::iterator End, std::error_code &EC) 1264fe6060f1SDimitry Andric : Dir(Path.str()), Current(Begin), End(End) { 1265fe6060f1SDimitry Andric EC = incrementImpl(/*IsFirstTime=*/true); 1266fe6060f1SDimitry Andric } 1267*0b57cec5SDimitry Andric 1268fe6060f1SDimitry Andric std::error_code increment() override { 1269fe6060f1SDimitry Andric return incrementImpl(/*IsFirstTime=*/false); 1270fe6060f1SDimitry Andric } 1271fe6060f1SDimitry Andric }; 1272fe6060f1SDimitry Andric 1273349cc55cSDimitry Andric namespace { 1274fe6060f1SDimitry Andric /// Directory iterator implementation for \c RedirectingFileSystem's 1275fe6060f1SDimitry Andric /// directory remap entries that maps the paths reported by the external 1276fe6060f1SDimitry Andric /// file system's directory iterator back to the virtual directory's path. 1277fe6060f1SDimitry Andric class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl { 1278fe6060f1SDimitry Andric std::string Dir; 1279fe6060f1SDimitry Andric llvm::sys::path::Style DirStyle; 1280fe6060f1SDimitry Andric llvm::vfs::directory_iterator ExternalIter; 1281fe6060f1SDimitry Andric 1282fe6060f1SDimitry Andric public: 1283fe6060f1SDimitry Andric RedirectingFSDirRemapIterImpl(std::string DirPath, 1284fe6060f1SDimitry Andric llvm::vfs::directory_iterator ExtIter) 1285fe6060f1SDimitry Andric : Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)), 1286fe6060f1SDimitry Andric ExternalIter(ExtIter) { 1287fe6060f1SDimitry Andric if (ExternalIter != llvm::vfs::directory_iterator()) 1288fe6060f1SDimitry Andric setCurrentEntry(); 1289fe6060f1SDimitry Andric } 1290fe6060f1SDimitry Andric 1291fe6060f1SDimitry Andric void setCurrentEntry() { 1292fe6060f1SDimitry Andric StringRef ExternalPath = ExternalIter->path(); 1293fe6060f1SDimitry Andric llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath); 1294fe6060f1SDimitry Andric StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle); 1295fe6060f1SDimitry Andric 1296fe6060f1SDimitry Andric SmallString<128> NewPath(Dir); 1297fe6060f1SDimitry Andric llvm::sys::path::append(NewPath, DirStyle, File); 1298fe6060f1SDimitry Andric 1299fe6060f1SDimitry Andric CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type()); 1300fe6060f1SDimitry Andric } 1301fe6060f1SDimitry Andric 1302fe6060f1SDimitry Andric std::error_code increment() override { 1303fe6060f1SDimitry Andric std::error_code EC; 1304fe6060f1SDimitry Andric ExternalIter.increment(EC); 1305fe6060f1SDimitry Andric if (!EC && ExternalIter != llvm::vfs::directory_iterator()) 1306fe6060f1SDimitry Andric setCurrentEntry(); 1307fe6060f1SDimitry Andric else 1308fe6060f1SDimitry Andric CurrentEntry = directory_entry(); 1309fe6060f1SDimitry Andric return EC; 1310fe6060f1SDimitry Andric } 1311*0b57cec5SDimitry Andric }; 1312349cc55cSDimitry Andric } // namespace 1313*0b57cec5SDimitry Andric 1314*0b57cec5SDimitry Andric llvm::ErrorOr<std::string> 1315*0b57cec5SDimitry Andric RedirectingFileSystem::getCurrentWorkingDirectory() const { 13168bcb0991SDimitry Andric return WorkingDirectory; 1317*0b57cec5SDimitry Andric } 1318*0b57cec5SDimitry Andric 1319*0b57cec5SDimitry Andric std::error_code 1320*0b57cec5SDimitry Andric RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) { 13218bcb0991SDimitry Andric // Don't change the working directory if the path doesn't exist. 13228bcb0991SDimitry Andric if (!exists(Path)) 13238bcb0991SDimitry Andric return errc::no_such_file_or_directory; 13248bcb0991SDimitry Andric 13258bcb0991SDimitry Andric SmallString<128> AbsolutePath; 13268bcb0991SDimitry Andric Path.toVector(AbsolutePath); 13278bcb0991SDimitry Andric if (std::error_code EC = makeAbsolute(AbsolutePath)) 13288bcb0991SDimitry Andric return EC; 13295ffd83dbSDimitry Andric WorkingDirectory = std::string(AbsolutePath.str()); 13308bcb0991SDimitry Andric return {}; 1331*0b57cec5SDimitry Andric } 1332*0b57cec5SDimitry Andric 1333e8d8bef9SDimitry Andric std::error_code RedirectingFileSystem::isLocal(const Twine &Path_, 1334*0b57cec5SDimitry Andric bool &Result) { 1335e8d8bef9SDimitry Andric SmallString<256> Path; 1336e8d8bef9SDimitry Andric Path_.toVector(Path); 1337e8d8bef9SDimitry Andric 1338e8d8bef9SDimitry Andric if (std::error_code EC = makeCanonical(Path)) 1339e8d8bef9SDimitry Andric return {}; 1340e8d8bef9SDimitry Andric 1341*0b57cec5SDimitry Andric return ExternalFS->isLocal(Path, Result); 1342*0b57cec5SDimitry Andric } 1343*0b57cec5SDimitry Andric 1344480093f4SDimitry Andric std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const { 1345349cc55cSDimitry Andric // is_absolute(..., Style::windows_*) accepts paths with both slash types. 1346480093f4SDimitry Andric if (llvm::sys::path::is_absolute(Path, llvm::sys::path::Style::posix) || 1347349cc55cSDimitry Andric llvm::sys::path::is_absolute(Path, 1348349cc55cSDimitry Andric llvm::sys::path::Style::windows_backslash)) 1349bdd1243dSDimitry Andric // This covers windows absolute path with forward slash as well, as the 1350bdd1243dSDimitry Andric // forward slashes are treated as path seperation in llvm::path 1351bdd1243dSDimitry Andric // regardless of what path::Style is used. 1352480093f4SDimitry Andric return {}; 1353480093f4SDimitry Andric 1354480093f4SDimitry Andric auto WorkingDir = getCurrentWorkingDirectory(); 1355480093f4SDimitry Andric if (!WorkingDir) 1356480093f4SDimitry Andric return WorkingDir.getError(); 1357480093f4SDimitry Andric 1358bdd1243dSDimitry Andric return makeAbsolute(WorkingDir.get(), Path); 1359bdd1243dSDimitry Andric } 1360bdd1243dSDimitry Andric 1361bdd1243dSDimitry Andric std::error_code 1362bdd1243dSDimitry Andric RedirectingFileSystem::makeAbsolute(StringRef WorkingDir, 1363bdd1243dSDimitry Andric SmallVectorImpl<char> &Path) const { 13645ffd83dbSDimitry Andric // We can't use sys::fs::make_absolute because that assumes the path style 13655ffd83dbSDimitry Andric // is native and there is no way to override that. Since we know WorkingDir 13665ffd83dbSDimitry Andric // is absolute, we can use it to determine which style we actually have and 13675ffd83dbSDimitry Andric // append Path ourselves. 1368bdd1243dSDimitry Andric if (!WorkingDir.empty() && 1369bdd1243dSDimitry Andric !sys::path::is_absolute(WorkingDir, sys::path::Style::posix) && 1370bdd1243dSDimitry Andric !sys::path::is_absolute(WorkingDir, 1371bdd1243dSDimitry Andric sys::path::Style::windows_backslash)) { 1372bdd1243dSDimitry Andric return std::error_code(); 1373bdd1243dSDimitry Andric } 1374349cc55cSDimitry Andric sys::path::Style style = sys::path::Style::windows_backslash; 1375bdd1243dSDimitry Andric if (sys::path::is_absolute(WorkingDir, sys::path::Style::posix)) { 13765ffd83dbSDimitry Andric style = sys::path::Style::posix; 1377349cc55cSDimitry Andric } else { 1378349cc55cSDimitry Andric // Distinguish between windows_backslash and windows_slash; getExistingStyle 1379349cc55cSDimitry Andric // returns posix for a path with windows_slash. 1380bdd1243dSDimitry Andric if (getExistingStyle(WorkingDir) != sys::path::Style::windows_backslash) 1381349cc55cSDimitry Andric style = sys::path::Style::windows_slash; 13825ffd83dbSDimitry Andric } 13835ffd83dbSDimitry Andric 1384bdd1243dSDimitry Andric std::string Result = std::string(WorkingDir); 13855ffd83dbSDimitry Andric StringRef Dir(Result); 13865ffd83dbSDimitry Andric if (!Dir.endswith(sys::path::get_separator(style))) { 13875ffd83dbSDimitry Andric Result += sys::path::get_separator(style); 13885ffd83dbSDimitry Andric } 1389bdd1243dSDimitry Andric // backslashes '\' are legit path charactors under POSIX. Windows APIs 1390bdd1243dSDimitry Andric // like CreateFile accepts forward slashes '/' as path 1391bdd1243dSDimitry Andric // separator (even when mixed with backslashes). Therefore, 1392bdd1243dSDimitry Andric // `Path` should be directly appended to `WorkingDir` without converting 1393bdd1243dSDimitry Andric // path separator. 13945ffd83dbSDimitry Andric Result.append(Path.data(), Path.size()); 13955ffd83dbSDimitry Andric Path.assign(Result.begin(), Result.end()); 13965ffd83dbSDimitry Andric 1397480093f4SDimitry Andric return {}; 1398480093f4SDimitry Andric } 1399480093f4SDimitry Andric 1400*0b57cec5SDimitry Andric directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir, 1401*0b57cec5SDimitry Andric std::error_code &EC) { 1402e8d8bef9SDimitry Andric SmallString<256> Path; 1403e8d8bef9SDimitry Andric Dir.toVector(Path); 1404e8d8bef9SDimitry Andric 1405e8d8bef9SDimitry Andric EC = makeCanonical(Path); 1406e8d8bef9SDimitry Andric if (EC) 1407e8d8bef9SDimitry Andric return {}; 1408e8d8bef9SDimitry Andric 1409fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path); 1410fe6060f1SDimitry Andric if (!Result) { 141181ad6265SDimitry Andric if (Redirection != RedirectKind::RedirectOnly && 141281ad6265SDimitry Andric isFileNotFound(Result.getError())) 1413e8d8bef9SDimitry Andric return ExternalFS->dir_begin(Path, EC); 141481ad6265SDimitry Andric 141581ad6265SDimitry Andric EC = Result.getError(); 1416*0b57cec5SDimitry Andric return {}; 1417*0b57cec5SDimitry Andric } 1418fe6060f1SDimitry Andric 1419fe6060f1SDimitry Andric // Use status to make sure the path exists and refers to a directory. 1420349cc55cSDimitry Andric ErrorOr<Status> S = status(Path, Dir, *Result); 1421*0b57cec5SDimitry Andric if (!S) { 142281ad6265SDimitry Andric if (Redirection != RedirectKind::RedirectOnly && 142381ad6265SDimitry Andric isFileNotFound(S.getError(), Result->E)) 1424fe6060f1SDimitry Andric return ExternalFS->dir_begin(Dir, EC); 142581ad6265SDimitry Andric 1426*0b57cec5SDimitry Andric EC = S.getError(); 1427*0b57cec5SDimitry Andric return {}; 1428*0b57cec5SDimitry Andric } 142981ad6265SDimitry Andric 1430*0b57cec5SDimitry Andric if (!S->isDirectory()) { 143181ad6265SDimitry Andric EC = errc::not_a_directory; 1432*0b57cec5SDimitry Andric return {}; 1433*0b57cec5SDimitry Andric } 1434*0b57cec5SDimitry Andric 1435fe6060f1SDimitry Andric // Create the appropriate directory iterator based on whether we found a 1436fe6060f1SDimitry Andric // DirectoryRemapEntry or DirectoryEntry. 143781ad6265SDimitry Andric directory_iterator RedirectIter; 143881ad6265SDimitry Andric std::error_code RedirectEC; 1439fe6060f1SDimitry Andric if (auto ExtRedirect = Result->getExternalRedirect()) { 1440fe6060f1SDimitry Andric auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E); 144181ad6265SDimitry Andric RedirectIter = ExternalFS->dir_begin(*ExtRedirect, RedirectEC); 1442fe6060f1SDimitry Andric 1443fe6060f1SDimitry Andric if (!RE->useExternalName(UseExternalNames)) { 1444fe6060f1SDimitry Andric // Update the paths in the results to use the virtual directory's path. 144581ad6265SDimitry Andric RedirectIter = 1446fe6060f1SDimitry Andric directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>( 144781ad6265SDimitry Andric std::string(Path), RedirectIter)); 1448fe6060f1SDimitry Andric } 1449fe6060f1SDimitry Andric } else { 1450fe6060f1SDimitry Andric auto DE = cast<DirectoryEntry>(Result->E); 145181ad6265SDimitry Andric RedirectIter = 145281ad6265SDimitry Andric directory_iterator(std::make_shared<RedirectingFSDirIterImpl>( 145381ad6265SDimitry Andric Path, DE->contents_begin(), DE->contents_end(), RedirectEC)); 1454fe6060f1SDimitry Andric } 1455fe6060f1SDimitry Andric 145681ad6265SDimitry Andric if (RedirectEC) { 145781ad6265SDimitry Andric if (RedirectEC != errc::no_such_file_or_directory) { 145881ad6265SDimitry Andric EC = RedirectEC; 145981ad6265SDimitry Andric return {}; 146081ad6265SDimitry Andric } 146181ad6265SDimitry Andric RedirectIter = {}; 146281ad6265SDimitry Andric } 146381ad6265SDimitry Andric 146481ad6265SDimitry Andric if (Redirection == RedirectKind::RedirectOnly) { 146581ad6265SDimitry Andric EC = RedirectEC; 146681ad6265SDimitry Andric return RedirectIter; 146781ad6265SDimitry Andric } 146881ad6265SDimitry Andric 146981ad6265SDimitry Andric std::error_code ExternalEC; 147081ad6265SDimitry Andric directory_iterator ExternalIter = ExternalFS->dir_begin(Path, ExternalEC); 147181ad6265SDimitry Andric if (ExternalEC) { 147281ad6265SDimitry Andric if (ExternalEC != errc::no_such_file_or_directory) { 147381ad6265SDimitry Andric EC = ExternalEC; 147481ad6265SDimitry Andric return {}; 147581ad6265SDimitry Andric } 147681ad6265SDimitry Andric ExternalIter = {}; 147781ad6265SDimitry Andric } 147881ad6265SDimitry Andric 147981ad6265SDimitry Andric SmallVector<directory_iterator, 2> Iters; 148081ad6265SDimitry Andric switch (Redirection) { 148181ad6265SDimitry Andric case RedirectKind::Fallthrough: 148281ad6265SDimitry Andric Iters.push_back(ExternalIter); 148381ad6265SDimitry Andric Iters.push_back(RedirectIter); 148481ad6265SDimitry Andric break; 148581ad6265SDimitry Andric case RedirectKind::Fallback: 148681ad6265SDimitry Andric Iters.push_back(RedirectIter); 148781ad6265SDimitry Andric Iters.push_back(ExternalIter); 148881ad6265SDimitry Andric break; 148981ad6265SDimitry Andric default: 149081ad6265SDimitry Andric llvm_unreachable("unhandled RedirectKind"); 149181ad6265SDimitry Andric } 149281ad6265SDimitry Andric 149381ad6265SDimitry Andric directory_iterator Combined{ 149481ad6265SDimitry Andric std::make_shared<CombiningDirIterImpl>(Iters, EC)}; 149581ad6265SDimitry Andric if (EC) 149681ad6265SDimitry Andric return {}; 149781ad6265SDimitry Andric return Combined; 1498*0b57cec5SDimitry Andric } 1499*0b57cec5SDimitry Andric 1500bdd1243dSDimitry Andric void RedirectingFileSystem::setOverlayFileDir(StringRef Dir) { 1501bdd1243dSDimitry Andric OverlayFileDir = Dir.str(); 1502*0b57cec5SDimitry Andric } 1503*0b57cec5SDimitry Andric 1504bdd1243dSDimitry Andric StringRef RedirectingFileSystem::getOverlayFileDir() const { 1505bdd1243dSDimitry Andric return OverlayFileDir; 1506*0b57cec5SDimitry Andric } 1507*0b57cec5SDimitry Andric 1508e8d8bef9SDimitry Andric void RedirectingFileSystem::setFallthrough(bool Fallthrough) { 150981ad6265SDimitry Andric if (Fallthrough) { 151081ad6265SDimitry Andric Redirection = RedirectingFileSystem::RedirectKind::Fallthrough; 151181ad6265SDimitry Andric } else { 151281ad6265SDimitry Andric Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly; 151381ad6265SDimitry Andric } 151481ad6265SDimitry Andric } 151581ad6265SDimitry Andric 151681ad6265SDimitry Andric void RedirectingFileSystem::setRedirection( 151781ad6265SDimitry Andric RedirectingFileSystem::RedirectKind Kind) { 151881ad6265SDimitry Andric Redirection = Kind; 1519e8d8bef9SDimitry Andric } 1520e8d8bef9SDimitry Andric 1521e8d8bef9SDimitry Andric std::vector<StringRef> RedirectingFileSystem::getRoots() const { 1522e8d8bef9SDimitry Andric std::vector<StringRef> R; 1523bdd1243dSDimitry Andric R.reserve(Roots.size()); 1524e8d8bef9SDimitry Andric for (const auto &Root : Roots) 1525e8d8bef9SDimitry Andric R.push_back(Root->getName()); 1526e8d8bef9SDimitry Andric return R; 1527e8d8bef9SDimitry Andric } 1528e8d8bef9SDimitry Andric 152981ad6265SDimitry Andric void RedirectingFileSystem::printImpl(raw_ostream &OS, PrintType Type, 153081ad6265SDimitry Andric unsigned IndentLevel) const { 153181ad6265SDimitry Andric printIndent(OS, IndentLevel); 153281ad6265SDimitry Andric OS << "RedirectingFileSystem (UseExternalNames: " 153381ad6265SDimitry Andric << (UseExternalNames ? "true" : "false") << ")\n"; 153481ad6265SDimitry Andric if (Type == PrintType::Summary) 153581ad6265SDimitry Andric return; 153681ad6265SDimitry Andric 1537*0b57cec5SDimitry Andric for (const auto &Root : Roots) 153881ad6265SDimitry Andric printEntry(OS, Root.get(), IndentLevel); 153981ad6265SDimitry Andric 154081ad6265SDimitry Andric printIndent(OS, IndentLevel); 154181ad6265SDimitry Andric OS << "ExternalFS:\n"; 154281ad6265SDimitry Andric ExternalFS->print(OS, Type == PrintType::Contents ? PrintType::Summary : Type, 154381ad6265SDimitry Andric IndentLevel + 1); 1544*0b57cec5SDimitry Andric } 1545*0b57cec5SDimitry Andric 154681ad6265SDimitry Andric void RedirectingFileSystem::printEntry(raw_ostream &OS, 15478bcb0991SDimitry Andric RedirectingFileSystem::Entry *E, 154881ad6265SDimitry Andric unsigned IndentLevel) const { 154981ad6265SDimitry Andric printIndent(OS, IndentLevel); 155081ad6265SDimitry Andric OS << "'" << E->getName() << "'"; 1551*0b57cec5SDimitry Andric 155281ad6265SDimitry Andric switch (E->getKind()) { 155381ad6265SDimitry Andric case EK_Directory: { 155481ad6265SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(E); 1555*0b57cec5SDimitry Andric 155681ad6265SDimitry Andric OS << "\n"; 1557*0b57cec5SDimitry Andric for (std::unique_ptr<Entry> &SubEntry : 1558*0b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) 155981ad6265SDimitry Andric printEntry(OS, SubEntry.get(), IndentLevel + 1); 156081ad6265SDimitry Andric break; 156181ad6265SDimitry Andric } 156281ad6265SDimitry Andric case EK_DirectoryRemap: 156381ad6265SDimitry Andric case EK_File: { 156481ad6265SDimitry Andric auto *RE = cast<RedirectingFileSystem::RemapEntry>(E); 156581ad6265SDimitry Andric OS << " -> '" << RE->getExternalContentsPath() << "'"; 156681ad6265SDimitry Andric switch (RE->getUseName()) { 156781ad6265SDimitry Andric case NK_NotSet: 156881ad6265SDimitry Andric break; 156981ad6265SDimitry Andric case NK_External: 157081ad6265SDimitry Andric OS << " (UseExternalName: true)"; 157181ad6265SDimitry Andric break; 157281ad6265SDimitry Andric case NK_Virtual: 157381ad6265SDimitry Andric OS << " (UseExternalName: false)"; 157481ad6265SDimitry Andric break; 157581ad6265SDimitry Andric } 157681ad6265SDimitry Andric OS << "\n"; 157781ad6265SDimitry Andric break; 1578*0b57cec5SDimitry Andric } 1579*0b57cec5SDimitry Andric } 158081ad6265SDimitry Andric } 1581*0b57cec5SDimitry Andric 1582*0b57cec5SDimitry Andric /// A helper class to hold the common YAML parsing state. 1583*0b57cec5SDimitry Andric class llvm::vfs::RedirectingFileSystemParser { 1584*0b57cec5SDimitry Andric yaml::Stream &Stream; 1585*0b57cec5SDimitry Andric 1586*0b57cec5SDimitry Andric void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); } 1587*0b57cec5SDimitry Andric 1588*0b57cec5SDimitry Andric // false on error 1589*0b57cec5SDimitry Andric bool parseScalarString(yaml::Node *N, StringRef &Result, 1590*0b57cec5SDimitry Andric SmallVectorImpl<char> &Storage) { 1591*0b57cec5SDimitry Andric const auto *S = dyn_cast<yaml::ScalarNode>(N); 1592*0b57cec5SDimitry Andric 1593*0b57cec5SDimitry Andric if (!S) { 1594*0b57cec5SDimitry Andric error(N, "expected string"); 1595*0b57cec5SDimitry Andric return false; 1596*0b57cec5SDimitry Andric } 1597*0b57cec5SDimitry Andric Result = S->getValue(Storage); 1598*0b57cec5SDimitry Andric return true; 1599*0b57cec5SDimitry Andric } 1600*0b57cec5SDimitry Andric 1601*0b57cec5SDimitry Andric // false on error 1602*0b57cec5SDimitry Andric bool parseScalarBool(yaml::Node *N, bool &Result) { 1603*0b57cec5SDimitry Andric SmallString<5> Storage; 1604*0b57cec5SDimitry Andric StringRef Value; 1605*0b57cec5SDimitry Andric if (!parseScalarString(N, Value, Storage)) 1606*0b57cec5SDimitry Andric return false; 1607*0b57cec5SDimitry Andric 1608fe6060f1SDimitry Andric if (Value.equals_insensitive("true") || Value.equals_insensitive("on") || 1609fe6060f1SDimitry Andric Value.equals_insensitive("yes") || Value == "1") { 1610*0b57cec5SDimitry Andric Result = true; 1611*0b57cec5SDimitry Andric return true; 1612fe6060f1SDimitry Andric } else if (Value.equals_insensitive("false") || 1613fe6060f1SDimitry Andric Value.equals_insensitive("off") || 1614fe6060f1SDimitry Andric Value.equals_insensitive("no") || Value == "0") { 1615*0b57cec5SDimitry Andric Result = false; 1616*0b57cec5SDimitry Andric return true; 1617*0b57cec5SDimitry Andric } 1618*0b57cec5SDimitry Andric 1619*0b57cec5SDimitry Andric error(N, "expected boolean value"); 1620*0b57cec5SDimitry Andric return false; 1621*0b57cec5SDimitry Andric } 1622*0b57cec5SDimitry Andric 1623bdd1243dSDimitry Andric std::optional<RedirectingFileSystem::RedirectKind> 162481ad6265SDimitry Andric parseRedirectKind(yaml::Node *N) { 162581ad6265SDimitry Andric SmallString<12> Storage; 162681ad6265SDimitry Andric StringRef Value; 162781ad6265SDimitry Andric if (!parseScalarString(N, Value, Storage)) 1628bdd1243dSDimitry Andric return std::nullopt; 162981ad6265SDimitry Andric 163081ad6265SDimitry Andric if (Value.equals_insensitive("fallthrough")) { 163181ad6265SDimitry Andric return RedirectingFileSystem::RedirectKind::Fallthrough; 163281ad6265SDimitry Andric } else if (Value.equals_insensitive("fallback")) { 163381ad6265SDimitry Andric return RedirectingFileSystem::RedirectKind::Fallback; 163481ad6265SDimitry Andric } else if (Value.equals_insensitive("redirect-only")) { 163581ad6265SDimitry Andric return RedirectingFileSystem::RedirectKind::RedirectOnly; 163681ad6265SDimitry Andric } 1637bdd1243dSDimitry Andric return std::nullopt; 1638bdd1243dSDimitry Andric } 1639bdd1243dSDimitry Andric 1640bdd1243dSDimitry Andric std::optional<RedirectingFileSystem::RootRelativeKind> 1641bdd1243dSDimitry Andric parseRootRelativeKind(yaml::Node *N) { 1642bdd1243dSDimitry Andric SmallString<12> Storage; 1643bdd1243dSDimitry Andric StringRef Value; 1644bdd1243dSDimitry Andric if (!parseScalarString(N, Value, Storage)) 1645bdd1243dSDimitry Andric return std::nullopt; 1646bdd1243dSDimitry Andric if (Value.equals_insensitive("cwd")) { 1647bdd1243dSDimitry Andric return RedirectingFileSystem::RootRelativeKind::CWD; 1648bdd1243dSDimitry Andric } else if (Value.equals_insensitive("overlay-dir")) { 1649bdd1243dSDimitry Andric return RedirectingFileSystem::RootRelativeKind::OverlayDir; 1650bdd1243dSDimitry Andric } 1651bdd1243dSDimitry Andric return std::nullopt; 165281ad6265SDimitry Andric } 165381ad6265SDimitry Andric 1654*0b57cec5SDimitry Andric struct KeyStatus { 1655*0b57cec5SDimitry Andric bool Required; 1656*0b57cec5SDimitry Andric bool Seen = false; 1657*0b57cec5SDimitry Andric 1658*0b57cec5SDimitry Andric KeyStatus(bool Required = false) : Required(Required) {} 1659*0b57cec5SDimitry Andric }; 1660*0b57cec5SDimitry Andric 1661*0b57cec5SDimitry Andric using KeyStatusPair = std::pair<StringRef, KeyStatus>; 1662*0b57cec5SDimitry Andric 1663*0b57cec5SDimitry Andric // false on error 1664*0b57cec5SDimitry Andric bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key, 1665*0b57cec5SDimitry Andric DenseMap<StringRef, KeyStatus> &Keys) { 1666*0b57cec5SDimitry Andric if (!Keys.count(Key)) { 1667*0b57cec5SDimitry Andric error(KeyNode, "unknown key"); 1668*0b57cec5SDimitry Andric return false; 1669*0b57cec5SDimitry Andric } 1670*0b57cec5SDimitry Andric KeyStatus &S = Keys[Key]; 1671*0b57cec5SDimitry Andric if (S.Seen) { 1672*0b57cec5SDimitry Andric error(KeyNode, Twine("duplicate key '") + Key + "'"); 1673*0b57cec5SDimitry Andric return false; 1674*0b57cec5SDimitry Andric } 1675*0b57cec5SDimitry Andric S.Seen = true; 1676*0b57cec5SDimitry Andric return true; 1677*0b57cec5SDimitry Andric } 1678*0b57cec5SDimitry Andric 1679*0b57cec5SDimitry Andric // false on error 1680*0b57cec5SDimitry Andric bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) { 1681*0b57cec5SDimitry Andric for (const auto &I : Keys) { 1682*0b57cec5SDimitry Andric if (I.second.Required && !I.second.Seen) { 1683*0b57cec5SDimitry Andric error(Obj, Twine("missing key '") + I.first + "'"); 1684*0b57cec5SDimitry Andric return false; 1685*0b57cec5SDimitry Andric } 1686*0b57cec5SDimitry Andric } 1687*0b57cec5SDimitry Andric return true; 1688*0b57cec5SDimitry Andric } 1689*0b57cec5SDimitry Andric 1690e8d8bef9SDimitry Andric public: 1691e8d8bef9SDimitry Andric static RedirectingFileSystem::Entry * 1692*0b57cec5SDimitry Andric lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name, 1693*0b57cec5SDimitry Andric RedirectingFileSystem::Entry *ParentEntry = nullptr) { 1694*0b57cec5SDimitry Andric if (!ParentEntry) { // Look for a existent root 1695*0b57cec5SDimitry Andric for (const auto &Root : FS->Roots) { 1696*0b57cec5SDimitry Andric if (Name.equals(Root->getName())) { 1697*0b57cec5SDimitry Andric ParentEntry = Root.get(); 1698*0b57cec5SDimitry Andric return ParentEntry; 1699*0b57cec5SDimitry Andric } 1700*0b57cec5SDimitry Andric } 1701*0b57cec5SDimitry Andric } else { // Advance to the next component 1702fe6060f1SDimitry Andric auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry); 1703*0b57cec5SDimitry Andric for (std::unique_ptr<RedirectingFileSystem::Entry> &Content : 1704*0b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) { 1705*0b57cec5SDimitry Andric auto *DirContent = 1706fe6060f1SDimitry Andric dyn_cast<RedirectingFileSystem::DirectoryEntry>(Content.get()); 1707*0b57cec5SDimitry Andric if (DirContent && Name.equals(Content->getName())) 1708*0b57cec5SDimitry Andric return DirContent; 1709*0b57cec5SDimitry Andric } 1710*0b57cec5SDimitry Andric } 1711*0b57cec5SDimitry Andric 1712*0b57cec5SDimitry Andric // ... or create a new one 1713*0b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem::Entry> E = 1714fe6060f1SDimitry Andric std::make_unique<RedirectingFileSystem::DirectoryEntry>( 1715*0b57cec5SDimitry Andric Name, Status("", getNextVirtualUniqueID(), 1716*0b57cec5SDimitry Andric std::chrono::system_clock::now(), 0, 0, 0, 1717*0b57cec5SDimitry Andric file_type::directory_file, sys::fs::all_all)); 1718*0b57cec5SDimitry Andric 1719*0b57cec5SDimitry Andric if (!ParentEntry) { // Add a new root to the overlay 1720*0b57cec5SDimitry Andric FS->Roots.push_back(std::move(E)); 1721*0b57cec5SDimitry Andric ParentEntry = FS->Roots.back().get(); 1722*0b57cec5SDimitry Andric return ParentEntry; 1723*0b57cec5SDimitry Andric } 1724*0b57cec5SDimitry Andric 1725fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry); 1726*0b57cec5SDimitry Andric DE->addContent(std::move(E)); 1727*0b57cec5SDimitry Andric return DE->getLastContent(); 1728*0b57cec5SDimitry Andric } 1729*0b57cec5SDimitry Andric 1730e8d8bef9SDimitry Andric private: 1731*0b57cec5SDimitry Andric void uniqueOverlayTree(RedirectingFileSystem *FS, 1732*0b57cec5SDimitry Andric RedirectingFileSystem::Entry *SrcE, 1733*0b57cec5SDimitry Andric RedirectingFileSystem::Entry *NewParentE = nullptr) { 1734*0b57cec5SDimitry Andric StringRef Name = SrcE->getName(); 1735*0b57cec5SDimitry Andric switch (SrcE->getKind()) { 1736*0b57cec5SDimitry Andric case RedirectingFileSystem::EK_Directory: { 1737fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(SrcE); 1738*0b57cec5SDimitry Andric // Empty directories could be present in the YAML as a way to 1739*0b57cec5SDimitry Andric // describe a file for a current directory after some of its subdir 1740*0b57cec5SDimitry Andric // is parsed. This only leads to redundant walks, ignore it. 1741*0b57cec5SDimitry Andric if (!Name.empty()) 1742*0b57cec5SDimitry Andric NewParentE = lookupOrCreateEntry(FS, Name, NewParentE); 1743*0b57cec5SDimitry Andric for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry : 1744*0b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) 1745*0b57cec5SDimitry Andric uniqueOverlayTree(FS, SubEntry.get(), NewParentE); 1746*0b57cec5SDimitry Andric break; 1747*0b57cec5SDimitry Andric } 1748fe6060f1SDimitry Andric case RedirectingFileSystem::EK_DirectoryRemap: { 1749fe6060f1SDimitry Andric assert(NewParentE && "Parent entry must exist"); 1750fe6060f1SDimitry Andric auto *DR = cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE); 1751fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE); 1752fe6060f1SDimitry Andric DE->addContent( 1753fe6060f1SDimitry Andric std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>( 1754fe6060f1SDimitry Andric Name, DR->getExternalContentsPath(), DR->getUseName())); 1755fe6060f1SDimitry Andric break; 1756fe6060f1SDimitry Andric } 1757*0b57cec5SDimitry Andric case RedirectingFileSystem::EK_File: { 1758*0b57cec5SDimitry Andric assert(NewParentE && "Parent entry must exist"); 1759fe6060f1SDimitry Andric auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE); 1760fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE); 1761fe6060f1SDimitry Andric DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>( 1762*0b57cec5SDimitry Andric Name, FE->getExternalContentsPath(), FE->getUseName())); 1763*0b57cec5SDimitry Andric break; 1764*0b57cec5SDimitry Andric } 1765*0b57cec5SDimitry Andric } 1766*0b57cec5SDimitry Andric } 1767*0b57cec5SDimitry Andric 1768*0b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem::Entry> 1769*0b57cec5SDimitry Andric parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) { 1770*0b57cec5SDimitry Andric auto *M = dyn_cast<yaml::MappingNode>(N); 1771*0b57cec5SDimitry Andric if (!M) { 1772*0b57cec5SDimitry Andric error(N, "expected mapping node for file or directory entry"); 1773*0b57cec5SDimitry Andric return nullptr; 1774*0b57cec5SDimitry Andric } 1775*0b57cec5SDimitry Andric 1776*0b57cec5SDimitry Andric KeyStatusPair Fields[] = { 1777*0b57cec5SDimitry Andric KeyStatusPair("name", true), 1778*0b57cec5SDimitry Andric KeyStatusPair("type", true), 1779*0b57cec5SDimitry Andric KeyStatusPair("contents", false), 1780*0b57cec5SDimitry Andric KeyStatusPair("external-contents", false), 1781*0b57cec5SDimitry Andric KeyStatusPair("use-external-name", false), 1782*0b57cec5SDimitry Andric }; 1783*0b57cec5SDimitry Andric 1784*0b57cec5SDimitry Andric DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields)); 1785*0b57cec5SDimitry Andric 1786fe6060f1SDimitry Andric enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet; 1787*0b57cec5SDimitry Andric std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> 1788*0b57cec5SDimitry Andric EntryArrayContents; 17895ffd83dbSDimitry Andric SmallString<256> ExternalContentsPath; 17905ffd83dbSDimitry Andric SmallString<256> Name; 1791*0b57cec5SDimitry Andric yaml::Node *NameValueNode = nullptr; 1792fe6060f1SDimitry Andric auto UseExternalName = RedirectingFileSystem::NK_NotSet; 1793*0b57cec5SDimitry Andric RedirectingFileSystem::EntryKind Kind; 1794*0b57cec5SDimitry Andric 1795*0b57cec5SDimitry Andric for (auto &I : *M) { 1796*0b57cec5SDimitry Andric StringRef Key; 1797*0b57cec5SDimitry Andric // Reuse the buffer for key and value, since we don't look at key after 1798*0b57cec5SDimitry Andric // parsing value. 1799*0b57cec5SDimitry Andric SmallString<256> Buffer; 1800*0b57cec5SDimitry Andric if (!parseScalarString(I.getKey(), Key, Buffer)) 1801*0b57cec5SDimitry Andric return nullptr; 1802*0b57cec5SDimitry Andric 1803*0b57cec5SDimitry Andric if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) 1804*0b57cec5SDimitry Andric return nullptr; 1805*0b57cec5SDimitry Andric 1806*0b57cec5SDimitry Andric StringRef Value; 1807*0b57cec5SDimitry Andric if (Key == "name") { 1808*0b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), Value, Buffer)) 1809*0b57cec5SDimitry Andric return nullptr; 1810*0b57cec5SDimitry Andric 1811*0b57cec5SDimitry Andric NameValueNode = I.getValue(); 1812*0b57cec5SDimitry Andric // Guarantee that old YAML files containing paths with ".." and "." 1813*0b57cec5SDimitry Andric // are properly canonicalized before read into the VFS. 18145ffd83dbSDimitry Andric Name = canonicalize(Value).str(); 1815*0b57cec5SDimitry Andric } else if (Key == "type") { 1816*0b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), Value, Buffer)) 1817*0b57cec5SDimitry Andric return nullptr; 1818*0b57cec5SDimitry Andric if (Value == "file") 1819*0b57cec5SDimitry Andric Kind = RedirectingFileSystem::EK_File; 1820*0b57cec5SDimitry Andric else if (Value == "directory") 1821*0b57cec5SDimitry Andric Kind = RedirectingFileSystem::EK_Directory; 1822fe6060f1SDimitry Andric else if (Value == "directory-remap") 1823fe6060f1SDimitry Andric Kind = RedirectingFileSystem::EK_DirectoryRemap; 1824*0b57cec5SDimitry Andric else { 1825*0b57cec5SDimitry Andric error(I.getValue(), "unknown value for 'type'"); 1826*0b57cec5SDimitry Andric return nullptr; 1827*0b57cec5SDimitry Andric } 1828*0b57cec5SDimitry Andric } else if (Key == "contents") { 1829fe6060f1SDimitry Andric if (ContentsField != CF_NotSet) { 1830*0b57cec5SDimitry Andric error(I.getKey(), 1831*0b57cec5SDimitry Andric "entry already has 'contents' or 'external-contents'"); 1832*0b57cec5SDimitry Andric return nullptr; 1833*0b57cec5SDimitry Andric } 1834fe6060f1SDimitry Andric ContentsField = CF_List; 1835*0b57cec5SDimitry Andric auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue()); 1836*0b57cec5SDimitry Andric if (!Contents) { 1837*0b57cec5SDimitry Andric // FIXME: this is only for directories, what about files? 1838*0b57cec5SDimitry Andric error(I.getValue(), "expected array"); 1839*0b57cec5SDimitry Andric return nullptr; 1840*0b57cec5SDimitry Andric } 1841*0b57cec5SDimitry Andric 1842*0b57cec5SDimitry Andric for (auto &I : *Contents) { 1843*0b57cec5SDimitry Andric if (std::unique_ptr<RedirectingFileSystem::Entry> E = 1844*0b57cec5SDimitry Andric parseEntry(&I, FS, /*IsRootEntry*/ false)) 1845*0b57cec5SDimitry Andric EntryArrayContents.push_back(std::move(E)); 1846*0b57cec5SDimitry Andric else 1847*0b57cec5SDimitry Andric return nullptr; 1848*0b57cec5SDimitry Andric } 1849*0b57cec5SDimitry Andric } else if (Key == "external-contents") { 1850fe6060f1SDimitry Andric if (ContentsField != CF_NotSet) { 1851*0b57cec5SDimitry Andric error(I.getKey(), 1852*0b57cec5SDimitry Andric "entry already has 'contents' or 'external-contents'"); 1853*0b57cec5SDimitry Andric return nullptr; 1854*0b57cec5SDimitry Andric } 1855fe6060f1SDimitry Andric ContentsField = CF_External; 1856*0b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), Value, Buffer)) 1857*0b57cec5SDimitry Andric return nullptr; 1858*0b57cec5SDimitry Andric 1859*0b57cec5SDimitry Andric SmallString<256> FullPath; 1860*0b57cec5SDimitry Andric if (FS->IsRelativeOverlay) { 1861bdd1243dSDimitry Andric FullPath = FS->getOverlayFileDir(); 1862*0b57cec5SDimitry Andric assert(!FullPath.empty() && 1863*0b57cec5SDimitry Andric "External contents prefix directory must exist"); 1864*0b57cec5SDimitry Andric llvm::sys::path::append(FullPath, Value); 1865*0b57cec5SDimitry Andric } else { 1866*0b57cec5SDimitry Andric FullPath = Value; 1867*0b57cec5SDimitry Andric } 1868*0b57cec5SDimitry Andric 1869*0b57cec5SDimitry Andric // Guarantee that old YAML files containing paths with ".." and "." 1870*0b57cec5SDimitry Andric // are properly canonicalized before read into the VFS. 18715ffd83dbSDimitry Andric FullPath = canonicalize(FullPath); 1872*0b57cec5SDimitry Andric ExternalContentsPath = FullPath.str(); 1873*0b57cec5SDimitry Andric } else if (Key == "use-external-name") { 1874*0b57cec5SDimitry Andric bool Val; 1875*0b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), Val)) 1876*0b57cec5SDimitry Andric return nullptr; 1877fe6060f1SDimitry Andric UseExternalName = Val ? RedirectingFileSystem::NK_External 1878fe6060f1SDimitry Andric : RedirectingFileSystem::NK_Virtual; 1879*0b57cec5SDimitry Andric } else { 1880*0b57cec5SDimitry Andric llvm_unreachable("key missing from Keys"); 1881*0b57cec5SDimitry Andric } 1882*0b57cec5SDimitry Andric } 1883*0b57cec5SDimitry Andric 1884*0b57cec5SDimitry Andric if (Stream.failed()) 1885*0b57cec5SDimitry Andric return nullptr; 1886*0b57cec5SDimitry Andric 1887*0b57cec5SDimitry Andric // check for missing keys 1888fe6060f1SDimitry Andric if (ContentsField == CF_NotSet) { 1889*0b57cec5SDimitry Andric error(N, "missing key 'contents' or 'external-contents'"); 1890*0b57cec5SDimitry Andric return nullptr; 1891*0b57cec5SDimitry Andric } 1892*0b57cec5SDimitry Andric if (!checkMissingKeys(N, Keys)) 1893*0b57cec5SDimitry Andric return nullptr; 1894*0b57cec5SDimitry Andric 1895*0b57cec5SDimitry Andric // check invalid configuration 1896*0b57cec5SDimitry Andric if (Kind == RedirectingFileSystem::EK_Directory && 1897fe6060f1SDimitry Andric UseExternalName != RedirectingFileSystem::NK_NotSet) { 1898fe6060f1SDimitry Andric error(N, "'use-external-name' is not supported for 'directory' entries"); 1899fe6060f1SDimitry Andric return nullptr; 1900fe6060f1SDimitry Andric } 1901fe6060f1SDimitry Andric 1902fe6060f1SDimitry Andric if (Kind == RedirectingFileSystem::EK_DirectoryRemap && 1903fe6060f1SDimitry Andric ContentsField == CF_List) { 1904fe6060f1SDimitry Andric error(N, "'contents' is not supported for 'directory-remap' entries"); 1905*0b57cec5SDimitry Andric return nullptr; 1906*0b57cec5SDimitry Andric } 1907*0b57cec5SDimitry Andric 1908480093f4SDimitry Andric sys::path::Style path_style = sys::path::Style::native; 1909480093f4SDimitry Andric if (IsRootEntry) { 1910480093f4SDimitry Andric // VFS root entries may be in either Posix or Windows style. Figure out 1911480093f4SDimitry Andric // which style we have, and use it consistently. 1912480093f4SDimitry Andric if (sys::path::is_absolute(Name, sys::path::Style::posix)) { 1913480093f4SDimitry Andric path_style = sys::path::Style::posix; 1914349cc55cSDimitry Andric } else if (sys::path::is_absolute(Name, 1915349cc55cSDimitry Andric sys::path::Style::windows_backslash)) { 1916349cc55cSDimitry Andric path_style = sys::path::Style::windows_backslash; 1917480093f4SDimitry Andric } else { 1918bdd1243dSDimitry Andric // Relative VFS root entries are made absolute to either the overlay 1919bdd1243dSDimitry Andric // directory, or the current working directory, then we can determine 1920bdd1243dSDimitry Andric // the path style from that. 1921bdd1243dSDimitry Andric std::error_code EC; 1922bdd1243dSDimitry Andric if (FS->RootRelative == 1923bdd1243dSDimitry Andric RedirectingFileSystem::RootRelativeKind::OverlayDir) { 1924bdd1243dSDimitry Andric StringRef FullPath = FS->getOverlayFileDir(); 1925bdd1243dSDimitry Andric assert(!FullPath.empty() && "Overlay file directory must exist"); 1926bdd1243dSDimitry Andric EC = FS->makeAbsolute(FullPath, Name); 1927bdd1243dSDimitry Andric Name = canonicalize(Name); 1928bdd1243dSDimitry Andric } else { 1929bdd1243dSDimitry Andric EC = sys::fs::make_absolute(Name); 1930bdd1243dSDimitry Andric } 193104eeddc0SDimitry Andric if (EC) { 1932*0b57cec5SDimitry Andric assert(NameValueNode && "Name presence should be checked earlier"); 193304eeddc0SDimitry Andric error( 193404eeddc0SDimitry Andric NameValueNode, 1935*0b57cec5SDimitry Andric "entry with relative path at the root level is not discoverable"); 1936*0b57cec5SDimitry Andric return nullptr; 1937*0b57cec5SDimitry Andric } 193804eeddc0SDimitry Andric path_style = sys::path::is_absolute(Name, sys::path::Style::posix) 193904eeddc0SDimitry Andric ? sys::path::Style::posix 194004eeddc0SDimitry Andric : sys::path::Style::windows_backslash; 194104eeddc0SDimitry Andric } 1942bdd1243dSDimitry Andric // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will 1943bdd1243dSDimitry Andric // return true even if `Name` is using forward slashes. Distinguish 1944bdd1243dSDimitry Andric // between windows_backslash and windows_slash. 1945bdd1243dSDimitry Andric if (path_style == sys::path::Style::windows_backslash && 1946bdd1243dSDimitry Andric getExistingStyle(Name) != sys::path::Style::windows_backslash) 1947bdd1243dSDimitry Andric path_style = sys::path::Style::windows_slash; 1948480093f4SDimitry Andric } 1949*0b57cec5SDimitry Andric 1950*0b57cec5SDimitry Andric // Remove trailing slash(es), being careful not to remove the root path 1951fe6060f1SDimitry Andric StringRef Trimmed = Name; 1952480093f4SDimitry Andric size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size(); 1953*0b57cec5SDimitry Andric while (Trimmed.size() > RootPathLen && 1954480093f4SDimitry Andric sys::path::is_separator(Trimmed.back(), path_style)) 1955*0b57cec5SDimitry Andric Trimmed = Trimmed.slice(0, Trimmed.size() - 1); 1956480093f4SDimitry Andric 1957*0b57cec5SDimitry Andric // Get the last component 1958480093f4SDimitry Andric StringRef LastComponent = sys::path::filename(Trimmed, path_style); 1959*0b57cec5SDimitry Andric 1960*0b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem::Entry> Result; 1961*0b57cec5SDimitry Andric switch (Kind) { 1962*0b57cec5SDimitry Andric case RedirectingFileSystem::EK_File: 1963fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::FileEntry>( 1964fe6060f1SDimitry Andric LastComponent, std::move(ExternalContentsPath), UseExternalName); 1965fe6060f1SDimitry Andric break; 1966fe6060f1SDimitry Andric case RedirectingFileSystem::EK_DirectoryRemap: 1967fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>( 1968*0b57cec5SDimitry Andric LastComponent, std::move(ExternalContentsPath), UseExternalName); 1969*0b57cec5SDimitry Andric break; 1970*0b57cec5SDimitry Andric case RedirectingFileSystem::EK_Directory: 1971fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>( 1972*0b57cec5SDimitry Andric LastComponent, std::move(EntryArrayContents), 1973fe6060f1SDimitry Andric Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(), 1974fe6060f1SDimitry Andric 0, 0, 0, file_type::directory_file, sys::fs::all_all)); 1975*0b57cec5SDimitry Andric break; 1976*0b57cec5SDimitry Andric } 1977*0b57cec5SDimitry Andric 1978480093f4SDimitry Andric StringRef Parent = sys::path::parent_path(Trimmed, path_style); 1979*0b57cec5SDimitry Andric if (Parent.empty()) 1980*0b57cec5SDimitry Andric return Result; 1981*0b57cec5SDimitry Andric 1982*0b57cec5SDimitry Andric // if 'name' contains multiple components, create implicit directory entries 1983480093f4SDimitry Andric for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style), 1984*0b57cec5SDimitry Andric E = sys::path::rend(Parent); 1985*0b57cec5SDimitry Andric I != E; ++I) { 1986*0b57cec5SDimitry Andric std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries; 1987*0b57cec5SDimitry Andric Entries.push_back(std::move(Result)); 1988fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>( 1989*0b57cec5SDimitry Andric *I, std::move(Entries), 1990fe6060f1SDimitry Andric Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(), 1991fe6060f1SDimitry Andric 0, 0, 0, file_type::directory_file, sys::fs::all_all)); 1992*0b57cec5SDimitry Andric } 1993*0b57cec5SDimitry Andric return Result; 1994*0b57cec5SDimitry Andric } 1995*0b57cec5SDimitry Andric 1996*0b57cec5SDimitry Andric public: 1997*0b57cec5SDimitry Andric RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {} 1998*0b57cec5SDimitry Andric 1999*0b57cec5SDimitry Andric // false on error 2000*0b57cec5SDimitry Andric bool parse(yaml::Node *Root, RedirectingFileSystem *FS) { 2001*0b57cec5SDimitry Andric auto *Top = dyn_cast<yaml::MappingNode>(Root); 2002*0b57cec5SDimitry Andric if (!Top) { 2003*0b57cec5SDimitry Andric error(Root, "expected mapping node"); 2004*0b57cec5SDimitry Andric return false; 2005*0b57cec5SDimitry Andric } 2006*0b57cec5SDimitry Andric 2007*0b57cec5SDimitry Andric KeyStatusPair Fields[] = { 2008*0b57cec5SDimitry Andric KeyStatusPair("version", true), 2009*0b57cec5SDimitry Andric KeyStatusPair("case-sensitive", false), 2010*0b57cec5SDimitry Andric KeyStatusPair("use-external-names", false), 2011bdd1243dSDimitry Andric KeyStatusPair("root-relative", false), 2012*0b57cec5SDimitry Andric KeyStatusPair("overlay-relative", false), 2013*0b57cec5SDimitry Andric KeyStatusPair("fallthrough", false), 201481ad6265SDimitry Andric KeyStatusPair("redirecting-with", false), 2015*0b57cec5SDimitry Andric KeyStatusPair("roots", true), 2016*0b57cec5SDimitry Andric }; 2017*0b57cec5SDimitry Andric 2018*0b57cec5SDimitry Andric DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields)); 2019*0b57cec5SDimitry Andric std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries; 2020*0b57cec5SDimitry Andric 2021*0b57cec5SDimitry Andric // Parse configuration and 'roots' 2022*0b57cec5SDimitry Andric for (auto &I : *Top) { 2023*0b57cec5SDimitry Andric SmallString<10> KeyBuffer; 2024*0b57cec5SDimitry Andric StringRef Key; 2025*0b57cec5SDimitry Andric if (!parseScalarString(I.getKey(), Key, KeyBuffer)) 2026*0b57cec5SDimitry Andric return false; 2027*0b57cec5SDimitry Andric 2028*0b57cec5SDimitry Andric if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) 2029*0b57cec5SDimitry Andric return false; 2030*0b57cec5SDimitry Andric 2031*0b57cec5SDimitry Andric if (Key == "roots") { 2032*0b57cec5SDimitry Andric auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue()); 2033*0b57cec5SDimitry Andric if (!Roots) { 2034*0b57cec5SDimitry Andric error(I.getValue(), "expected array"); 2035*0b57cec5SDimitry Andric return false; 2036*0b57cec5SDimitry Andric } 2037*0b57cec5SDimitry Andric 2038*0b57cec5SDimitry Andric for (auto &I : *Roots) { 2039*0b57cec5SDimitry Andric if (std::unique_ptr<RedirectingFileSystem::Entry> E = 2040*0b57cec5SDimitry Andric parseEntry(&I, FS, /*IsRootEntry*/ true)) 2041*0b57cec5SDimitry Andric RootEntries.push_back(std::move(E)); 2042*0b57cec5SDimitry Andric else 2043*0b57cec5SDimitry Andric return false; 2044*0b57cec5SDimitry Andric } 2045*0b57cec5SDimitry Andric } else if (Key == "version") { 2046*0b57cec5SDimitry Andric StringRef VersionString; 2047*0b57cec5SDimitry Andric SmallString<4> Storage; 2048*0b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), VersionString, Storage)) 2049*0b57cec5SDimitry Andric return false; 2050*0b57cec5SDimitry Andric int Version; 2051*0b57cec5SDimitry Andric if (VersionString.getAsInteger<int>(10, Version)) { 2052*0b57cec5SDimitry Andric error(I.getValue(), "expected integer"); 2053*0b57cec5SDimitry Andric return false; 2054*0b57cec5SDimitry Andric } 2055*0b57cec5SDimitry Andric if (Version < 0) { 2056*0b57cec5SDimitry Andric error(I.getValue(), "invalid version number"); 2057*0b57cec5SDimitry Andric return false; 2058*0b57cec5SDimitry Andric } 2059*0b57cec5SDimitry Andric if (Version != 0) { 2060*0b57cec5SDimitry Andric error(I.getValue(), "version mismatch, expected 0"); 2061*0b57cec5SDimitry Andric return false; 2062*0b57cec5SDimitry Andric } 2063*0b57cec5SDimitry Andric } else if (Key == "case-sensitive") { 2064*0b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), FS->CaseSensitive)) 2065*0b57cec5SDimitry Andric return false; 2066*0b57cec5SDimitry Andric } else if (Key == "overlay-relative") { 2067*0b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay)) 2068*0b57cec5SDimitry Andric return false; 2069*0b57cec5SDimitry Andric } else if (Key == "use-external-names") { 2070*0b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), FS->UseExternalNames)) 2071*0b57cec5SDimitry Andric return false; 2072*0b57cec5SDimitry Andric } else if (Key == "fallthrough") { 207381ad6265SDimitry Andric if (Keys["redirecting-with"].Seen) { 207481ad6265SDimitry Andric error(I.getValue(), 207581ad6265SDimitry Andric "'fallthrough' and 'redirecting-with' are mutually exclusive"); 2076*0b57cec5SDimitry Andric return false; 207781ad6265SDimitry Andric } 207881ad6265SDimitry Andric 207981ad6265SDimitry Andric bool ShouldFallthrough = false; 208081ad6265SDimitry Andric if (!parseScalarBool(I.getValue(), ShouldFallthrough)) 208181ad6265SDimitry Andric return false; 208281ad6265SDimitry Andric 208381ad6265SDimitry Andric if (ShouldFallthrough) { 208481ad6265SDimitry Andric FS->Redirection = RedirectingFileSystem::RedirectKind::Fallthrough; 208581ad6265SDimitry Andric } else { 208681ad6265SDimitry Andric FS->Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly; 208781ad6265SDimitry Andric } 208881ad6265SDimitry Andric } else if (Key == "redirecting-with") { 208981ad6265SDimitry Andric if (Keys["fallthrough"].Seen) { 209081ad6265SDimitry Andric error(I.getValue(), 209181ad6265SDimitry Andric "'fallthrough' and 'redirecting-with' are mutually exclusive"); 209281ad6265SDimitry Andric return false; 209381ad6265SDimitry Andric } 209481ad6265SDimitry Andric 209581ad6265SDimitry Andric if (auto Kind = parseRedirectKind(I.getValue())) { 209681ad6265SDimitry Andric FS->Redirection = *Kind; 209781ad6265SDimitry Andric } else { 209881ad6265SDimitry Andric error(I.getValue(), "expected valid redirect kind"); 209981ad6265SDimitry Andric return false; 210081ad6265SDimitry Andric } 2101bdd1243dSDimitry Andric } else if (Key == "root-relative") { 2102bdd1243dSDimitry Andric if (auto Kind = parseRootRelativeKind(I.getValue())) { 2103bdd1243dSDimitry Andric FS->RootRelative = *Kind; 2104bdd1243dSDimitry Andric } else { 2105bdd1243dSDimitry Andric error(I.getValue(), "expected valid root-relative kind"); 2106bdd1243dSDimitry Andric return false; 2107bdd1243dSDimitry Andric } 2108*0b57cec5SDimitry Andric } else { 2109*0b57cec5SDimitry Andric llvm_unreachable("key missing from Keys"); 2110*0b57cec5SDimitry Andric } 2111*0b57cec5SDimitry Andric } 2112*0b57cec5SDimitry Andric 2113*0b57cec5SDimitry Andric if (Stream.failed()) 2114*0b57cec5SDimitry Andric return false; 2115*0b57cec5SDimitry Andric 2116*0b57cec5SDimitry Andric if (!checkMissingKeys(Top, Keys)) 2117*0b57cec5SDimitry Andric return false; 2118*0b57cec5SDimitry Andric 2119*0b57cec5SDimitry Andric // Now that we sucessefully parsed the YAML file, canonicalize the internal 2120*0b57cec5SDimitry Andric // representation to a proper directory tree so that we can search faster 2121*0b57cec5SDimitry Andric // inside the VFS. 2122*0b57cec5SDimitry Andric for (auto &E : RootEntries) 2123*0b57cec5SDimitry Andric uniqueOverlayTree(FS, E.get()); 2124*0b57cec5SDimitry Andric 2125*0b57cec5SDimitry Andric return true; 2126*0b57cec5SDimitry Andric } 2127*0b57cec5SDimitry Andric }; 2128*0b57cec5SDimitry Andric 2129e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> 2130*0b57cec5SDimitry Andric RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer, 2131*0b57cec5SDimitry Andric SourceMgr::DiagHandlerTy DiagHandler, 2132*0b57cec5SDimitry Andric StringRef YAMLFilePath, void *DiagContext, 2133*0b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> ExternalFS) { 2134*0b57cec5SDimitry Andric SourceMgr SM; 2135*0b57cec5SDimitry Andric yaml::Stream Stream(Buffer->getMemBufferRef(), SM); 2136*0b57cec5SDimitry Andric 2137*0b57cec5SDimitry Andric SM.setDiagHandler(DiagHandler, DiagContext); 2138*0b57cec5SDimitry Andric yaml::document_iterator DI = Stream.begin(); 2139*0b57cec5SDimitry Andric yaml::Node *Root = DI->getRoot(); 2140*0b57cec5SDimitry Andric if (DI == Stream.end() || !Root) { 2141*0b57cec5SDimitry Andric SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node"); 2142*0b57cec5SDimitry Andric return nullptr; 2143*0b57cec5SDimitry Andric } 2144*0b57cec5SDimitry Andric 2145*0b57cec5SDimitry Andric RedirectingFileSystemParser P(Stream); 2146*0b57cec5SDimitry Andric 2147*0b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem> FS( 21488bcb0991SDimitry Andric new RedirectingFileSystem(ExternalFS)); 2149*0b57cec5SDimitry Andric 2150*0b57cec5SDimitry Andric if (!YAMLFilePath.empty()) { 2151*0b57cec5SDimitry Andric // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed 2152*0b57cec5SDimitry Andric // to each 'external-contents' path. 2153*0b57cec5SDimitry Andric // 2154*0b57cec5SDimitry Andric // Example: 2155*0b57cec5SDimitry Andric // -ivfsoverlay dummy.cache/vfs/vfs.yaml 2156*0b57cec5SDimitry Andric // yields: 2157bdd1243dSDimitry Andric // FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs 2158*0b57cec5SDimitry Andric // 2159*0b57cec5SDimitry Andric SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath); 2160*0b57cec5SDimitry Andric std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir); 2161*0b57cec5SDimitry Andric assert(!EC && "Overlay dir final path must be absolute"); 2162*0b57cec5SDimitry Andric (void)EC; 2163bdd1243dSDimitry Andric FS->setOverlayFileDir(OverlayAbsDir); 2164*0b57cec5SDimitry Andric } 2165*0b57cec5SDimitry Andric 2166*0b57cec5SDimitry Andric if (!P.parse(Root, FS.get())) 2167*0b57cec5SDimitry Andric return nullptr; 2168*0b57cec5SDimitry Andric 2169e8d8bef9SDimitry Andric return FS; 2170*0b57cec5SDimitry Andric } 2171*0b57cec5SDimitry Andric 2172e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create( 2173e8d8bef9SDimitry Andric ArrayRef<std::pair<std::string, std::string>> RemappedFiles, 2174e8d8bef9SDimitry Andric bool UseExternalNames, FileSystem &ExternalFS) { 2175e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> FS( 2176e8d8bef9SDimitry Andric new RedirectingFileSystem(&ExternalFS)); 2177e8d8bef9SDimitry Andric FS->UseExternalNames = UseExternalNames; 2178*0b57cec5SDimitry Andric 2179e8d8bef9SDimitry Andric StringMap<RedirectingFileSystem::Entry *> Entries; 2180e8d8bef9SDimitry Andric 2181e8d8bef9SDimitry Andric for (auto &Mapping : llvm::reverse(RemappedFiles)) { 2182e8d8bef9SDimitry Andric SmallString<128> From = StringRef(Mapping.first); 2183e8d8bef9SDimitry Andric SmallString<128> To = StringRef(Mapping.second); 2184e8d8bef9SDimitry Andric { 2185e8d8bef9SDimitry Andric auto EC = ExternalFS.makeAbsolute(From); 2186e8d8bef9SDimitry Andric (void)EC; 2187e8d8bef9SDimitry Andric assert(!EC && "Could not make absolute path"); 2188e8d8bef9SDimitry Andric } 2189e8d8bef9SDimitry Andric 2190e8d8bef9SDimitry Andric // Check if we've already mapped this file. The first one we see (in the 2191e8d8bef9SDimitry Andric // reverse iteration) wins. 2192e8d8bef9SDimitry Andric RedirectingFileSystem::Entry *&ToEntry = Entries[From]; 2193e8d8bef9SDimitry Andric if (ToEntry) 2194e8d8bef9SDimitry Andric continue; 2195e8d8bef9SDimitry Andric 2196e8d8bef9SDimitry Andric // Add parent directories. 2197e8d8bef9SDimitry Andric RedirectingFileSystem::Entry *Parent = nullptr; 2198e8d8bef9SDimitry Andric StringRef FromDirectory = llvm::sys::path::parent_path(From); 2199e8d8bef9SDimitry Andric for (auto I = llvm::sys::path::begin(FromDirectory), 2200e8d8bef9SDimitry Andric E = llvm::sys::path::end(FromDirectory); 2201e8d8bef9SDimitry Andric I != E; ++I) { 2202e8d8bef9SDimitry Andric Parent = RedirectingFileSystemParser::lookupOrCreateEntry(FS.get(), *I, 2203e8d8bef9SDimitry Andric Parent); 2204e8d8bef9SDimitry Andric } 2205e8d8bef9SDimitry Andric assert(Parent && "File without a directory?"); 2206e8d8bef9SDimitry Andric { 2207e8d8bef9SDimitry Andric auto EC = ExternalFS.makeAbsolute(To); 2208e8d8bef9SDimitry Andric (void)EC; 2209e8d8bef9SDimitry Andric assert(!EC && "Could not make absolute path"); 2210e8d8bef9SDimitry Andric } 2211e8d8bef9SDimitry Andric 2212e8d8bef9SDimitry Andric // Add the file. 2213fe6060f1SDimitry Andric auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>( 2214e8d8bef9SDimitry Andric llvm::sys::path::filename(From), To, 2215fe6060f1SDimitry Andric UseExternalNames ? RedirectingFileSystem::NK_External 2216fe6060f1SDimitry Andric : RedirectingFileSystem::NK_Virtual); 2217e8d8bef9SDimitry Andric ToEntry = NewFile.get(); 2218fe6060f1SDimitry Andric cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent( 2219e8d8bef9SDimitry Andric std::move(NewFile)); 2220e8d8bef9SDimitry Andric } 2221e8d8bef9SDimitry Andric 2222e8d8bef9SDimitry Andric return FS; 2223e8d8bef9SDimitry Andric } 2224e8d8bef9SDimitry Andric 2225fe6060f1SDimitry Andric RedirectingFileSystem::LookupResult::LookupResult( 2226fe6060f1SDimitry Andric Entry *E, sys::path::const_iterator Start, sys::path::const_iterator End) 2227fe6060f1SDimitry Andric : E(E) { 2228fe6060f1SDimitry Andric assert(E != nullptr); 2229fe6060f1SDimitry Andric // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the 2230fe6060f1SDimitry Andric // path of the directory it maps to in the external file system plus any 2231fe6060f1SDimitry Andric // remaining path components in the provided iterator. 2232fe6060f1SDimitry Andric if (auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(E)) { 2233fe6060f1SDimitry Andric SmallString<256> Redirect(DRE->getExternalContentsPath()); 2234fe6060f1SDimitry Andric sys::path::append(Redirect, Start, End, 2235fe6060f1SDimitry Andric getExistingStyle(DRE->getExternalContentsPath())); 2236fe6060f1SDimitry Andric ExternalRedirect = std::string(Redirect); 2237fe6060f1SDimitry Andric } 2238fe6060f1SDimitry Andric } 2239fe6060f1SDimitry Andric 2240e8d8bef9SDimitry Andric std::error_code 2241e8d8bef9SDimitry Andric RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const { 2242*0b57cec5SDimitry Andric if (std::error_code EC = makeAbsolute(Path)) 2243*0b57cec5SDimitry Andric return EC; 2244*0b57cec5SDimitry Andric 2245e8d8bef9SDimitry Andric llvm::SmallString<256> CanonicalPath = 2246e8d8bef9SDimitry Andric canonicalize(StringRef(Path.data(), Path.size())); 2247e8d8bef9SDimitry Andric if (CanonicalPath.empty()) 2248*0b57cec5SDimitry Andric return make_error_code(llvm::errc::invalid_argument); 2249*0b57cec5SDimitry Andric 2250e8d8bef9SDimitry Andric Path.assign(CanonicalPath.begin(), CanonicalPath.end()); 2251e8d8bef9SDimitry Andric return {}; 2252e8d8bef9SDimitry Andric } 2253e8d8bef9SDimitry Andric 2254fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> 2255e8d8bef9SDimitry Andric RedirectingFileSystem::lookupPath(StringRef Path) const { 2256*0b57cec5SDimitry Andric sys::path::const_iterator Start = sys::path::begin(Path); 2257*0b57cec5SDimitry Andric sys::path::const_iterator End = sys::path::end(Path); 2258*0b57cec5SDimitry Andric for (const auto &Root : Roots) { 2259fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = 2260fe6060f1SDimitry Andric lookupPathImpl(Start, End, Root.get()); 2261*0b57cec5SDimitry Andric if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) 2262*0b57cec5SDimitry Andric return Result; 2263*0b57cec5SDimitry Andric } 2264*0b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory); 2265*0b57cec5SDimitry Andric } 2266*0b57cec5SDimitry Andric 2267fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> 2268fe6060f1SDimitry Andric RedirectingFileSystem::lookupPathImpl( 2269fe6060f1SDimitry Andric sys::path::const_iterator Start, sys::path::const_iterator End, 2270*0b57cec5SDimitry Andric RedirectingFileSystem::Entry *From) const { 2271*0b57cec5SDimitry Andric assert(!isTraversalComponent(*Start) && 2272*0b57cec5SDimitry Andric !isTraversalComponent(From->getName()) && 2273*0b57cec5SDimitry Andric "Paths should not contain traversal components"); 2274*0b57cec5SDimitry Andric 2275*0b57cec5SDimitry Andric StringRef FromName = From->getName(); 2276*0b57cec5SDimitry Andric 2277*0b57cec5SDimitry Andric // Forward the search to the next component in case this is an empty one. 2278*0b57cec5SDimitry Andric if (!FromName.empty()) { 2279480093f4SDimitry Andric if (!pathComponentMatches(*Start, FromName)) 2280*0b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory); 2281*0b57cec5SDimitry Andric 2282*0b57cec5SDimitry Andric ++Start; 2283*0b57cec5SDimitry Andric 2284*0b57cec5SDimitry Andric if (Start == End) { 2285*0b57cec5SDimitry Andric // Match! 2286fe6060f1SDimitry Andric return LookupResult(From, Start, End); 2287*0b57cec5SDimitry Andric } 2288*0b57cec5SDimitry Andric } 2289*0b57cec5SDimitry Andric 2290fe6060f1SDimitry Andric if (isa<RedirectingFileSystem::FileEntry>(From)) 2291*0b57cec5SDimitry Andric return make_error_code(llvm::errc::not_a_directory); 2292*0b57cec5SDimitry Andric 2293fe6060f1SDimitry Andric if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From)) 2294fe6060f1SDimitry Andric return LookupResult(From, Start, End); 2295fe6060f1SDimitry Andric 2296fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From); 2297*0b57cec5SDimitry Andric for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry : 2298*0b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) { 2299fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = 2300fe6060f1SDimitry Andric lookupPathImpl(Start, End, DirEntry.get()); 2301*0b57cec5SDimitry Andric if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) 2302*0b57cec5SDimitry Andric return Result; 2303*0b57cec5SDimitry Andric } 2304480093f4SDimitry Andric 2305*0b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory); 2306*0b57cec5SDimitry Andric } 2307*0b57cec5SDimitry Andric 2308349cc55cSDimitry Andric static Status getRedirectedFileStatus(const Twine &OriginalPath, 2309349cc55cSDimitry Andric bool UseExternalNames, 2310*0b57cec5SDimitry Andric Status ExternalStatus) { 231181ad6265SDimitry Andric // The path has been mapped by some nested VFS and exposes an external path, 231281ad6265SDimitry Andric // don't override it with the original path. 231381ad6265SDimitry Andric if (ExternalStatus.ExposesExternalVFSPath) 231481ad6265SDimitry Andric return ExternalStatus; 231581ad6265SDimitry Andric 2316*0b57cec5SDimitry Andric Status S = ExternalStatus; 2317*0b57cec5SDimitry Andric if (!UseExternalNames) 2318349cc55cSDimitry Andric S = Status::copyWithNewName(S, OriginalPath); 231981ad6265SDimitry Andric else 232081ad6265SDimitry Andric S.ExposesExternalVFSPath = true; 2321*0b57cec5SDimitry Andric S.IsVFSMapped = true; 2322*0b57cec5SDimitry Andric return S; 2323*0b57cec5SDimitry Andric } 2324*0b57cec5SDimitry Andric 2325fe6060f1SDimitry Andric ErrorOr<Status> RedirectingFileSystem::status( 2326349cc55cSDimitry Andric const Twine &CanonicalPath, const Twine &OriginalPath, 2327349cc55cSDimitry Andric const RedirectingFileSystem::LookupResult &Result) { 2328bdd1243dSDimitry Andric if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) { 2329349cc55cSDimitry Andric SmallString<256> CanonicalRemappedPath((*ExtRedirect).str()); 2330349cc55cSDimitry Andric if (std::error_code EC = makeCanonical(CanonicalRemappedPath)) 2331349cc55cSDimitry Andric return EC; 2332349cc55cSDimitry Andric 2333349cc55cSDimitry Andric ErrorOr<Status> S = ExternalFS->status(CanonicalRemappedPath); 2334fe6060f1SDimitry Andric if (!S) 2335*0b57cec5SDimitry Andric return S; 2336349cc55cSDimitry Andric S = Status::copyWithNewName(*S, *ExtRedirect); 2337fe6060f1SDimitry Andric auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E); 2338349cc55cSDimitry Andric return getRedirectedFileStatus(OriginalPath, 2339349cc55cSDimitry Andric RE->useExternalName(UseExternalNames), *S); 2340*0b57cec5SDimitry Andric } 2341fe6060f1SDimitry Andric 2342fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E); 2343349cc55cSDimitry Andric return Status::copyWithNewName(DE->getStatus(), CanonicalPath); 2344*0b57cec5SDimitry Andric } 2345*0b57cec5SDimitry Andric 2346349cc55cSDimitry Andric ErrorOr<Status> 2347349cc55cSDimitry Andric RedirectingFileSystem::getExternalStatus(const Twine &CanonicalPath, 2348349cc55cSDimitry Andric const Twine &OriginalPath) const { 234981ad6265SDimitry Andric auto Result = ExternalFS->status(CanonicalPath); 235081ad6265SDimitry Andric 235181ad6265SDimitry Andric // The path has been mapped by some nested VFS, don't override it with the 235281ad6265SDimitry Andric // original path. 235381ad6265SDimitry Andric if (!Result || Result->ExposesExternalVFSPath) 235481ad6265SDimitry Andric return Result; 235581ad6265SDimitry Andric return Status::copyWithNewName(Result.get(), OriginalPath); 2356349cc55cSDimitry Andric } 2357e8d8bef9SDimitry Andric 2358349cc55cSDimitry Andric ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) { 2359349cc55cSDimitry Andric SmallString<256> CanonicalPath; 2360349cc55cSDimitry Andric OriginalPath.toVector(CanonicalPath); 2361349cc55cSDimitry Andric 2362349cc55cSDimitry Andric if (std::error_code EC = makeCanonical(CanonicalPath)) 2363e8d8bef9SDimitry Andric return EC; 2364e8d8bef9SDimitry Andric 236581ad6265SDimitry Andric if (Redirection == RedirectKind::Fallback) { 236681ad6265SDimitry Andric // Attempt to find the original file first, only falling back to the 236781ad6265SDimitry Andric // mapped file if that fails. 236881ad6265SDimitry Andric ErrorOr<Status> S = getExternalStatus(CanonicalPath, OriginalPath); 236981ad6265SDimitry Andric if (S) 237081ad6265SDimitry Andric return S; 237181ad6265SDimitry Andric } 237281ad6265SDimitry Andric 2373349cc55cSDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = 2374349cc55cSDimitry Andric lookupPath(CanonicalPath); 2375*0b57cec5SDimitry Andric if (!Result) { 237681ad6265SDimitry Andric // Was not able to map file, fallthrough to using the original path if 237781ad6265SDimitry Andric // that was the specified redirection type. 237881ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough && 237981ad6265SDimitry Andric isFileNotFound(Result.getError())) 2380349cc55cSDimitry Andric return getExternalStatus(CanonicalPath, OriginalPath); 2381*0b57cec5SDimitry Andric return Result.getError(); 2382*0b57cec5SDimitry Andric } 2383fe6060f1SDimitry Andric 2384349cc55cSDimitry Andric ErrorOr<Status> S = status(CanonicalPath, OriginalPath, *Result); 238581ad6265SDimitry Andric if (!S && Redirection == RedirectKind::Fallthrough && 238681ad6265SDimitry Andric isFileNotFound(S.getError(), Result->E)) { 238781ad6265SDimitry Andric // Mapped the file but it wasn't found in the underlying filesystem, 238881ad6265SDimitry Andric // fallthrough to using the original path if that was the specified 238981ad6265SDimitry Andric // redirection type. 2390349cc55cSDimitry Andric return getExternalStatus(CanonicalPath, OriginalPath); 2391349cc55cSDimitry Andric } 2392349cc55cSDimitry Andric 2393fe6060f1SDimitry Andric return S; 2394*0b57cec5SDimitry Andric } 2395*0b57cec5SDimitry Andric 2396*0b57cec5SDimitry Andric namespace { 2397*0b57cec5SDimitry Andric 2398*0b57cec5SDimitry Andric /// Provide a file wrapper with an overriden status. 2399*0b57cec5SDimitry Andric class FileWithFixedStatus : public File { 2400*0b57cec5SDimitry Andric std::unique_ptr<File> InnerFile; 2401*0b57cec5SDimitry Andric Status S; 2402*0b57cec5SDimitry Andric 2403*0b57cec5SDimitry Andric public: 2404*0b57cec5SDimitry Andric FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S) 2405*0b57cec5SDimitry Andric : InnerFile(std::move(InnerFile)), S(std::move(S)) {} 2406*0b57cec5SDimitry Andric 2407*0b57cec5SDimitry Andric ErrorOr<Status> status() override { return S; } 2408*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 2409*0b57cec5SDimitry Andric 2410*0b57cec5SDimitry Andric getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 2411*0b57cec5SDimitry Andric bool IsVolatile) override { 2412*0b57cec5SDimitry Andric return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator, 2413*0b57cec5SDimitry Andric IsVolatile); 2414*0b57cec5SDimitry Andric } 2415*0b57cec5SDimitry Andric 2416*0b57cec5SDimitry Andric std::error_code close() override { return InnerFile->close(); } 2417349cc55cSDimitry Andric 2418349cc55cSDimitry Andric void setPath(const Twine &Path) override { S = S.copyWithNewName(S, Path); } 2419*0b57cec5SDimitry Andric }; 2420*0b57cec5SDimitry Andric 2421*0b57cec5SDimitry Andric } // namespace 2422*0b57cec5SDimitry Andric 2423*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>> 2424349cc55cSDimitry Andric File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) { 242581ad6265SDimitry Andric // See \c getRedirectedFileStatus - don't update path if it's exposing an 242681ad6265SDimitry Andric // external path. 242781ad6265SDimitry Andric if (!Result || (*Result)->status()->ExposesExternalVFSPath) 2428349cc55cSDimitry Andric return Result; 2429e8d8bef9SDimitry Andric 2430349cc55cSDimitry Andric ErrorOr<std::unique_ptr<File>> F = std::move(*Result); 2431349cc55cSDimitry Andric auto Name = F->get()->getName(); 2432349cc55cSDimitry Andric if (Name && Name.get() != P.str()) 2433349cc55cSDimitry Andric F->get()->setPath(P); 2434349cc55cSDimitry Andric return F; 2435349cc55cSDimitry Andric } 2436349cc55cSDimitry Andric 2437349cc55cSDimitry Andric ErrorOr<std::unique_ptr<File>> 2438349cc55cSDimitry Andric RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) { 2439349cc55cSDimitry Andric SmallString<256> CanonicalPath; 2440349cc55cSDimitry Andric OriginalPath.toVector(CanonicalPath); 2441349cc55cSDimitry Andric 2442349cc55cSDimitry Andric if (std::error_code EC = makeCanonical(CanonicalPath)) 2443e8d8bef9SDimitry Andric return EC; 2444e8d8bef9SDimitry Andric 244581ad6265SDimitry Andric if (Redirection == RedirectKind::Fallback) { 244681ad6265SDimitry Andric // Attempt to find the original file first, only falling back to the 244781ad6265SDimitry Andric // mapped file if that fails. 244881ad6265SDimitry Andric auto F = File::getWithPath(ExternalFS->openFileForRead(CanonicalPath), 244981ad6265SDimitry Andric OriginalPath); 245081ad6265SDimitry Andric if (F) 245181ad6265SDimitry Andric return F; 245281ad6265SDimitry Andric } 245381ad6265SDimitry Andric 2454349cc55cSDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = 2455349cc55cSDimitry Andric lookupPath(CanonicalPath); 2456fe6060f1SDimitry Andric if (!Result) { 245781ad6265SDimitry Andric // Was not able to map file, fallthrough to using the original path if 245881ad6265SDimitry Andric // that was the specified redirection type. 245981ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough && 246081ad6265SDimitry Andric isFileNotFound(Result.getError())) 2461349cc55cSDimitry Andric return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath), 2462349cc55cSDimitry Andric OriginalPath); 2463fe6060f1SDimitry Andric return Result.getError(); 2464*0b57cec5SDimitry Andric } 2465*0b57cec5SDimitry Andric 2466fe6060f1SDimitry Andric if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file? 2467*0b57cec5SDimitry Andric return make_error_code(llvm::errc::invalid_argument); 2468*0b57cec5SDimitry Andric 2469fe6060f1SDimitry Andric StringRef ExtRedirect = *Result->getExternalRedirect(); 2470349cc55cSDimitry Andric SmallString<256> CanonicalRemappedPath(ExtRedirect.str()); 2471349cc55cSDimitry Andric if (std::error_code EC = makeCanonical(CanonicalRemappedPath)) 2472349cc55cSDimitry Andric return EC; 2473349cc55cSDimitry Andric 2474fe6060f1SDimitry Andric auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E); 2475*0b57cec5SDimitry Andric 2476349cc55cSDimitry Andric auto ExternalFile = File::getWithPath( 2477349cc55cSDimitry Andric ExternalFS->openFileForRead(CanonicalRemappedPath), ExtRedirect); 2478fe6060f1SDimitry Andric if (!ExternalFile) { 247981ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough && 248081ad6265SDimitry Andric isFileNotFound(ExternalFile.getError(), Result->E)) { 248181ad6265SDimitry Andric // Mapped the file but it wasn't found in the underlying filesystem, 248281ad6265SDimitry Andric // fallthrough to using the original path if that was the specified 248381ad6265SDimitry Andric // redirection type. 2484349cc55cSDimitry Andric return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath), 2485349cc55cSDimitry Andric OriginalPath); 248681ad6265SDimitry Andric } 2487fe6060f1SDimitry Andric return ExternalFile; 2488fe6060f1SDimitry Andric } 2489fe6060f1SDimitry Andric 2490fe6060f1SDimitry Andric auto ExternalStatus = (*ExternalFile)->status(); 2491*0b57cec5SDimitry Andric if (!ExternalStatus) 2492*0b57cec5SDimitry Andric return ExternalStatus.getError(); 2493*0b57cec5SDimitry Andric 249481ad6265SDimitry Andric // Otherwise, the file was successfully remapped. Mark it as such. Also 249581ad6265SDimitry Andric // replace the underlying path if the external name is being used. 2496fe6060f1SDimitry Andric Status S = getRedirectedFileStatus( 2497349cc55cSDimitry Andric OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus); 2498*0b57cec5SDimitry Andric return std::unique_ptr<File>( 2499fe6060f1SDimitry Andric std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S)); 2500*0b57cec5SDimitry Andric } 2501*0b57cec5SDimitry Andric 2502*0b57cec5SDimitry Andric std::error_code 250381ad6265SDimitry Andric RedirectingFileSystem::getRealPath(const Twine &OriginalPath, 2504*0b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const { 250581ad6265SDimitry Andric SmallString<256> CanonicalPath; 250681ad6265SDimitry Andric OriginalPath.toVector(CanonicalPath); 2507e8d8bef9SDimitry Andric 250881ad6265SDimitry Andric if (std::error_code EC = makeCanonical(CanonicalPath)) 2509e8d8bef9SDimitry Andric return EC; 2510e8d8bef9SDimitry Andric 251181ad6265SDimitry Andric if (Redirection == RedirectKind::Fallback) { 251281ad6265SDimitry Andric // Attempt to find the original file first, only falling back to the 251381ad6265SDimitry Andric // mapped file if that fails. 251481ad6265SDimitry Andric std::error_code EC = ExternalFS->getRealPath(CanonicalPath, Output); 251581ad6265SDimitry Andric if (!EC) 251681ad6265SDimitry Andric return EC; 251781ad6265SDimitry Andric } 251881ad6265SDimitry Andric 251981ad6265SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = 252081ad6265SDimitry Andric lookupPath(CanonicalPath); 2521*0b57cec5SDimitry Andric if (!Result) { 252281ad6265SDimitry Andric // Was not able to map file, fallthrough to using the original path if 252381ad6265SDimitry Andric // that was the specified redirection type. 252481ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough && 252581ad6265SDimitry Andric isFileNotFound(Result.getError())) 252681ad6265SDimitry Andric return ExternalFS->getRealPath(CanonicalPath, Output); 2527*0b57cec5SDimitry Andric return Result.getError(); 2528*0b57cec5SDimitry Andric } 2529*0b57cec5SDimitry Andric 2530fe6060f1SDimitry Andric // If we found FileEntry or DirectoryRemapEntry, look up the mapped 2531fe6060f1SDimitry Andric // path in the external file system. 2532fe6060f1SDimitry Andric if (auto ExtRedirect = Result->getExternalRedirect()) { 2533fe6060f1SDimitry Andric auto P = ExternalFS->getRealPath(*ExtRedirect, Output); 253481ad6265SDimitry Andric if (P && Redirection == RedirectKind::Fallthrough && 253581ad6265SDimitry Andric isFileNotFound(P, Result->E)) { 253681ad6265SDimitry Andric // Mapped the file but it wasn't found in the underlying filesystem, 253781ad6265SDimitry Andric // fallthrough to using the original path if that was the specified 253881ad6265SDimitry Andric // redirection type. 253981ad6265SDimitry Andric return ExternalFS->getRealPath(CanonicalPath, Output); 2540*0b57cec5SDimitry Andric } 2541fe6060f1SDimitry Andric return P; 2542fe6060f1SDimitry Andric } 2543fe6060f1SDimitry Andric 254481ad6265SDimitry Andric // If we found a DirectoryEntry, still fallthrough to the original path if 254581ad6265SDimitry Andric // allowed, because directories don't have a single external contents path. 254681ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough) 254781ad6265SDimitry Andric return ExternalFS->getRealPath(CanonicalPath, Output); 254881ad6265SDimitry Andric return llvm::errc::invalid_argument; 2549*0b57cec5SDimitry Andric } 2550*0b57cec5SDimitry Andric 2551e8d8bef9SDimitry Andric std::unique_ptr<FileSystem> 2552*0b57cec5SDimitry Andric vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer, 2553*0b57cec5SDimitry Andric SourceMgr::DiagHandlerTy DiagHandler, 2554*0b57cec5SDimitry Andric StringRef YAMLFilePath, void *DiagContext, 2555*0b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> ExternalFS) { 2556*0b57cec5SDimitry Andric return RedirectingFileSystem::create(std::move(Buffer), DiagHandler, 2557*0b57cec5SDimitry Andric YAMLFilePath, DiagContext, 2558*0b57cec5SDimitry Andric std::move(ExternalFS)); 2559*0b57cec5SDimitry Andric } 2560*0b57cec5SDimitry Andric 2561*0b57cec5SDimitry Andric static void getVFSEntries(RedirectingFileSystem::Entry *SrcE, 2562*0b57cec5SDimitry Andric SmallVectorImpl<StringRef> &Path, 2563*0b57cec5SDimitry Andric SmallVectorImpl<YAMLVFSEntry> &Entries) { 2564*0b57cec5SDimitry Andric auto Kind = SrcE->getKind(); 2565*0b57cec5SDimitry Andric if (Kind == RedirectingFileSystem::EK_Directory) { 2566fe6060f1SDimitry Andric auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(SrcE); 2567*0b57cec5SDimitry Andric assert(DE && "Must be a directory"); 2568*0b57cec5SDimitry Andric for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry : 2569*0b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) { 2570*0b57cec5SDimitry Andric Path.push_back(SubEntry->getName()); 2571*0b57cec5SDimitry Andric getVFSEntries(SubEntry.get(), Path, Entries); 2572*0b57cec5SDimitry Andric Path.pop_back(); 2573*0b57cec5SDimitry Andric } 2574*0b57cec5SDimitry Andric return; 2575*0b57cec5SDimitry Andric } 2576*0b57cec5SDimitry Andric 2577fe6060f1SDimitry Andric if (Kind == RedirectingFileSystem::EK_DirectoryRemap) { 2578fe6060f1SDimitry Andric auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE); 2579fe6060f1SDimitry Andric assert(DR && "Must be a directory remap"); 2580fe6060f1SDimitry Andric SmallString<128> VPath; 2581fe6060f1SDimitry Andric for (auto &Comp : Path) 2582fe6060f1SDimitry Andric llvm::sys::path::append(VPath, Comp); 2583fe6060f1SDimitry Andric Entries.push_back( 2584fe6060f1SDimitry Andric YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath())); 2585fe6060f1SDimitry Andric return; 2586fe6060f1SDimitry Andric } 2587fe6060f1SDimitry Andric 2588*0b57cec5SDimitry Andric assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File"); 2589fe6060f1SDimitry Andric auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE); 2590*0b57cec5SDimitry Andric assert(FE && "Must be a file"); 2591*0b57cec5SDimitry Andric SmallString<128> VPath; 2592*0b57cec5SDimitry Andric for (auto &Comp : Path) 2593*0b57cec5SDimitry Andric llvm::sys::path::append(VPath, Comp); 2594*0b57cec5SDimitry Andric Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath())); 2595*0b57cec5SDimitry Andric } 2596*0b57cec5SDimitry Andric 2597*0b57cec5SDimitry Andric void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer, 2598*0b57cec5SDimitry Andric SourceMgr::DiagHandlerTy DiagHandler, 2599*0b57cec5SDimitry Andric StringRef YAMLFilePath, 2600*0b57cec5SDimitry Andric SmallVectorImpl<YAMLVFSEntry> &CollectedEntries, 2601*0b57cec5SDimitry Andric void *DiagContext, 2602*0b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> ExternalFS) { 2603e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create( 2604*0b57cec5SDimitry Andric std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext, 2605*0b57cec5SDimitry Andric std::move(ExternalFS)); 2606fe6060f1SDimitry Andric if (!VFS) 2607fe6060f1SDimitry Andric return; 2608fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> RootResult = 2609fe6060f1SDimitry Andric VFS->lookupPath("/"); 2610fe6060f1SDimitry Andric if (!RootResult) 2611*0b57cec5SDimitry Andric return; 2612*0b57cec5SDimitry Andric SmallVector<StringRef, 8> Components; 2613*0b57cec5SDimitry Andric Components.push_back("/"); 2614fe6060f1SDimitry Andric getVFSEntries(RootResult->E, Components, CollectedEntries); 2615*0b57cec5SDimitry Andric } 2616*0b57cec5SDimitry Andric 2617*0b57cec5SDimitry Andric UniqueID vfs::getNextVirtualUniqueID() { 2618*0b57cec5SDimitry Andric static std::atomic<unsigned> UID; 2619*0b57cec5SDimitry Andric unsigned ID = ++UID; 2620*0b57cec5SDimitry Andric // The following assumes that uint64_t max will never collide with a real 2621*0b57cec5SDimitry Andric // dev_t value from the OS. 2622*0b57cec5SDimitry Andric return UniqueID(std::numeric_limits<uint64_t>::max(), ID); 2623*0b57cec5SDimitry Andric } 2624*0b57cec5SDimitry Andric 26255ffd83dbSDimitry Andric void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath, 26265ffd83dbSDimitry Andric bool IsDirectory) { 2627*0b57cec5SDimitry Andric assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute"); 2628*0b57cec5SDimitry Andric assert(sys::path::is_absolute(RealPath) && "real path not absolute"); 2629*0b57cec5SDimitry Andric assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported"); 26305ffd83dbSDimitry Andric Mappings.emplace_back(VirtualPath, RealPath, IsDirectory); 26315ffd83dbSDimitry Andric } 26325ffd83dbSDimitry Andric 26335ffd83dbSDimitry Andric void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) { 26345ffd83dbSDimitry Andric addEntry(VirtualPath, RealPath, /*IsDirectory=*/false); 26355ffd83dbSDimitry Andric } 26365ffd83dbSDimitry Andric 26375ffd83dbSDimitry Andric void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath, 26385ffd83dbSDimitry Andric StringRef RealPath) { 26395ffd83dbSDimitry Andric addEntry(VirtualPath, RealPath, /*IsDirectory=*/true); 2640*0b57cec5SDimitry Andric } 2641*0b57cec5SDimitry Andric 2642*0b57cec5SDimitry Andric namespace { 2643*0b57cec5SDimitry Andric 2644*0b57cec5SDimitry Andric class JSONWriter { 2645*0b57cec5SDimitry Andric llvm::raw_ostream &OS; 2646*0b57cec5SDimitry Andric SmallVector<StringRef, 16> DirStack; 2647*0b57cec5SDimitry Andric 2648*0b57cec5SDimitry Andric unsigned getDirIndent() { return 4 * DirStack.size(); } 2649*0b57cec5SDimitry Andric unsigned getFileIndent() { return 4 * (DirStack.size() + 1); } 2650*0b57cec5SDimitry Andric bool containedIn(StringRef Parent, StringRef Path); 2651*0b57cec5SDimitry Andric StringRef containedPart(StringRef Parent, StringRef Path); 2652*0b57cec5SDimitry Andric void startDirectory(StringRef Path); 2653*0b57cec5SDimitry Andric void endDirectory(); 2654*0b57cec5SDimitry Andric void writeEntry(StringRef VPath, StringRef RPath); 2655*0b57cec5SDimitry Andric 2656*0b57cec5SDimitry Andric public: 2657*0b57cec5SDimitry Andric JSONWriter(llvm::raw_ostream &OS) : OS(OS) {} 2658*0b57cec5SDimitry Andric 2659bdd1243dSDimitry Andric void write(ArrayRef<YAMLVFSEntry> Entries, 2660bdd1243dSDimitry Andric std::optional<bool> UseExternalNames, 2661bdd1243dSDimitry Andric std::optional<bool> IsCaseSensitive, 2662bdd1243dSDimitry Andric std::optional<bool> IsOverlayRelative, StringRef OverlayDir); 2663*0b57cec5SDimitry Andric }; 2664*0b57cec5SDimitry Andric 2665*0b57cec5SDimitry Andric } // namespace 2666*0b57cec5SDimitry Andric 2667*0b57cec5SDimitry Andric bool JSONWriter::containedIn(StringRef Parent, StringRef Path) { 2668*0b57cec5SDimitry Andric using namespace llvm::sys; 2669*0b57cec5SDimitry Andric 2670*0b57cec5SDimitry Andric // Compare each path component. 2671*0b57cec5SDimitry Andric auto IParent = path::begin(Parent), EParent = path::end(Parent); 2672*0b57cec5SDimitry Andric for (auto IChild = path::begin(Path), EChild = path::end(Path); 2673*0b57cec5SDimitry Andric IParent != EParent && IChild != EChild; ++IParent, ++IChild) { 2674*0b57cec5SDimitry Andric if (*IParent != *IChild) 2675*0b57cec5SDimitry Andric return false; 2676*0b57cec5SDimitry Andric } 2677*0b57cec5SDimitry Andric // Have we exhausted the parent path? 2678*0b57cec5SDimitry Andric return IParent == EParent; 2679*0b57cec5SDimitry Andric } 2680*0b57cec5SDimitry Andric 2681*0b57cec5SDimitry Andric StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) { 2682*0b57cec5SDimitry Andric assert(!Parent.empty()); 2683*0b57cec5SDimitry Andric assert(containedIn(Parent, Path)); 2684*0b57cec5SDimitry Andric return Path.slice(Parent.size() + 1, StringRef::npos); 2685*0b57cec5SDimitry Andric } 2686*0b57cec5SDimitry Andric 2687*0b57cec5SDimitry Andric void JSONWriter::startDirectory(StringRef Path) { 2688*0b57cec5SDimitry Andric StringRef Name = 2689*0b57cec5SDimitry Andric DirStack.empty() ? Path : containedPart(DirStack.back(), Path); 2690*0b57cec5SDimitry Andric DirStack.push_back(Path); 2691*0b57cec5SDimitry Andric unsigned Indent = getDirIndent(); 2692*0b57cec5SDimitry Andric OS.indent(Indent) << "{\n"; 2693*0b57cec5SDimitry Andric OS.indent(Indent + 2) << "'type': 'directory',\n"; 2694*0b57cec5SDimitry Andric OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n"; 2695*0b57cec5SDimitry Andric OS.indent(Indent + 2) << "'contents': [\n"; 2696*0b57cec5SDimitry Andric } 2697*0b57cec5SDimitry Andric 2698*0b57cec5SDimitry Andric void JSONWriter::endDirectory() { 2699*0b57cec5SDimitry Andric unsigned Indent = getDirIndent(); 2700*0b57cec5SDimitry Andric OS.indent(Indent + 2) << "]\n"; 2701*0b57cec5SDimitry Andric OS.indent(Indent) << "}"; 2702*0b57cec5SDimitry Andric 2703*0b57cec5SDimitry Andric DirStack.pop_back(); 2704*0b57cec5SDimitry Andric } 2705*0b57cec5SDimitry Andric 2706*0b57cec5SDimitry Andric void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) { 2707*0b57cec5SDimitry Andric unsigned Indent = getFileIndent(); 2708*0b57cec5SDimitry Andric OS.indent(Indent) << "{\n"; 2709*0b57cec5SDimitry Andric OS.indent(Indent + 2) << "'type': 'file',\n"; 2710*0b57cec5SDimitry Andric OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n"; 2711*0b57cec5SDimitry Andric OS.indent(Indent + 2) << "'external-contents': \"" 2712*0b57cec5SDimitry Andric << llvm::yaml::escape(RPath) << "\"\n"; 2713*0b57cec5SDimitry Andric OS.indent(Indent) << "}"; 2714*0b57cec5SDimitry Andric } 2715*0b57cec5SDimitry Andric 2716*0b57cec5SDimitry Andric void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries, 2717bdd1243dSDimitry Andric std::optional<bool> UseExternalNames, 2718bdd1243dSDimitry Andric std::optional<bool> IsCaseSensitive, 2719bdd1243dSDimitry Andric std::optional<bool> IsOverlayRelative, 2720*0b57cec5SDimitry Andric StringRef OverlayDir) { 2721*0b57cec5SDimitry Andric using namespace llvm::sys; 2722*0b57cec5SDimitry Andric 2723*0b57cec5SDimitry Andric OS << "{\n" 2724*0b57cec5SDimitry Andric " 'version': 0,\n"; 272581ad6265SDimitry Andric if (IsCaseSensitive) 2726bdd1243dSDimitry Andric OS << " 'case-sensitive': '" << (*IsCaseSensitive ? "true" : "false") 2727bdd1243dSDimitry Andric << "',\n"; 272881ad6265SDimitry Andric if (UseExternalNames) 2729bdd1243dSDimitry Andric OS << " 'use-external-names': '" << (*UseExternalNames ? "true" : "false") 2730bdd1243dSDimitry Andric << "',\n"; 2731*0b57cec5SDimitry Andric bool UseOverlayRelative = false; 273281ad6265SDimitry Andric if (IsOverlayRelative) { 2733bdd1243dSDimitry Andric UseOverlayRelative = *IsOverlayRelative; 2734*0b57cec5SDimitry Andric OS << " 'overlay-relative': '" << (UseOverlayRelative ? "true" : "false") 2735*0b57cec5SDimitry Andric << "',\n"; 2736*0b57cec5SDimitry Andric } 2737*0b57cec5SDimitry Andric OS << " 'roots': [\n"; 2738*0b57cec5SDimitry Andric 2739*0b57cec5SDimitry Andric if (!Entries.empty()) { 2740*0b57cec5SDimitry Andric const YAMLVFSEntry &Entry = Entries.front(); 27415ffd83dbSDimitry Andric 27425ffd83dbSDimitry Andric startDirectory( 27435ffd83dbSDimitry Andric Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath) 27445ffd83dbSDimitry Andric ); 2745*0b57cec5SDimitry Andric 2746*0b57cec5SDimitry Andric StringRef RPath = Entry.RPath; 2747*0b57cec5SDimitry Andric if (UseOverlayRelative) { 2748*0b57cec5SDimitry Andric unsigned OverlayDirLen = OverlayDir.size(); 2749*0b57cec5SDimitry Andric assert(RPath.substr(0, OverlayDirLen) == OverlayDir && 2750*0b57cec5SDimitry Andric "Overlay dir must be contained in RPath"); 2751*0b57cec5SDimitry Andric RPath = RPath.slice(OverlayDirLen, RPath.size()); 2752*0b57cec5SDimitry Andric } 2753*0b57cec5SDimitry Andric 27545ffd83dbSDimitry Andric bool IsCurrentDirEmpty = true; 27555ffd83dbSDimitry Andric if (!Entry.IsDirectory) { 2756*0b57cec5SDimitry Andric writeEntry(path::filename(Entry.VPath), RPath); 27575ffd83dbSDimitry Andric IsCurrentDirEmpty = false; 27585ffd83dbSDimitry Andric } 2759*0b57cec5SDimitry Andric 2760*0b57cec5SDimitry Andric for (const auto &Entry : Entries.slice(1)) { 27615ffd83dbSDimitry Andric StringRef Dir = 27625ffd83dbSDimitry Andric Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath); 27635ffd83dbSDimitry Andric if (Dir == DirStack.back()) { 27645ffd83dbSDimitry Andric if (!IsCurrentDirEmpty) { 2765*0b57cec5SDimitry Andric OS << ",\n"; 27665ffd83dbSDimitry Andric } 27675ffd83dbSDimitry Andric } else { 27685ffd83dbSDimitry Andric bool IsDirPoppedFromStack = false; 2769*0b57cec5SDimitry Andric while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) { 2770*0b57cec5SDimitry Andric OS << "\n"; 2771*0b57cec5SDimitry Andric endDirectory(); 27725ffd83dbSDimitry Andric IsDirPoppedFromStack = true; 2773*0b57cec5SDimitry Andric } 27745ffd83dbSDimitry Andric if (IsDirPoppedFromStack || !IsCurrentDirEmpty) { 2775*0b57cec5SDimitry Andric OS << ",\n"; 27765ffd83dbSDimitry Andric } 2777*0b57cec5SDimitry Andric startDirectory(Dir); 27785ffd83dbSDimitry Andric IsCurrentDirEmpty = true; 2779*0b57cec5SDimitry Andric } 2780*0b57cec5SDimitry Andric StringRef RPath = Entry.RPath; 2781*0b57cec5SDimitry Andric if (UseOverlayRelative) { 2782*0b57cec5SDimitry Andric unsigned OverlayDirLen = OverlayDir.size(); 2783*0b57cec5SDimitry Andric assert(RPath.substr(0, OverlayDirLen) == OverlayDir && 2784*0b57cec5SDimitry Andric "Overlay dir must be contained in RPath"); 2785*0b57cec5SDimitry Andric RPath = RPath.slice(OverlayDirLen, RPath.size()); 2786*0b57cec5SDimitry Andric } 27875ffd83dbSDimitry Andric if (!Entry.IsDirectory) { 2788*0b57cec5SDimitry Andric writeEntry(path::filename(Entry.VPath), RPath); 27895ffd83dbSDimitry Andric IsCurrentDirEmpty = false; 27905ffd83dbSDimitry Andric } 2791*0b57cec5SDimitry Andric } 2792*0b57cec5SDimitry Andric 2793*0b57cec5SDimitry Andric while (!DirStack.empty()) { 2794*0b57cec5SDimitry Andric OS << "\n"; 2795*0b57cec5SDimitry Andric endDirectory(); 2796*0b57cec5SDimitry Andric } 2797*0b57cec5SDimitry Andric OS << "\n"; 2798*0b57cec5SDimitry Andric } 2799*0b57cec5SDimitry Andric 2800*0b57cec5SDimitry Andric OS << " ]\n" 2801*0b57cec5SDimitry Andric << "}\n"; 2802*0b57cec5SDimitry Andric } 2803*0b57cec5SDimitry Andric 2804*0b57cec5SDimitry Andric void YAMLVFSWriter::write(llvm::raw_ostream &OS) { 2805*0b57cec5SDimitry Andric llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) { 2806*0b57cec5SDimitry Andric return LHS.VPath < RHS.VPath; 2807*0b57cec5SDimitry Andric }); 2808*0b57cec5SDimitry Andric 2809*0b57cec5SDimitry Andric JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive, 2810*0b57cec5SDimitry Andric IsOverlayRelative, OverlayDir); 2811*0b57cec5SDimitry Andric } 2812*0b57cec5SDimitry Andric 2813*0b57cec5SDimitry Andric vfs::recursive_directory_iterator::recursive_directory_iterator( 2814*0b57cec5SDimitry Andric FileSystem &FS_, const Twine &Path, std::error_code &EC) 2815*0b57cec5SDimitry Andric : FS(&FS_) { 2816*0b57cec5SDimitry Andric directory_iterator I = FS->dir_begin(Path, EC); 2817*0b57cec5SDimitry Andric if (I != directory_iterator()) { 2818*0b57cec5SDimitry Andric State = std::make_shared<detail::RecDirIterState>(); 2819*0b57cec5SDimitry Andric State->Stack.push(I); 2820*0b57cec5SDimitry Andric } 2821*0b57cec5SDimitry Andric } 2822*0b57cec5SDimitry Andric 2823*0b57cec5SDimitry Andric vfs::recursive_directory_iterator & 2824*0b57cec5SDimitry Andric recursive_directory_iterator::increment(std::error_code &EC) { 2825*0b57cec5SDimitry Andric assert(FS && State && !State->Stack.empty() && "incrementing past end"); 2826*0b57cec5SDimitry Andric assert(!State->Stack.top()->path().empty() && "non-canonical end iterator"); 2827*0b57cec5SDimitry Andric vfs::directory_iterator End; 2828*0b57cec5SDimitry Andric 2829*0b57cec5SDimitry Andric if (State->HasNoPushRequest) 2830*0b57cec5SDimitry Andric State->HasNoPushRequest = false; 2831*0b57cec5SDimitry Andric else { 2832*0b57cec5SDimitry Andric if (State->Stack.top()->type() == sys::fs::file_type::directory_file) { 2833*0b57cec5SDimitry Andric vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC); 2834*0b57cec5SDimitry Andric if (I != End) { 2835*0b57cec5SDimitry Andric State->Stack.push(I); 2836*0b57cec5SDimitry Andric return *this; 2837*0b57cec5SDimitry Andric } 2838*0b57cec5SDimitry Andric } 2839*0b57cec5SDimitry Andric } 2840*0b57cec5SDimitry Andric 2841*0b57cec5SDimitry Andric while (!State->Stack.empty() && State->Stack.top().increment(EC) == End) 2842*0b57cec5SDimitry Andric State->Stack.pop(); 2843*0b57cec5SDimitry Andric 2844*0b57cec5SDimitry Andric if (State->Stack.empty()) 2845*0b57cec5SDimitry Andric State.reset(); // end iterator 2846*0b57cec5SDimitry Andric 2847*0b57cec5SDimitry Andric return *this; 2848*0b57cec5SDimitry Andric } 2849