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