10b57cec5SDimitry Andric //===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements the VirtualFileSystem interface.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric
130b57cec5SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
140b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h"
150b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
160b57cec5SDimitry Andric #include "llvm/ADT/IntrusiveRefCntPtr.h"
170b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
180b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
190b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
200b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
210b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h"
220b57cec5SDimitry Andric #include "llvm/ADT/Twine.h"
230b57cec5SDimitry Andric #include "llvm/ADT/iterator_range.h"
240b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h"
250b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
260b57cec5SDimitry Andric #include "llvm/Support/Chrono.h"
270b57cec5SDimitry Andric #include "llvm/Support/Compiler.h"
280b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
290b57cec5SDimitry Andric #include "llvm/Support/Errc.h"
300b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
310b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h"
320b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
33349cc55cSDimitry Andric #include "llvm/Support/FileSystem/UniqueID.h"
340b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
350b57cec5SDimitry Andric #include "llvm/Support/Path.h"
360b57cec5SDimitry Andric #include "llvm/Support/SMLoc.h"
370b57cec5SDimitry Andric #include "llvm/Support/SourceMgr.h"
380b57cec5SDimitry Andric #include "llvm/Support/YAMLParser.h"
390b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
400b57cec5SDimitry Andric #include <algorithm>
410b57cec5SDimitry Andric #include <atomic>
420b57cec5SDimitry Andric #include <cassert>
430b57cec5SDimitry Andric #include <cstdint>
440b57cec5SDimitry Andric #include <iterator>
450b57cec5SDimitry Andric #include <limits>
4606c3fb27SDimitry Andric #include <map>
470b57cec5SDimitry Andric #include <memory>
48bdd1243dSDimitry Andric #include <optional>
490b57cec5SDimitry Andric #include <string>
500b57cec5SDimitry Andric #include <system_error>
510b57cec5SDimitry Andric #include <utility>
520b57cec5SDimitry Andric #include <vector>
530b57cec5SDimitry Andric
540b57cec5SDimitry Andric using namespace llvm;
550b57cec5SDimitry Andric using namespace llvm::vfs;
560b57cec5SDimitry Andric
570b57cec5SDimitry Andric using llvm::sys::fs::file_t;
580b57cec5SDimitry Andric using llvm::sys::fs::file_status;
590b57cec5SDimitry Andric using llvm::sys::fs::file_type;
600b57cec5SDimitry Andric using llvm::sys::fs::kInvalidFile;
610b57cec5SDimitry Andric using llvm::sys::fs::perms;
620b57cec5SDimitry Andric using llvm::sys::fs::UniqueID;
630b57cec5SDimitry Andric
Status(const file_status & Status)640b57cec5SDimitry Andric Status::Status(const file_status &Status)
650b57cec5SDimitry Andric : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
660b57cec5SDimitry Andric User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
670b57cec5SDimitry Andric Type(Status.type()), Perms(Status.permissions()) {}
680b57cec5SDimitry Andric
Status(const Twine & Name,UniqueID UID,sys::TimePoint<> MTime,uint32_t User,uint32_t Group,uint64_t Size,file_type Type,perms Perms)690b57cec5SDimitry Andric Status::Status(const Twine &Name, UniqueID UID, sys::TimePoint<> MTime,
700b57cec5SDimitry Andric uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
710b57cec5SDimitry Andric perms Perms)
720b57cec5SDimitry Andric : Name(Name.str()), UID(UID), MTime(MTime), User(User), Group(Group),
730b57cec5SDimitry Andric Size(Size), Type(Type), Perms(Perms) {}
740b57cec5SDimitry Andric
copyWithNewSize(const Status & In,uint64_t NewSize)750eae32dcSDimitry Andric Status Status::copyWithNewSize(const Status &In, uint64_t NewSize) {
760eae32dcSDimitry Andric return Status(In.getName(), In.getUniqueID(), In.getLastModificationTime(),
770eae32dcSDimitry Andric In.getUser(), In.getGroup(), NewSize, In.getType(),
780eae32dcSDimitry Andric In.getPermissions());
790eae32dcSDimitry Andric }
800eae32dcSDimitry Andric
copyWithNewName(const Status & In,const Twine & NewName)810b57cec5SDimitry Andric Status Status::copyWithNewName(const Status &In, const Twine &NewName) {
820b57cec5SDimitry Andric return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
830b57cec5SDimitry Andric In.getUser(), In.getGroup(), In.getSize(), In.getType(),
840b57cec5SDimitry Andric In.getPermissions());
850b57cec5SDimitry Andric }
860b57cec5SDimitry Andric
copyWithNewName(const file_status & In,const Twine & NewName)870b57cec5SDimitry Andric Status Status::copyWithNewName(const file_status &In, const Twine &NewName) {
880b57cec5SDimitry Andric return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
890b57cec5SDimitry Andric In.getUser(), In.getGroup(), In.getSize(), In.type(),
900b57cec5SDimitry Andric In.permissions());
910b57cec5SDimitry Andric }
920b57cec5SDimitry Andric
equivalent(const Status & Other) const930b57cec5SDimitry Andric bool Status::equivalent(const Status &Other) const {
940b57cec5SDimitry Andric assert(isStatusKnown() && Other.isStatusKnown());
950b57cec5SDimitry Andric return getUniqueID() == Other.getUniqueID();
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric
isDirectory() const980b57cec5SDimitry Andric bool Status::isDirectory() const { return Type == file_type::directory_file; }
990b57cec5SDimitry Andric
isRegularFile() const1000b57cec5SDimitry Andric bool Status::isRegularFile() const { return Type == file_type::regular_file; }
1010b57cec5SDimitry Andric
isOther() const1020b57cec5SDimitry Andric bool Status::isOther() const {
1030b57cec5SDimitry Andric return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
1040b57cec5SDimitry Andric }
1050b57cec5SDimitry Andric
isSymlink() const1060b57cec5SDimitry Andric bool Status::isSymlink() const { return Type == file_type::symlink_file; }
1070b57cec5SDimitry Andric
isStatusKnown() const1080b57cec5SDimitry Andric bool Status::isStatusKnown() const { return Type != file_type::status_error; }
1090b57cec5SDimitry Andric
exists() const1100b57cec5SDimitry Andric bool Status::exists() const {
1110b57cec5SDimitry Andric return isStatusKnown() && Type != file_type::file_not_found;
1120b57cec5SDimitry Andric }
1130b57cec5SDimitry Andric
1140b57cec5SDimitry Andric File::~File() = default;
1150b57cec5SDimitry Andric
1160b57cec5SDimitry Andric FileSystem::~FileSystem() = default;
1170b57cec5SDimitry Andric
1180b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>>
getBufferForFile(const llvm::Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)1190b57cec5SDimitry Andric FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
1200b57cec5SDimitry Andric bool RequiresNullTerminator, bool IsVolatile) {
1210b57cec5SDimitry Andric auto F = openFileForRead(Name);
1220b57cec5SDimitry Andric if (!F)
1230b57cec5SDimitry Andric return F.getError();
1240b57cec5SDimitry Andric
1250b57cec5SDimitry Andric return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
1260b57cec5SDimitry Andric }
1270b57cec5SDimitry Andric
makeAbsolute(SmallVectorImpl<char> & Path) const1280b57cec5SDimitry Andric std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1290b57cec5SDimitry Andric if (llvm::sys::path::is_absolute(Path))
1300b57cec5SDimitry Andric return {};
1310b57cec5SDimitry Andric
1320b57cec5SDimitry Andric auto WorkingDir = getCurrentWorkingDirectory();
1330b57cec5SDimitry Andric if (!WorkingDir)
1340b57cec5SDimitry Andric return WorkingDir.getError();
1350b57cec5SDimitry Andric
1360b57cec5SDimitry Andric llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
1370b57cec5SDimitry Andric return {};
1380b57cec5SDimitry Andric }
1390b57cec5SDimitry Andric
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output)1400b57cec5SDimitry Andric std::error_code FileSystem::getRealPath(const Twine &Path,
141*0fca6ea1SDimitry Andric SmallVectorImpl<char> &Output) {
1420b57cec5SDimitry Andric return errc::operation_not_permitted;
1430b57cec5SDimitry Andric }
1440b57cec5SDimitry Andric
isLocal(const Twine & Path,bool & Result)1450b57cec5SDimitry Andric std::error_code FileSystem::isLocal(const Twine &Path, bool &Result) {
1460b57cec5SDimitry Andric return errc::operation_not_permitted;
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric
exists(const Twine & Path)1490b57cec5SDimitry Andric bool FileSystem::exists(const Twine &Path) {
1500b57cec5SDimitry Andric auto Status = status(Path);
1510b57cec5SDimitry Andric return Status && Status->exists();
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric
equivalent(const Twine & A,const Twine & B)154*0fca6ea1SDimitry Andric llvm::ErrorOr<bool> FileSystem::equivalent(const Twine &A, const Twine &B) {
155*0fca6ea1SDimitry Andric auto StatusA = status(A);
156*0fca6ea1SDimitry Andric if (!StatusA)
157*0fca6ea1SDimitry Andric return StatusA.getError();
158*0fca6ea1SDimitry Andric auto StatusB = status(B);
159*0fca6ea1SDimitry Andric if (!StatusB)
160*0fca6ea1SDimitry Andric return StatusB.getError();
161*0fca6ea1SDimitry Andric return StatusA->equivalent(*StatusB);
162*0fca6ea1SDimitry Andric }
163*0fca6ea1SDimitry Andric
16481ad6265SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
dump() const16581ad6265SDimitry Andric void FileSystem::dump() const { print(dbgs(), PrintType::RecursiveContents); }
16681ad6265SDimitry Andric #endif
16781ad6265SDimitry Andric
1680b57cec5SDimitry Andric #ifndef NDEBUG
isTraversalComponent(StringRef Component)1690b57cec5SDimitry Andric static bool isTraversalComponent(StringRef Component) {
170*0fca6ea1SDimitry Andric return Component == ".." || Component == ".";
1710b57cec5SDimitry Andric }
1720b57cec5SDimitry Andric
pathHasTraversal(StringRef Path)1730b57cec5SDimitry Andric static bool pathHasTraversal(StringRef Path) {
1740b57cec5SDimitry Andric using namespace llvm::sys;
1750b57cec5SDimitry Andric
1760b57cec5SDimitry Andric for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1770b57cec5SDimitry Andric if (isTraversalComponent(Comp))
1780b57cec5SDimitry Andric return true;
1790b57cec5SDimitry Andric return false;
1800b57cec5SDimitry Andric }
1810b57cec5SDimitry Andric #endif
1820b57cec5SDimitry Andric
1830b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
1840b57cec5SDimitry Andric // RealFileSystem implementation
1850b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
1860b57cec5SDimitry Andric
1870b57cec5SDimitry Andric namespace {
1880b57cec5SDimitry Andric
1890b57cec5SDimitry Andric /// Wrapper around a raw file descriptor.
1900b57cec5SDimitry Andric class RealFile : public File {
1910b57cec5SDimitry Andric friend class RealFileSystem;
1920b57cec5SDimitry Andric
1930b57cec5SDimitry Andric file_t FD;
1940b57cec5SDimitry Andric Status S;
1950b57cec5SDimitry Andric std::string RealName;
1960b57cec5SDimitry Andric
RealFile(file_t RawFD,StringRef NewName,StringRef NewRealPathName)1978bcb0991SDimitry Andric RealFile(file_t RawFD, StringRef NewName, StringRef NewRealPathName)
1988bcb0991SDimitry Andric : FD(RawFD), S(NewName, {}, {}, {}, {}, {},
1990b57cec5SDimitry Andric llvm::sys::fs::file_type::status_error, {}),
2000b57cec5SDimitry Andric RealName(NewRealPathName.str()) {
2010b57cec5SDimitry Andric assert(FD != kInvalidFile && "Invalid or inactive file descriptor");
2020b57cec5SDimitry Andric }
2030b57cec5SDimitry Andric
2040b57cec5SDimitry Andric public:
2050b57cec5SDimitry Andric ~RealFile() override;
2060b57cec5SDimitry Andric
2070b57cec5SDimitry Andric ErrorOr<Status> status() override;
2080b57cec5SDimitry Andric ErrorOr<std::string> getName() override;
2090b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
2100b57cec5SDimitry Andric int64_t FileSize,
2110b57cec5SDimitry Andric bool RequiresNullTerminator,
2120b57cec5SDimitry Andric bool IsVolatile) override;
2130b57cec5SDimitry Andric std::error_code close() override;
214349cc55cSDimitry Andric void setPath(const Twine &Path) override;
2150b57cec5SDimitry Andric };
2160b57cec5SDimitry Andric
2170b57cec5SDimitry Andric } // namespace
2180b57cec5SDimitry Andric
~RealFile()2190b57cec5SDimitry Andric RealFile::~RealFile() { close(); }
2200b57cec5SDimitry Andric
status()2210b57cec5SDimitry Andric ErrorOr<Status> RealFile::status() {
2220b57cec5SDimitry Andric assert(FD != kInvalidFile && "cannot stat closed file");
2230b57cec5SDimitry Andric if (!S.isStatusKnown()) {
2240b57cec5SDimitry Andric file_status RealStatus;
2250b57cec5SDimitry Andric if (std::error_code EC = sys::fs::status(FD, RealStatus))
2260b57cec5SDimitry Andric return EC;
2270b57cec5SDimitry Andric S = Status::copyWithNewName(RealStatus, S.getName());
2280b57cec5SDimitry Andric }
2290b57cec5SDimitry Andric return S;
2300b57cec5SDimitry Andric }
2310b57cec5SDimitry Andric
getName()2320b57cec5SDimitry Andric ErrorOr<std::string> RealFile::getName() {
2330b57cec5SDimitry Andric return RealName.empty() ? S.getName().str() : RealName;
2340b57cec5SDimitry Andric }
2350b57cec5SDimitry Andric
2360b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>>
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)2370b57cec5SDimitry Andric RealFile::getBuffer(const Twine &Name, int64_t FileSize,
2380b57cec5SDimitry Andric bool RequiresNullTerminator, bool IsVolatile) {
2390b57cec5SDimitry Andric assert(FD != kInvalidFile && "cannot get buffer for closed file");
2400b57cec5SDimitry Andric return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
2410b57cec5SDimitry Andric IsVolatile);
2420b57cec5SDimitry Andric }
2430b57cec5SDimitry Andric
close()2440b57cec5SDimitry Andric std::error_code RealFile::close() {
2450b57cec5SDimitry Andric std::error_code EC = sys::fs::closeFile(FD);
2460b57cec5SDimitry Andric FD = kInvalidFile;
2470b57cec5SDimitry Andric return EC;
2480b57cec5SDimitry Andric }
2490b57cec5SDimitry Andric
setPath(const Twine & Path)250349cc55cSDimitry Andric void RealFile::setPath(const Twine &Path) {
251349cc55cSDimitry Andric RealName = Path.str();
252349cc55cSDimitry Andric if (auto Status = status())
253349cc55cSDimitry Andric S = Status.get().copyWithNewName(Status.get(), Path);
254349cc55cSDimitry Andric }
255349cc55cSDimitry Andric
2560b57cec5SDimitry Andric namespace {
2570b57cec5SDimitry Andric
2580b57cec5SDimitry Andric /// A file system according to your operating system.
2590b57cec5SDimitry Andric /// This may be linked to the process's working directory, or maintain its own.
2600b57cec5SDimitry Andric ///
2610b57cec5SDimitry Andric /// Currently, its own working directory is emulated by storing the path and
2620b57cec5SDimitry Andric /// sending absolute paths to llvm::sys::fs:: functions.
2630b57cec5SDimitry Andric /// A more principled approach would be to push this down a level, modelling
2640b57cec5SDimitry Andric /// the working dir as an llvm::sys::fs::WorkingDir or similar.
2650b57cec5SDimitry Andric /// This would enable the use of openat()-style functions on some platforms.
2660b57cec5SDimitry Andric class RealFileSystem : public FileSystem {
2670b57cec5SDimitry Andric public:
RealFileSystem(bool LinkCWDToProcess)2680b57cec5SDimitry Andric explicit RealFileSystem(bool LinkCWDToProcess) {
2690b57cec5SDimitry Andric if (!LinkCWDToProcess) {
2700b57cec5SDimitry Andric SmallString<128> PWD, RealPWD;
27106c3fb27SDimitry Andric if (std::error_code EC = llvm::sys::fs::current_path(PWD))
27206c3fb27SDimitry Andric WD = EC;
27306c3fb27SDimitry Andric else if (llvm::sys::fs::real_path(PWD, RealPWD))
27406c3fb27SDimitry Andric WD = WorkingDirectory{PWD, PWD};
2750b57cec5SDimitry Andric else
27606c3fb27SDimitry Andric WD = WorkingDirectory{PWD, RealPWD};
2770b57cec5SDimitry Andric }
2780b57cec5SDimitry Andric }
2790b57cec5SDimitry Andric
2800b57cec5SDimitry Andric ErrorOr<Status> status(const Twine &Path) override;
2810b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
2820b57cec5SDimitry Andric directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
2830b57cec5SDimitry Andric
2840b57cec5SDimitry Andric llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
2850b57cec5SDimitry Andric std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
2860b57cec5SDimitry Andric std::error_code isLocal(const Twine &Path, bool &Result) override;
2870b57cec5SDimitry Andric std::error_code getRealPath(const Twine &Path,
288*0fca6ea1SDimitry Andric SmallVectorImpl<char> &Output) override;
2890b57cec5SDimitry Andric
29081ad6265SDimitry Andric protected:
29181ad6265SDimitry Andric void printImpl(raw_ostream &OS, PrintType Type,
29281ad6265SDimitry Andric unsigned IndentLevel) const override;
29381ad6265SDimitry Andric
2940b57cec5SDimitry Andric private:
2950b57cec5SDimitry Andric // If this FS has its own working dir, use it to make Path absolute.
2960b57cec5SDimitry Andric // The returned twine is safe to use as long as both Storage and Path live.
adjustPath(const Twine & Path,SmallVectorImpl<char> & Storage) const2970b57cec5SDimitry Andric Twine adjustPath(const Twine &Path, SmallVectorImpl<char> &Storage) const {
29806c3fb27SDimitry Andric if (!WD || !*WD)
2990b57cec5SDimitry Andric return Path;
3000b57cec5SDimitry Andric Path.toVector(Storage);
30106c3fb27SDimitry Andric sys::fs::make_absolute(WD->get().Resolved, Storage);
3020b57cec5SDimitry Andric return Storage;
3030b57cec5SDimitry Andric }
3040b57cec5SDimitry Andric
3050b57cec5SDimitry Andric struct WorkingDirectory {
3060b57cec5SDimitry Andric // The current working directory, without symlinks resolved. (echo $PWD).
3070b57cec5SDimitry Andric SmallString<128> Specified;
3080b57cec5SDimitry Andric // The current working directory, with links resolved. (readlink .).
3090b57cec5SDimitry Andric SmallString<128> Resolved;
3100b57cec5SDimitry Andric };
31106c3fb27SDimitry Andric std::optional<llvm::ErrorOr<WorkingDirectory>> WD;
3120b57cec5SDimitry Andric };
3130b57cec5SDimitry Andric
3140b57cec5SDimitry Andric } // namespace
3150b57cec5SDimitry Andric
status(const Twine & Path)3160b57cec5SDimitry Andric ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
3170b57cec5SDimitry Andric SmallString<256> Storage;
3180b57cec5SDimitry Andric sys::fs::file_status RealStatus;
3190b57cec5SDimitry Andric if (std::error_code EC =
3200b57cec5SDimitry Andric sys::fs::status(adjustPath(Path, Storage), RealStatus))
3210b57cec5SDimitry Andric return EC;
3220b57cec5SDimitry Andric return Status::copyWithNewName(RealStatus, Path);
3230b57cec5SDimitry Andric }
3240b57cec5SDimitry Andric
3250b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Name)3260b57cec5SDimitry Andric RealFileSystem::openFileForRead(const Twine &Name) {
3270b57cec5SDimitry Andric SmallString<256> RealName, Storage;
3280b57cec5SDimitry Andric Expected<file_t> FDOrErr = sys::fs::openNativeFileForRead(
3290b57cec5SDimitry Andric adjustPath(Name, Storage), sys::fs::OF_None, &RealName);
3300b57cec5SDimitry Andric if (!FDOrErr)
3310b57cec5SDimitry Andric return errorToErrorCode(FDOrErr.takeError());
3320b57cec5SDimitry Andric return std::unique_ptr<File>(
3330b57cec5SDimitry Andric new RealFile(*FDOrErr, Name.str(), RealName.str()));
3340b57cec5SDimitry Andric }
3350b57cec5SDimitry Andric
getCurrentWorkingDirectory() const3360b57cec5SDimitry Andric llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
33706c3fb27SDimitry Andric if (WD && *WD)
3387a6dacacSDimitry Andric return std::string(WD->get().Specified);
3390b57cec5SDimitry Andric if (WD)
34006c3fb27SDimitry Andric return WD->getError();
3410b57cec5SDimitry Andric
3420b57cec5SDimitry Andric SmallString<128> Dir;
3430b57cec5SDimitry Andric if (std::error_code EC = llvm::sys::fs::current_path(Dir))
3440b57cec5SDimitry Andric return EC;
3457a6dacacSDimitry Andric return std::string(Dir);
3460b57cec5SDimitry Andric }
3470b57cec5SDimitry Andric
setCurrentWorkingDirectory(const Twine & Path)3480b57cec5SDimitry Andric std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
3490b57cec5SDimitry Andric if (!WD)
3500b57cec5SDimitry Andric return llvm::sys::fs::set_current_path(Path);
3510b57cec5SDimitry Andric
3520b57cec5SDimitry Andric SmallString<128> Absolute, Resolved, Storage;
3530b57cec5SDimitry Andric adjustPath(Path, Storage).toVector(Absolute);
3540b57cec5SDimitry Andric bool IsDir;
3550b57cec5SDimitry Andric if (auto Err = llvm::sys::fs::is_directory(Absolute, IsDir))
3560b57cec5SDimitry Andric return Err;
3570b57cec5SDimitry Andric if (!IsDir)
3580b57cec5SDimitry Andric return std::make_error_code(std::errc::not_a_directory);
3590b57cec5SDimitry Andric if (auto Err = llvm::sys::fs::real_path(Absolute, Resolved))
3600b57cec5SDimitry Andric return Err;
36106c3fb27SDimitry Andric WD = WorkingDirectory{Absolute, Resolved};
3620b57cec5SDimitry Andric return std::error_code();
3630b57cec5SDimitry Andric }
3640b57cec5SDimitry Andric
isLocal(const Twine & Path,bool & Result)3650b57cec5SDimitry Andric std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) {
3660b57cec5SDimitry Andric SmallString<256> Storage;
3670b57cec5SDimitry Andric return llvm::sys::fs::is_local(adjustPath(Path, Storage), Result);
3680b57cec5SDimitry Andric }
3690b57cec5SDimitry Andric
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output)370*0fca6ea1SDimitry Andric std::error_code RealFileSystem::getRealPath(const Twine &Path,
371*0fca6ea1SDimitry Andric SmallVectorImpl<char> &Output) {
3720b57cec5SDimitry Andric SmallString<256> Storage;
3730b57cec5SDimitry Andric return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output);
3740b57cec5SDimitry Andric }
3750b57cec5SDimitry Andric
printImpl(raw_ostream & OS,PrintType Type,unsigned IndentLevel) const37681ad6265SDimitry Andric void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type,
37781ad6265SDimitry Andric unsigned IndentLevel) const {
37881ad6265SDimitry Andric printIndent(OS, IndentLevel);
37981ad6265SDimitry Andric OS << "RealFileSystem using ";
38081ad6265SDimitry Andric if (WD)
38181ad6265SDimitry Andric OS << "own";
38281ad6265SDimitry Andric else
38381ad6265SDimitry Andric OS << "process";
38481ad6265SDimitry Andric OS << " CWD\n";
38581ad6265SDimitry Andric }
38681ad6265SDimitry Andric
getRealFileSystem()3870b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
3880b57cec5SDimitry Andric static IntrusiveRefCntPtr<FileSystem> FS(new RealFileSystem(true));
3890b57cec5SDimitry Andric return FS;
3900b57cec5SDimitry Andric }
3910b57cec5SDimitry Andric
createPhysicalFileSystem()3920b57cec5SDimitry Andric std::unique_ptr<FileSystem> vfs::createPhysicalFileSystem() {
3938bcb0991SDimitry Andric return std::make_unique<RealFileSystem>(false);
3940b57cec5SDimitry Andric }
3950b57cec5SDimitry Andric
3960b57cec5SDimitry Andric namespace {
3970b57cec5SDimitry Andric
3980b57cec5SDimitry Andric class RealFSDirIter : public llvm::vfs::detail::DirIterImpl {
3990b57cec5SDimitry Andric llvm::sys::fs::directory_iterator Iter;
4000b57cec5SDimitry Andric
4010b57cec5SDimitry Andric public:
RealFSDirIter(const Twine & Path,std::error_code & EC)4020b57cec5SDimitry Andric RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
4030b57cec5SDimitry Andric if (Iter != llvm::sys::fs::directory_iterator())
4040b57cec5SDimitry Andric CurrentEntry = directory_entry(Iter->path(), Iter->type());
4050b57cec5SDimitry Andric }
4060b57cec5SDimitry Andric
increment()4070b57cec5SDimitry Andric std::error_code increment() override {
4080b57cec5SDimitry Andric std::error_code EC;
4090b57cec5SDimitry Andric Iter.increment(EC);
4100b57cec5SDimitry Andric CurrentEntry = (Iter == llvm::sys::fs::directory_iterator())
4110b57cec5SDimitry Andric ? directory_entry()
4120b57cec5SDimitry Andric : directory_entry(Iter->path(), Iter->type());
4130b57cec5SDimitry Andric return EC;
4140b57cec5SDimitry Andric }
4150b57cec5SDimitry Andric };
4160b57cec5SDimitry Andric
4170b57cec5SDimitry Andric } // namespace
4180b57cec5SDimitry Andric
dir_begin(const Twine & Dir,std::error_code & EC)4190b57cec5SDimitry Andric directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
4200b57cec5SDimitry Andric std::error_code &EC) {
4210b57cec5SDimitry Andric SmallString<128> Storage;
4220b57cec5SDimitry Andric return directory_iterator(
4230b57cec5SDimitry Andric std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC));
4240b57cec5SDimitry Andric }
4250b57cec5SDimitry Andric
4260b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
4270b57cec5SDimitry Andric // OverlayFileSystem implementation
4280b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
4290b57cec5SDimitry Andric
OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS)4300b57cec5SDimitry Andric OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
4310b57cec5SDimitry Andric FSList.push_back(std::move(BaseFS));
4320b57cec5SDimitry Andric }
4330b57cec5SDimitry Andric
pushOverlay(IntrusiveRefCntPtr<FileSystem> FS)4340b57cec5SDimitry Andric void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
4350b57cec5SDimitry Andric FSList.push_back(FS);
4360b57cec5SDimitry Andric // Synchronize added file systems by duplicating the working directory from
4370b57cec5SDimitry Andric // the first one in the list.
4380b57cec5SDimitry Andric FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
4390b57cec5SDimitry Andric }
4400b57cec5SDimitry Andric
status(const Twine & Path)4410b57cec5SDimitry Andric ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
4420b57cec5SDimitry Andric // FIXME: handle symlinks that cross file systems
4430b57cec5SDimitry Andric for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
4440b57cec5SDimitry Andric ErrorOr<Status> Status = (*I)->status(Path);
4450b57cec5SDimitry Andric if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
4460b57cec5SDimitry Andric return Status;
4470b57cec5SDimitry Andric }
4480b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory);
4490b57cec5SDimitry Andric }
4500b57cec5SDimitry Andric
exists(const Twine & Path)451*0fca6ea1SDimitry Andric bool OverlayFileSystem::exists(const Twine &Path) {
452*0fca6ea1SDimitry Andric // FIXME: handle symlinks that cross file systems
453*0fca6ea1SDimitry Andric for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
454*0fca6ea1SDimitry Andric if ((*I)->exists(Path))
455*0fca6ea1SDimitry Andric return true;
456*0fca6ea1SDimitry Andric }
457*0fca6ea1SDimitry Andric return false;
458*0fca6ea1SDimitry Andric }
459*0fca6ea1SDimitry Andric
4600b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const llvm::Twine & Path)4610b57cec5SDimitry Andric OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
4620b57cec5SDimitry Andric // FIXME: handle symlinks that cross file systems
4630b57cec5SDimitry Andric for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
4640b57cec5SDimitry Andric auto Result = (*I)->openFileForRead(Path);
4650b57cec5SDimitry Andric if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
4660b57cec5SDimitry Andric return Result;
4670b57cec5SDimitry Andric }
4680b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory);
4690b57cec5SDimitry Andric }
4700b57cec5SDimitry Andric
4710b57cec5SDimitry Andric llvm::ErrorOr<std::string>
getCurrentWorkingDirectory() const4720b57cec5SDimitry Andric OverlayFileSystem::getCurrentWorkingDirectory() const {
4730b57cec5SDimitry Andric // All file systems are synchronized, just take the first working directory.
4740b57cec5SDimitry Andric return FSList.front()->getCurrentWorkingDirectory();
4750b57cec5SDimitry Andric }
4760b57cec5SDimitry Andric
4770b57cec5SDimitry Andric std::error_code
setCurrentWorkingDirectory(const Twine & Path)4780b57cec5SDimitry Andric OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
4790b57cec5SDimitry Andric for (auto &FS : FSList)
4800b57cec5SDimitry Andric if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
4810b57cec5SDimitry Andric return EC;
4820b57cec5SDimitry Andric return {};
4830b57cec5SDimitry Andric }
4840b57cec5SDimitry Andric
isLocal(const Twine & Path,bool & Result)4850b57cec5SDimitry Andric std::error_code OverlayFileSystem::isLocal(const Twine &Path, bool &Result) {
4860b57cec5SDimitry Andric for (auto &FS : FSList)
4870b57cec5SDimitry Andric if (FS->exists(Path))
4880b57cec5SDimitry Andric return FS->isLocal(Path, Result);
4890b57cec5SDimitry Andric return errc::no_such_file_or_directory;
4900b57cec5SDimitry Andric }
4910b57cec5SDimitry Andric
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output)492*0fca6ea1SDimitry Andric std::error_code OverlayFileSystem::getRealPath(const Twine &Path,
493*0fca6ea1SDimitry Andric SmallVectorImpl<char> &Output) {
494349cc55cSDimitry Andric for (const auto &FS : FSList)
4950b57cec5SDimitry Andric if (FS->exists(Path))
4960b57cec5SDimitry Andric return FS->getRealPath(Path, Output);
4970b57cec5SDimitry Andric return errc::no_such_file_or_directory;
4980b57cec5SDimitry Andric }
4990b57cec5SDimitry Andric
visitChildFileSystems(VisitCallbackTy Callback)500*0fca6ea1SDimitry Andric void OverlayFileSystem::visitChildFileSystems(VisitCallbackTy Callback) {
501*0fca6ea1SDimitry Andric for (IntrusiveRefCntPtr<FileSystem> FS : overlays_range()) {
502*0fca6ea1SDimitry Andric Callback(*FS);
503*0fca6ea1SDimitry Andric FS->visitChildFileSystems(Callback);
504*0fca6ea1SDimitry Andric }
505*0fca6ea1SDimitry Andric }
506*0fca6ea1SDimitry Andric
printImpl(raw_ostream & OS,PrintType Type,unsigned IndentLevel) const50781ad6265SDimitry Andric void OverlayFileSystem::printImpl(raw_ostream &OS, PrintType Type,
50881ad6265SDimitry Andric unsigned IndentLevel) const {
50981ad6265SDimitry Andric printIndent(OS, IndentLevel);
51081ad6265SDimitry Andric OS << "OverlayFileSystem\n";
51181ad6265SDimitry Andric if (Type == PrintType::Summary)
51281ad6265SDimitry Andric return;
51381ad6265SDimitry Andric
51481ad6265SDimitry Andric if (Type == PrintType::Contents)
51581ad6265SDimitry Andric Type = PrintType::Summary;
5165f757f3fSDimitry Andric for (const auto &FS : overlays_range())
51781ad6265SDimitry Andric FS->print(OS, Type, IndentLevel + 1);
51881ad6265SDimitry Andric }
51981ad6265SDimitry Andric
5200b57cec5SDimitry Andric llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;
5210b57cec5SDimitry Andric
5220b57cec5SDimitry Andric namespace {
5230b57cec5SDimitry Andric
524fe6060f1SDimitry Andric /// Combines and deduplicates directory entries across multiple file systems.
525fe6060f1SDimitry Andric class CombiningDirIterImpl : public llvm::vfs::detail::DirIterImpl {
526fe6060f1SDimitry Andric using FileSystemPtr = llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>;
527fe6060f1SDimitry Andric
52881ad6265SDimitry Andric /// Iterators to combine, processed in reverse order.
52981ad6265SDimitry Andric SmallVector<directory_iterator, 8> IterList;
53081ad6265SDimitry Andric /// The iterator currently being traversed.
5310b57cec5SDimitry Andric directory_iterator CurrentDirIter;
532fe6060f1SDimitry Andric /// The set of names already returned as entries.
5330b57cec5SDimitry Andric llvm::StringSet<> SeenNames;
5340b57cec5SDimitry Andric
53581ad6265SDimitry Andric /// Sets \c CurrentDirIter to the next iterator in the list, or leaves it as
53681ad6265SDimitry Andric /// is (at its end position) if we've already gone through them all.
incrementIter(bool IsFirstTime)53781ad6265SDimitry Andric std::error_code incrementIter(bool IsFirstTime) {
53881ad6265SDimitry Andric while (!IterList.empty()) {
53981ad6265SDimitry Andric CurrentDirIter = IterList.back();
54081ad6265SDimitry Andric IterList.pop_back();
5410b57cec5SDimitry Andric if (CurrentDirIter != directory_iterator())
5420b57cec5SDimitry Andric break; // found
5430b57cec5SDimitry Andric }
54481ad6265SDimitry Andric
54581ad6265SDimitry Andric if (IsFirstTime && CurrentDirIter == directory_iterator())
54681ad6265SDimitry Andric return errc::no_such_file_or_directory;
5470b57cec5SDimitry Andric return {};
5480b57cec5SDimitry Andric }
5490b57cec5SDimitry Andric
incrementDirIter(bool IsFirstTime)5500b57cec5SDimitry Andric std::error_code incrementDirIter(bool IsFirstTime) {
5510b57cec5SDimitry Andric assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
5520b57cec5SDimitry Andric "incrementing past end");
5530b57cec5SDimitry Andric std::error_code EC;
5540b57cec5SDimitry Andric if (!IsFirstTime)
5550b57cec5SDimitry Andric CurrentDirIter.increment(EC);
5560b57cec5SDimitry Andric if (!EC && CurrentDirIter == directory_iterator())
55781ad6265SDimitry Andric EC = incrementIter(IsFirstTime);
5580b57cec5SDimitry Andric return EC;
5590b57cec5SDimitry Andric }
5600b57cec5SDimitry Andric
incrementImpl(bool IsFirstTime)5610b57cec5SDimitry Andric std::error_code incrementImpl(bool IsFirstTime) {
5620b57cec5SDimitry Andric while (true) {
5630b57cec5SDimitry Andric std::error_code EC = incrementDirIter(IsFirstTime);
5640b57cec5SDimitry Andric if (EC || CurrentDirIter == directory_iterator()) {
5650b57cec5SDimitry Andric CurrentEntry = directory_entry();
5660b57cec5SDimitry Andric return EC;
5670b57cec5SDimitry Andric }
5680b57cec5SDimitry Andric CurrentEntry = *CurrentDirIter;
5690b57cec5SDimitry Andric StringRef Name = llvm::sys::path::filename(CurrentEntry.path());
5700b57cec5SDimitry Andric if (SeenNames.insert(Name).second)
5710b57cec5SDimitry Andric return EC; // name not seen before
5720b57cec5SDimitry Andric }
5730b57cec5SDimitry Andric llvm_unreachable("returned above");
5740b57cec5SDimitry Andric }
5750b57cec5SDimitry Andric
5760b57cec5SDimitry Andric public:
CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems,std::string Dir,std::error_code & EC)577fe6060f1SDimitry Andric CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems, std::string Dir,
57881ad6265SDimitry Andric std::error_code &EC) {
5795f757f3fSDimitry Andric for (const auto &FS : FileSystems) {
58081ad6265SDimitry Andric std::error_code FEC;
58181ad6265SDimitry Andric directory_iterator Iter = FS->dir_begin(Dir, FEC);
58281ad6265SDimitry Andric if (FEC && FEC != errc::no_such_file_or_directory) {
58381ad6265SDimitry Andric EC = FEC;
58481ad6265SDimitry Andric return;
58581ad6265SDimitry Andric }
58681ad6265SDimitry Andric if (!FEC)
58781ad6265SDimitry Andric IterList.push_back(Iter);
58881ad6265SDimitry Andric }
589fe6060f1SDimitry Andric EC = incrementImpl(true);
590fe6060f1SDimitry Andric }
591fe6060f1SDimitry Andric
CombiningDirIterImpl(ArrayRef<directory_iterator> DirIters,std::error_code & EC)59281ad6265SDimitry Andric CombiningDirIterImpl(ArrayRef<directory_iterator> DirIters,
59381ad6265SDimitry Andric std::error_code &EC)
59481ad6265SDimitry Andric : IterList(DirIters.begin(), DirIters.end()) {
5950b57cec5SDimitry Andric EC = incrementImpl(true);
5960b57cec5SDimitry Andric }
5970b57cec5SDimitry Andric
increment()5980b57cec5SDimitry Andric std::error_code increment() override { return incrementImpl(false); }
5990b57cec5SDimitry Andric };
6000b57cec5SDimitry Andric
6010b57cec5SDimitry Andric } // namespace
6020b57cec5SDimitry Andric
dir_begin(const Twine & Dir,std::error_code & EC)6030b57cec5SDimitry Andric directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
6040b57cec5SDimitry Andric std::error_code &EC) {
60581ad6265SDimitry Andric directory_iterator Combined = directory_iterator(
606fe6060f1SDimitry Andric std::make_shared<CombiningDirIterImpl>(FSList, Dir.str(), EC));
60781ad6265SDimitry Andric if (EC)
60881ad6265SDimitry Andric return {};
60981ad6265SDimitry Andric return Combined;
6100b57cec5SDimitry Andric }
6110b57cec5SDimitry Andric
anchor()6120b57cec5SDimitry Andric void ProxyFileSystem::anchor() {}
6130b57cec5SDimitry Andric
6140b57cec5SDimitry Andric namespace llvm {
6150b57cec5SDimitry Andric namespace vfs {
6160b57cec5SDimitry Andric
6170b57cec5SDimitry Andric namespace detail {
6180b57cec5SDimitry Andric
61981ad6265SDimitry Andric enum InMemoryNodeKind {
62081ad6265SDimitry Andric IME_File,
62181ad6265SDimitry Andric IME_Directory,
62281ad6265SDimitry Andric IME_HardLink,
62381ad6265SDimitry Andric IME_SymbolicLink,
62481ad6265SDimitry Andric };
6250b57cec5SDimitry Andric
6260b57cec5SDimitry Andric /// The in memory file system is a tree of Nodes. Every node can either be a
62781ad6265SDimitry Andric /// file, symlink, hardlink or a directory.
6280b57cec5SDimitry Andric class InMemoryNode {
6290b57cec5SDimitry Andric InMemoryNodeKind Kind;
6300b57cec5SDimitry Andric std::string FileName;
6310b57cec5SDimitry Andric
6320b57cec5SDimitry Andric public:
InMemoryNode(llvm::StringRef FileName,InMemoryNodeKind Kind)6330b57cec5SDimitry Andric InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind)
6345ffd83dbSDimitry Andric : Kind(Kind), FileName(std::string(llvm::sys::path::filename(FileName))) {
6355ffd83dbSDimitry Andric }
6360b57cec5SDimitry Andric virtual ~InMemoryNode() = default;
6370b57cec5SDimitry Andric
63804eeddc0SDimitry Andric /// Return the \p Status for this node. \p RequestedName should be the name
63904eeddc0SDimitry Andric /// through which the caller referred to this node. It will override
64004eeddc0SDimitry Andric /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
64104eeddc0SDimitry Andric virtual Status getStatus(const Twine &RequestedName) const = 0;
64204eeddc0SDimitry Andric
6430b57cec5SDimitry Andric /// Get the filename of this node (the name without the directory part).
getFileName() const6440b57cec5SDimitry Andric StringRef getFileName() const { return FileName; }
getKind() const6450b57cec5SDimitry Andric InMemoryNodeKind getKind() const { return Kind; }
6460b57cec5SDimitry Andric virtual std::string toString(unsigned Indent) const = 0;
6470b57cec5SDimitry Andric };
6480b57cec5SDimitry Andric
6490b57cec5SDimitry Andric class InMemoryFile : public InMemoryNode {
6500b57cec5SDimitry Andric Status Stat;
6510b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer;
6520b57cec5SDimitry Andric
6530b57cec5SDimitry Andric public:
InMemoryFile(Status Stat,std::unique_ptr<llvm::MemoryBuffer> Buffer)6540b57cec5SDimitry Andric InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
6550b57cec5SDimitry Andric : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)),
6560b57cec5SDimitry Andric Buffer(std::move(Buffer)) {}
6570b57cec5SDimitry Andric
getStatus(const Twine & RequestedName) const65804eeddc0SDimitry Andric Status getStatus(const Twine &RequestedName) const override {
6590b57cec5SDimitry Andric return Status::copyWithNewName(Stat, RequestedName);
6600b57cec5SDimitry Andric }
getBuffer() const6610b57cec5SDimitry Andric llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); }
6620b57cec5SDimitry Andric
toString(unsigned Indent) const6630b57cec5SDimitry Andric std::string toString(unsigned Indent) const override {
6640b57cec5SDimitry Andric return (std::string(Indent, ' ') + Stat.getName() + "\n").str();
6650b57cec5SDimitry Andric }
6660b57cec5SDimitry Andric
classof(const InMemoryNode * N)6670b57cec5SDimitry Andric static bool classof(const InMemoryNode *N) {
6680b57cec5SDimitry Andric return N->getKind() == IME_File;
6690b57cec5SDimitry Andric }
6700b57cec5SDimitry Andric };
6710b57cec5SDimitry Andric
6720b57cec5SDimitry Andric namespace {
6730b57cec5SDimitry Andric
6740b57cec5SDimitry Andric class InMemoryHardLink : public InMemoryNode {
6750b57cec5SDimitry Andric const InMemoryFile &ResolvedFile;
6760b57cec5SDimitry Andric
6770b57cec5SDimitry Andric public:
InMemoryHardLink(StringRef Path,const InMemoryFile & ResolvedFile)6780b57cec5SDimitry Andric InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile)
6790b57cec5SDimitry Andric : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {}
getResolvedFile() const6800b57cec5SDimitry Andric const InMemoryFile &getResolvedFile() const { return ResolvedFile; }
6810b57cec5SDimitry Andric
getStatus(const Twine & RequestedName) const68204eeddc0SDimitry Andric Status getStatus(const Twine &RequestedName) const override {
68304eeddc0SDimitry Andric return ResolvedFile.getStatus(RequestedName);
68404eeddc0SDimitry Andric }
68504eeddc0SDimitry Andric
toString(unsigned Indent) const6860b57cec5SDimitry Andric std::string toString(unsigned Indent) const override {
6870b57cec5SDimitry Andric return std::string(Indent, ' ') + "HardLink to -> " +
6880b57cec5SDimitry Andric ResolvedFile.toString(0);
6890b57cec5SDimitry Andric }
6900b57cec5SDimitry Andric
classof(const InMemoryNode * N)6910b57cec5SDimitry Andric static bool classof(const InMemoryNode *N) {
6920b57cec5SDimitry Andric return N->getKind() == IME_HardLink;
6930b57cec5SDimitry Andric }
6940b57cec5SDimitry Andric };
6950b57cec5SDimitry Andric
69681ad6265SDimitry Andric class InMemorySymbolicLink : public InMemoryNode {
69781ad6265SDimitry Andric std::string TargetPath;
69881ad6265SDimitry Andric Status Stat;
69981ad6265SDimitry Andric
70081ad6265SDimitry Andric public:
InMemorySymbolicLink(StringRef Path,StringRef TargetPath,Status Stat)70181ad6265SDimitry Andric InMemorySymbolicLink(StringRef Path, StringRef TargetPath, Status Stat)
70281ad6265SDimitry Andric : InMemoryNode(Path, IME_SymbolicLink), TargetPath(std::move(TargetPath)),
70381ad6265SDimitry Andric Stat(Stat) {}
70481ad6265SDimitry Andric
toString(unsigned Indent) const70581ad6265SDimitry Andric std::string toString(unsigned Indent) const override {
70681ad6265SDimitry Andric return std::string(Indent, ' ') + "SymbolicLink to -> " + TargetPath;
70781ad6265SDimitry Andric }
70881ad6265SDimitry Andric
getStatus(const Twine & RequestedName) const70981ad6265SDimitry Andric Status getStatus(const Twine &RequestedName) const override {
71081ad6265SDimitry Andric return Status::copyWithNewName(Stat, RequestedName);
71181ad6265SDimitry Andric }
71281ad6265SDimitry Andric
getTargetPath() const71381ad6265SDimitry Andric StringRef getTargetPath() const { return TargetPath; }
71481ad6265SDimitry Andric
classof(const InMemoryNode * N)71581ad6265SDimitry Andric static bool classof(const InMemoryNode *N) {
71681ad6265SDimitry Andric return N->getKind() == IME_SymbolicLink;
71781ad6265SDimitry Andric }
71881ad6265SDimitry Andric };
71981ad6265SDimitry Andric
7200b57cec5SDimitry Andric /// Adapt a InMemoryFile for VFS' File interface. The goal is to make
7210b57cec5SDimitry Andric /// \p InMemoryFileAdaptor mimic as much as possible the behavior of
7220b57cec5SDimitry Andric /// \p RealFile.
7230b57cec5SDimitry Andric class InMemoryFileAdaptor : public File {
7240b57cec5SDimitry Andric const InMemoryFile &Node;
7250b57cec5SDimitry Andric /// The name to use when returning a Status for this file.
7260b57cec5SDimitry Andric std::string RequestedName;
7270b57cec5SDimitry Andric
7280b57cec5SDimitry Andric public:
InMemoryFileAdaptor(const InMemoryFile & Node,std::string RequestedName)7290b57cec5SDimitry Andric explicit InMemoryFileAdaptor(const InMemoryFile &Node,
7300b57cec5SDimitry Andric std::string RequestedName)
7310b57cec5SDimitry Andric : Node(Node), RequestedName(std::move(RequestedName)) {}
7320b57cec5SDimitry Andric
status()7330b57cec5SDimitry Andric llvm::ErrorOr<Status> status() override {
7340b57cec5SDimitry Andric return Node.getStatus(RequestedName);
7350b57cec5SDimitry Andric }
7360b57cec5SDimitry Andric
7370b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)7380b57cec5SDimitry Andric getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
7390b57cec5SDimitry Andric bool IsVolatile) override {
7400b57cec5SDimitry Andric llvm::MemoryBuffer *Buf = Node.getBuffer();
7410b57cec5SDimitry Andric return llvm::MemoryBuffer::getMemBuffer(
7420b57cec5SDimitry Andric Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
7430b57cec5SDimitry Andric }
7440b57cec5SDimitry Andric
close()7450b57cec5SDimitry Andric std::error_code close() override { return {}; }
746349cc55cSDimitry Andric
setPath(const Twine & Path)747349cc55cSDimitry Andric void setPath(const Twine &Path) override { RequestedName = Path.str(); }
7480b57cec5SDimitry Andric };
7490b57cec5SDimitry Andric } // namespace
7500b57cec5SDimitry Andric
7510b57cec5SDimitry Andric class InMemoryDirectory : public InMemoryNode {
7520b57cec5SDimitry Andric Status Stat;
75306c3fb27SDimitry Andric std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
7540b57cec5SDimitry Andric
7550b57cec5SDimitry Andric public:
InMemoryDirectory(Status Stat)7560b57cec5SDimitry Andric InMemoryDirectory(Status Stat)
7570b57cec5SDimitry Andric : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {}
7580b57cec5SDimitry Andric
7590b57cec5SDimitry Andric /// Return the \p Status for this node. \p RequestedName should be the name
7600b57cec5SDimitry Andric /// through which the caller referred to this node. It will override
7610b57cec5SDimitry Andric /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
getStatus(const Twine & RequestedName) const76204eeddc0SDimitry Andric Status getStatus(const Twine &RequestedName) const override {
7630b57cec5SDimitry Andric return Status::copyWithNewName(Stat, RequestedName);
7640b57cec5SDimitry Andric }
765349cc55cSDimitry Andric
getUniqueID() const766349cc55cSDimitry Andric UniqueID getUniqueID() const { return Stat.getUniqueID(); }
767349cc55cSDimitry Andric
getChild(StringRef Name) const76881ad6265SDimitry Andric InMemoryNode *getChild(StringRef Name) const {
76906c3fb27SDimitry Andric auto I = Entries.find(Name.str());
7700b57cec5SDimitry Andric if (I != Entries.end())
7710b57cec5SDimitry Andric return I->second.get();
7720b57cec5SDimitry Andric return nullptr;
7730b57cec5SDimitry Andric }
7740b57cec5SDimitry Andric
addChild(StringRef Name,std::unique_ptr<InMemoryNode> Child)7750b57cec5SDimitry Andric InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
77606c3fb27SDimitry Andric return Entries.emplace(Name, std::move(Child)).first->second.get();
7770b57cec5SDimitry Andric }
7780b57cec5SDimitry Andric
7790b57cec5SDimitry Andric using const_iterator = decltype(Entries)::const_iterator;
7800b57cec5SDimitry Andric
begin() const7810b57cec5SDimitry Andric const_iterator begin() const { return Entries.begin(); }
end() const7820b57cec5SDimitry Andric const_iterator end() const { return Entries.end(); }
7830b57cec5SDimitry Andric
toString(unsigned Indent) const7840b57cec5SDimitry Andric std::string toString(unsigned Indent) const override {
7850b57cec5SDimitry Andric std::string Result =
7860b57cec5SDimitry Andric (std::string(Indent, ' ') + Stat.getName() + "\n").str();
7870b57cec5SDimitry Andric for (const auto &Entry : Entries)
7880b57cec5SDimitry Andric Result += Entry.second->toString(Indent + 2);
7890b57cec5SDimitry Andric return Result;
7900b57cec5SDimitry Andric }
7910b57cec5SDimitry Andric
classof(const InMemoryNode * N)7920b57cec5SDimitry Andric static bool classof(const InMemoryNode *N) {
7930b57cec5SDimitry Andric return N->getKind() == IME_Directory;
7940b57cec5SDimitry Andric }
7950b57cec5SDimitry Andric };
7960b57cec5SDimitry Andric
7970b57cec5SDimitry Andric } // namespace detail
7980b57cec5SDimitry Andric
799349cc55cSDimitry Andric // The UniqueID of in-memory files is derived from path and content.
800349cc55cSDimitry Andric // This avoids difficulties in creating exactly equivalent in-memory FSes,
801349cc55cSDimitry Andric // as often needed in multithreaded programs.
getUniqueID(hash_code Hash)802349cc55cSDimitry Andric static sys::fs::UniqueID getUniqueID(hash_code Hash) {
803349cc55cSDimitry Andric return sys::fs::UniqueID(std::numeric_limits<uint64_t>::max(),
804349cc55cSDimitry Andric uint64_t(size_t(Hash)));
805349cc55cSDimitry Andric }
getFileID(sys::fs::UniqueID Parent,llvm::StringRef Name,llvm::StringRef Contents)806349cc55cSDimitry Andric static sys::fs::UniqueID getFileID(sys::fs::UniqueID Parent,
807349cc55cSDimitry Andric llvm::StringRef Name,
808349cc55cSDimitry Andric llvm::StringRef Contents) {
809349cc55cSDimitry Andric return getUniqueID(llvm::hash_combine(Parent.getFile(), Name, Contents));
810349cc55cSDimitry Andric }
getDirectoryID(sys::fs::UniqueID Parent,llvm::StringRef Name)811349cc55cSDimitry Andric static sys::fs::UniqueID getDirectoryID(sys::fs::UniqueID Parent,
812349cc55cSDimitry Andric llvm::StringRef Name) {
813349cc55cSDimitry Andric return getUniqueID(llvm::hash_combine(Parent.getFile(), Name));
814349cc55cSDimitry Andric }
815349cc55cSDimitry Andric
makeStatus() const81604eeddc0SDimitry Andric Status detail::NewInMemoryNodeInfo::makeStatus() const {
81704eeddc0SDimitry Andric UniqueID UID =
81804eeddc0SDimitry Andric (Type == sys::fs::file_type::directory_file)
81904eeddc0SDimitry Andric ? getDirectoryID(DirUID, Name)
82004eeddc0SDimitry Andric : getFileID(DirUID, Name, Buffer ? Buffer->getBuffer() : "");
82104eeddc0SDimitry Andric
82204eeddc0SDimitry Andric return Status(Path, UID, llvm::sys::toTimePoint(ModificationTime), User,
82304eeddc0SDimitry Andric Group, Buffer ? Buffer->getBufferSize() : 0, Type, Perms);
82404eeddc0SDimitry Andric }
82504eeddc0SDimitry Andric
InMemoryFileSystem(bool UseNormalizedPaths)8260b57cec5SDimitry Andric InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
8270b57cec5SDimitry Andric : Root(new detail::InMemoryDirectory(
828349cc55cSDimitry Andric Status("", getDirectoryID(llvm::sys::fs::UniqueID(), ""),
829349cc55cSDimitry Andric llvm::sys::TimePoint<>(), 0, 0, 0,
830349cc55cSDimitry Andric llvm::sys::fs::file_type::directory_file,
8310b57cec5SDimitry Andric llvm::sys::fs::perms::all_all))),
8320b57cec5SDimitry Andric UseNormalizedPaths(UseNormalizedPaths) {}
8330b57cec5SDimitry Andric
8340b57cec5SDimitry Andric InMemoryFileSystem::~InMemoryFileSystem() = default;
8350b57cec5SDimitry Andric
toString() const8360b57cec5SDimitry Andric std::string InMemoryFileSystem::toString() const {
8370b57cec5SDimitry Andric return Root->toString(/*Indent=*/0);
8380b57cec5SDimitry Andric }
8390b57cec5SDimitry Andric
addFile(const Twine & P,time_t ModificationTime,std::unique_ptr<llvm::MemoryBuffer> Buffer,std::optional<uint32_t> User,std::optional<uint32_t> Group,std::optional<llvm::sys::fs::file_type> Type,std::optional<llvm::sys::fs::perms> Perms,MakeNodeFn MakeNode)8400b57cec5SDimitry Andric bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
8410b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer,
842bdd1243dSDimitry Andric std::optional<uint32_t> User,
843bdd1243dSDimitry Andric std::optional<uint32_t> Group,
844bdd1243dSDimitry Andric std::optional<llvm::sys::fs::file_type> Type,
845bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms,
84604eeddc0SDimitry Andric MakeNodeFn MakeNode) {
8470b57cec5SDimitry Andric SmallString<128> Path;
8480b57cec5SDimitry Andric P.toVector(Path);
8490b57cec5SDimitry Andric
8500b57cec5SDimitry Andric // Fix up relative paths. This just prepends the current working directory.
8510b57cec5SDimitry Andric std::error_code EC = makeAbsolute(Path);
8520b57cec5SDimitry Andric assert(!EC);
8530b57cec5SDimitry Andric (void)EC;
8540b57cec5SDimitry Andric
8550b57cec5SDimitry Andric if (useNormalizedPaths())
8560b57cec5SDimitry Andric llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
8570b57cec5SDimitry Andric
8580b57cec5SDimitry Andric if (Path.empty())
8590b57cec5SDimitry Andric return false;
8600b57cec5SDimitry Andric
8610b57cec5SDimitry Andric detail::InMemoryDirectory *Dir = Root.get();
8620b57cec5SDimitry Andric auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path);
86381ad6265SDimitry Andric const auto ResolvedUser = User.value_or(0);
86481ad6265SDimitry Andric const auto ResolvedGroup = Group.value_or(0);
86581ad6265SDimitry Andric const auto ResolvedType = Type.value_or(sys::fs::file_type::regular_file);
86681ad6265SDimitry Andric const auto ResolvedPerms = Perms.value_or(sys::fs::all_all);
8670b57cec5SDimitry Andric // Any intermediate directories we create should be accessible by
8680b57cec5SDimitry Andric // the owner, even if Perms says otherwise for the final path.
8690b57cec5SDimitry Andric const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all;
8700b57cec5SDimitry Andric
871*0fca6ea1SDimitry Andric StringRef Name = *I;
872*0fca6ea1SDimitry Andric while (true) {
873*0fca6ea1SDimitry Andric Name = *I;
874*0fca6ea1SDimitry Andric ++I;
875*0fca6ea1SDimitry Andric if (I == E)
876*0fca6ea1SDimitry Andric break;
877*0fca6ea1SDimitry Andric detail::InMemoryNode *Node = Dir->getChild(Name);
878*0fca6ea1SDimitry Andric if (!Node) {
879*0fca6ea1SDimitry Andric // This isn't the last element, so we create a new directory.
8800b57cec5SDimitry Andric Status Stat(
8810b57cec5SDimitry Andric StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
882349cc55cSDimitry Andric getDirectoryID(Dir->getUniqueID(), Name),
883349cc55cSDimitry Andric llvm::sys::toTimePoint(ModificationTime), ResolvedUser, ResolvedGroup,
884349cc55cSDimitry Andric 0, sys::fs::file_type::directory_file, NewDirectoryPerms);
8850b57cec5SDimitry Andric Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
8868bcb0991SDimitry Andric Name, std::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
8870b57cec5SDimitry Andric continue;
8880b57cec5SDimitry Andric }
889*0fca6ea1SDimitry Andric // Creating file under another file.
890*0fca6ea1SDimitry Andric if (!isa<detail::InMemoryDirectory>(Node))
891*0fca6ea1SDimitry Andric return false;
892*0fca6ea1SDimitry Andric Dir = cast<detail::InMemoryDirectory>(Node);
893*0fca6ea1SDimitry Andric }
894*0fca6ea1SDimitry Andric detail::InMemoryNode *Node = Dir->getChild(Name);
895*0fca6ea1SDimitry Andric if (!Node) {
896*0fca6ea1SDimitry Andric Dir->addChild(Name,
897*0fca6ea1SDimitry Andric MakeNode({Dir->getUniqueID(), Path, Name, ModificationTime,
898*0fca6ea1SDimitry Andric std::move(Buffer), ResolvedUser, ResolvedGroup,
899*0fca6ea1SDimitry Andric ResolvedType, ResolvedPerms}));
900*0fca6ea1SDimitry Andric return true;
901*0fca6ea1SDimitry Andric }
902*0fca6ea1SDimitry Andric if (isa<detail::InMemoryDirectory>(Node))
903*0fca6ea1SDimitry Andric return ResolvedType == sys::fs::file_type::directory_file;
9040b57cec5SDimitry Andric
9050b57cec5SDimitry Andric assert((isa<detail::InMemoryFile>(Node) ||
9060b57cec5SDimitry Andric isa<detail::InMemoryHardLink>(Node)) &&
9070b57cec5SDimitry Andric "Must be either file, hardlink or directory!");
9080b57cec5SDimitry Andric
9090b57cec5SDimitry Andric // Return false only if the new file is different from the existing one.
910*0fca6ea1SDimitry Andric if (auto *Link = dyn_cast<detail::InMemoryHardLink>(Node)) {
9110b57cec5SDimitry Andric return Link->getResolvedFile().getBuffer()->getBuffer() ==
9120b57cec5SDimitry Andric Buffer->getBuffer();
9130b57cec5SDimitry Andric }
9140b57cec5SDimitry Andric return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
9150b57cec5SDimitry Andric Buffer->getBuffer();
9160b57cec5SDimitry Andric }
9170b57cec5SDimitry Andric
addFile(const Twine & P,time_t ModificationTime,std::unique_ptr<llvm::MemoryBuffer> Buffer,std::optional<uint32_t> User,std::optional<uint32_t> Group,std::optional<llvm::sys::fs::file_type> Type,std::optional<llvm::sys::fs::perms> Perms)9180b57cec5SDimitry Andric bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
9190b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer,
920bdd1243dSDimitry Andric std::optional<uint32_t> User,
921bdd1243dSDimitry Andric std::optional<uint32_t> Group,
922bdd1243dSDimitry Andric std::optional<llvm::sys::fs::file_type> Type,
923bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms) {
9240b57cec5SDimitry Andric return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type,
92504eeddc0SDimitry Andric Perms,
92604eeddc0SDimitry Andric [](detail::NewInMemoryNodeInfo NNI)
92704eeddc0SDimitry Andric -> std::unique_ptr<detail::InMemoryNode> {
92804eeddc0SDimitry Andric Status Stat = NNI.makeStatus();
92904eeddc0SDimitry Andric if (Stat.getType() == sys::fs::file_type::directory_file)
93004eeddc0SDimitry Andric return std::make_unique<detail::InMemoryDirectory>(Stat);
93104eeddc0SDimitry Andric return std::make_unique<detail::InMemoryFile>(
93204eeddc0SDimitry Andric Stat, std::move(NNI.Buffer));
93304eeddc0SDimitry Andric });
9340b57cec5SDimitry Andric }
9350b57cec5SDimitry Andric
addFileNoOwn(const Twine & P,time_t ModificationTime,const llvm::MemoryBufferRef & Buffer,std::optional<uint32_t> User,std::optional<uint32_t> Group,std::optional<llvm::sys::fs::file_type> Type,std::optional<llvm::sys::fs::perms> Perms)936bdd1243dSDimitry Andric bool InMemoryFileSystem::addFileNoOwn(
937bdd1243dSDimitry Andric const Twine &P, time_t ModificationTime,
938bdd1243dSDimitry Andric const llvm::MemoryBufferRef &Buffer, std::optional<uint32_t> User,
939bdd1243dSDimitry Andric std::optional<uint32_t> Group, std::optional<llvm::sys::fs::file_type> Type,
940bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms) {
941e8d8bef9SDimitry Andric return addFile(P, ModificationTime, llvm::MemoryBuffer::getMemBuffer(Buffer),
9420b57cec5SDimitry Andric std::move(User), std::move(Group), std::move(Type),
94304eeddc0SDimitry Andric std::move(Perms),
94404eeddc0SDimitry Andric [](detail::NewInMemoryNodeInfo NNI)
94504eeddc0SDimitry Andric -> std::unique_ptr<detail::InMemoryNode> {
94604eeddc0SDimitry Andric Status Stat = NNI.makeStatus();
94704eeddc0SDimitry Andric if (Stat.getType() == sys::fs::file_type::directory_file)
94804eeddc0SDimitry Andric return std::make_unique<detail::InMemoryDirectory>(Stat);
94904eeddc0SDimitry Andric return std::make_unique<detail::InMemoryFile>(
95004eeddc0SDimitry Andric Stat, std::move(NNI.Buffer));
95104eeddc0SDimitry Andric });
9520b57cec5SDimitry Andric }
9530b57cec5SDimitry Andric
95481ad6265SDimitry Andric detail::NamedNodeOrError
lookupNode(const Twine & P,bool FollowFinalSymlink,size_t SymlinkDepth) const95581ad6265SDimitry Andric InMemoryFileSystem::lookupNode(const Twine &P, bool FollowFinalSymlink,
95681ad6265SDimitry Andric size_t SymlinkDepth) const {
9570b57cec5SDimitry Andric SmallString<128> Path;
9580b57cec5SDimitry Andric P.toVector(Path);
9590b57cec5SDimitry Andric
9600b57cec5SDimitry Andric // Fix up relative paths. This just prepends the current working directory.
96181ad6265SDimitry Andric std::error_code EC = makeAbsolute(Path);
9620b57cec5SDimitry Andric assert(!EC);
9630b57cec5SDimitry Andric (void)EC;
9640b57cec5SDimitry Andric
96581ad6265SDimitry Andric if (useNormalizedPaths())
9660b57cec5SDimitry Andric llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
9670b57cec5SDimitry Andric
96881ad6265SDimitry Andric const detail::InMemoryDirectory *Dir = Root.get();
9690b57cec5SDimitry Andric if (Path.empty())
97081ad6265SDimitry Andric return detail::NamedNodeOrError(Path, Dir);
9710b57cec5SDimitry Andric
9720b57cec5SDimitry Andric auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
9730b57cec5SDimitry Andric while (true) {
9740b57cec5SDimitry Andric detail::InMemoryNode *Node = Dir->getChild(*I);
9750b57cec5SDimitry Andric ++I;
9760b57cec5SDimitry Andric if (!Node)
9770b57cec5SDimitry Andric return errc::no_such_file_or_directory;
9780b57cec5SDimitry Andric
97981ad6265SDimitry Andric if (auto Symlink = dyn_cast<detail::InMemorySymbolicLink>(Node)) {
98081ad6265SDimitry Andric // If we're at the end of the path, and we're not following through
98181ad6265SDimitry Andric // terminal symlinks, then we're done.
98281ad6265SDimitry Andric if (I == E && !FollowFinalSymlink)
98381ad6265SDimitry Andric return detail::NamedNodeOrError(Path, Symlink);
98481ad6265SDimitry Andric
98581ad6265SDimitry Andric if (SymlinkDepth > InMemoryFileSystem::MaxSymlinkDepth)
98681ad6265SDimitry Andric return errc::no_such_file_or_directory;
98781ad6265SDimitry Andric
98881ad6265SDimitry Andric SmallString<128> TargetPath = Symlink->getTargetPath();
98981ad6265SDimitry Andric if (std::error_code EC = makeAbsolute(TargetPath))
99081ad6265SDimitry Andric return EC;
99181ad6265SDimitry Andric
99281ad6265SDimitry Andric // Keep going with the target. We always want to follow symlinks here
99381ad6265SDimitry Andric // because we're either at the end of a path that we want to follow, or
99481ad6265SDimitry Andric // not at the end of a path, in which case we need to follow the symlink
99581ad6265SDimitry Andric // regardless.
99681ad6265SDimitry Andric auto Target =
99781ad6265SDimitry Andric lookupNode(TargetPath, /*FollowFinalSymlink=*/true, SymlinkDepth + 1);
99881ad6265SDimitry Andric if (!Target || I == E)
99981ad6265SDimitry Andric return Target;
100081ad6265SDimitry Andric
100181ad6265SDimitry Andric if (!isa<detail::InMemoryDirectory>(*Target))
100281ad6265SDimitry Andric return errc::no_such_file_or_directory;
100381ad6265SDimitry Andric
100481ad6265SDimitry Andric // Otherwise, continue on the search in the symlinked directory.
100581ad6265SDimitry Andric Dir = cast<detail::InMemoryDirectory>(*Target);
100681ad6265SDimitry Andric continue;
100781ad6265SDimitry Andric }
100881ad6265SDimitry Andric
10090b57cec5SDimitry Andric // Return the file if it's at the end of the path.
10100b57cec5SDimitry Andric if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
10110b57cec5SDimitry Andric if (I == E)
101281ad6265SDimitry Andric return detail::NamedNodeOrError(Path, File);
10130b57cec5SDimitry Andric return errc::no_such_file_or_directory;
10140b57cec5SDimitry Andric }
10150b57cec5SDimitry Andric
10160b57cec5SDimitry Andric // If Node is HardLink then return the resolved file.
10170b57cec5SDimitry Andric if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) {
10180b57cec5SDimitry Andric if (I == E)
101981ad6265SDimitry Andric return detail::NamedNodeOrError(Path, &File->getResolvedFile());
10200b57cec5SDimitry Andric return errc::no_such_file_or_directory;
10210b57cec5SDimitry Andric }
10220b57cec5SDimitry Andric // Traverse directories.
10230b57cec5SDimitry Andric Dir = cast<detail::InMemoryDirectory>(Node);
10240b57cec5SDimitry Andric if (I == E)
102581ad6265SDimitry Andric return detail::NamedNodeOrError(Path, Dir);
10260b57cec5SDimitry Andric }
10270b57cec5SDimitry Andric }
10280b57cec5SDimitry Andric
addHardLink(const Twine & NewLink,const Twine & Target)102981ad6265SDimitry Andric bool InMemoryFileSystem::addHardLink(const Twine &NewLink,
103081ad6265SDimitry Andric const Twine &Target) {
103181ad6265SDimitry Andric auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
103281ad6265SDimitry Andric // Whether symlinks in the hardlink target are followed is
103381ad6265SDimitry Andric // implementation-defined in POSIX.
103481ad6265SDimitry Andric // We're following symlinks here to be consistent with macOS.
103581ad6265SDimitry Andric auto TargetNode = lookupNode(Target, /*FollowFinalSymlink=*/true);
10360b57cec5SDimitry Andric // FromPath must not have been added before. ToPath must have been added
10370b57cec5SDimitry Andric // before. Resolved ToPath must be a File.
103881ad6265SDimitry Andric if (!TargetNode || NewLinkNode || !isa<detail::InMemoryFile>(*TargetNode))
10390b57cec5SDimitry Andric return false;
1040bdd1243dSDimitry Andric return addFile(NewLink, 0, nullptr, std::nullopt, std::nullopt, std::nullopt,
1041bdd1243dSDimitry Andric std::nullopt, [&](detail::NewInMemoryNodeInfo NNI) {
104204eeddc0SDimitry Andric return std::make_unique<detail::InMemoryHardLink>(
104381ad6265SDimitry Andric NNI.Path.str(),
104481ad6265SDimitry Andric *cast<detail::InMemoryFile>(*TargetNode));
104581ad6265SDimitry Andric });
104681ad6265SDimitry Andric }
104781ad6265SDimitry Andric
addSymbolicLink(const Twine & NewLink,const Twine & Target,time_t ModificationTime,std::optional<uint32_t> User,std::optional<uint32_t> Group,std::optional<llvm::sys::fs::perms> Perms)1048bdd1243dSDimitry Andric bool InMemoryFileSystem::addSymbolicLink(
1049bdd1243dSDimitry Andric const Twine &NewLink, const Twine &Target, time_t ModificationTime,
1050bdd1243dSDimitry Andric std::optional<uint32_t> User, std::optional<uint32_t> Group,
1051bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms) {
105281ad6265SDimitry Andric auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
105381ad6265SDimitry Andric if (NewLinkNode)
105481ad6265SDimitry Andric return false;
105581ad6265SDimitry Andric
105681ad6265SDimitry Andric SmallString<128> NewLinkStr, TargetStr;
105781ad6265SDimitry Andric NewLink.toVector(NewLinkStr);
105881ad6265SDimitry Andric Target.toVector(TargetStr);
105981ad6265SDimitry Andric
106081ad6265SDimitry Andric return addFile(NewLinkStr, ModificationTime, nullptr, User, Group,
106181ad6265SDimitry Andric sys::fs::file_type::symlink_file, Perms,
106281ad6265SDimitry Andric [&](detail::NewInMemoryNodeInfo NNI) {
106381ad6265SDimitry Andric return std::make_unique<detail::InMemorySymbolicLink>(
106481ad6265SDimitry Andric NewLinkStr, TargetStr, NNI.makeStatus());
106504eeddc0SDimitry Andric });
10660b57cec5SDimitry Andric }
10670b57cec5SDimitry Andric
status(const Twine & Path)10680b57cec5SDimitry Andric llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
106981ad6265SDimitry Andric auto Node = lookupNode(Path, /*FollowFinalSymlink=*/true);
10700b57cec5SDimitry Andric if (Node)
107104eeddc0SDimitry Andric return (*Node)->getStatus(Path);
10720b57cec5SDimitry Andric return Node.getError();
10730b57cec5SDimitry Andric }
10740b57cec5SDimitry Andric
10750b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Path)10760b57cec5SDimitry Andric InMemoryFileSystem::openFileForRead(const Twine &Path) {
107781ad6265SDimitry Andric auto Node = lookupNode(Path,/*FollowFinalSymlink=*/true);
10780b57cec5SDimitry Andric if (!Node)
10790b57cec5SDimitry Andric return Node.getError();
10800b57cec5SDimitry Andric
10810b57cec5SDimitry Andric // When we have a file provide a heap-allocated wrapper for the memory buffer
10820b57cec5SDimitry Andric // to match the ownership semantics for File.
10830b57cec5SDimitry Andric if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
10840b57cec5SDimitry Andric return std::unique_ptr<File>(
10850b57cec5SDimitry Andric new detail::InMemoryFileAdaptor(*F, Path.str()));
10860b57cec5SDimitry Andric
10870b57cec5SDimitry Andric // FIXME: errc::not_a_file?
10880b57cec5SDimitry Andric return make_error_code(llvm::errc::invalid_argument);
10890b57cec5SDimitry Andric }
10900b57cec5SDimitry Andric
10910b57cec5SDimitry Andric /// Adaptor from InMemoryDir::iterator to directory_iterator.
109281ad6265SDimitry Andric class InMemoryFileSystem::DirIterator : public llvm::vfs::detail::DirIterImpl {
109381ad6265SDimitry Andric const InMemoryFileSystem *FS;
10940b57cec5SDimitry Andric detail::InMemoryDirectory::const_iterator I;
10950b57cec5SDimitry Andric detail::InMemoryDirectory::const_iterator E;
10960b57cec5SDimitry Andric std::string RequestedDirName;
10970b57cec5SDimitry Andric
setCurrentEntry()10980b57cec5SDimitry Andric void setCurrentEntry() {
10990b57cec5SDimitry Andric if (I != E) {
11000b57cec5SDimitry Andric SmallString<256> Path(RequestedDirName);
11010b57cec5SDimitry Andric llvm::sys::path::append(Path, I->second->getFileName());
1102480093f4SDimitry Andric sys::fs::file_type Type = sys::fs::file_type::type_unknown;
11030b57cec5SDimitry Andric switch (I->second->getKind()) {
11040b57cec5SDimitry Andric case detail::IME_File:
11050b57cec5SDimitry Andric case detail::IME_HardLink:
11060b57cec5SDimitry Andric Type = sys::fs::file_type::regular_file;
11070b57cec5SDimitry Andric break;
11080b57cec5SDimitry Andric case detail::IME_Directory:
11090b57cec5SDimitry Andric Type = sys::fs::file_type::directory_file;
11100b57cec5SDimitry Andric break;
111181ad6265SDimitry Andric case detail::IME_SymbolicLink:
111281ad6265SDimitry Andric if (auto SymlinkTarget =
111381ad6265SDimitry Andric FS->lookupNode(Path, /*FollowFinalSymlink=*/true)) {
111481ad6265SDimitry Andric Path = SymlinkTarget.getName();
111581ad6265SDimitry Andric Type = (*SymlinkTarget)->getStatus(Path).getType();
111681ad6265SDimitry Andric }
111781ad6265SDimitry Andric break;
11180b57cec5SDimitry Andric }
11197a6dacacSDimitry Andric CurrentEntry = directory_entry(std::string(Path), Type);
11200b57cec5SDimitry Andric } else {
11210b57cec5SDimitry Andric // When we're at the end, make CurrentEntry invalid and DirIterImpl will
11220b57cec5SDimitry Andric // do the rest.
11230b57cec5SDimitry Andric CurrentEntry = directory_entry();
11240b57cec5SDimitry Andric }
11250b57cec5SDimitry Andric }
11260b57cec5SDimitry Andric
11270b57cec5SDimitry Andric public:
112881ad6265SDimitry Andric DirIterator() = default;
11290b57cec5SDimitry Andric
DirIterator(const InMemoryFileSystem * FS,const detail::InMemoryDirectory & Dir,std::string RequestedDirName)113081ad6265SDimitry Andric DirIterator(const InMemoryFileSystem *FS,
113181ad6265SDimitry Andric const detail::InMemoryDirectory &Dir,
11320b57cec5SDimitry Andric std::string RequestedDirName)
113381ad6265SDimitry Andric : FS(FS), I(Dir.begin()), E(Dir.end()),
11340b57cec5SDimitry Andric RequestedDirName(std::move(RequestedDirName)) {
11350b57cec5SDimitry Andric setCurrentEntry();
11360b57cec5SDimitry Andric }
11370b57cec5SDimitry Andric
increment()11380b57cec5SDimitry Andric std::error_code increment() override {
11390b57cec5SDimitry Andric ++I;
11400b57cec5SDimitry Andric setCurrentEntry();
11410b57cec5SDimitry Andric return {};
11420b57cec5SDimitry Andric }
11430b57cec5SDimitry Andric };
11440b57cec5SDimitry Andric
dir_begin(const Twine & Dir,std::error_code & EC)11450b57cec5SDimitry Andric directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
11460b57cec5SDimitry Andric std::error_code &EC) {
114781ad6265SDimitry Andric auto Node = lookupNode(Dir, /*FollowFinalSymlink=*/true);
11480b57cec5SDimitry Andric if (!Node) {
11490b57cec5SDimitry Andric EC = Node.getError();
115081ad6265SDimitry Andric return directory_iterator(std::make_shared<DirIterator>());
11510b57cec5SDimitry Andric }
11520b57cec5SDimitry Andric
11530b57cec5SDimitry Andric if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
11540b57cec5SDimitry Andric return directory_iterator(
115581ad6265SDimitry Andric std::make_shared<DirIterator>(this, *DirNode, Dir.str()));
11560b57cec5SDimitry Andric
11570b57cec5SDimitry Andric EC = make_error_code(llvm::errc::not_a_directory);
115881ad6265SDimitry Andric return directory_iterator(std::make_shared<DirIterator>());
11590b57cec5SDimitry Andric }
11600b57cec5SDimitry Andric
setCurrentWorkingDirectory(const Twine & P)11610b57cec5SDimitry Andric std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) {
11620b57cec5SDimitry Andric SmallString<128> Path;
11630b57cec5SDimitry Andric P.toVector(Path);
11640b57cec5SDimitry Andric
11650b57cec5SDimitry Andric // Fix up relative paths. This just prepends the current working directory.
11660b57cec5SDimitry Andric std::error_code EC = makeAbsolute(Path);
11670b57cec5SDimitry Andric assert(!EC);
11680b57cec5SDimitry Andric (void)EC;
11690b57cec5SDimitry Andric
11700b57cec5SDimitry Andric if (useNormalizedPaths())
11710b57cec5SDimitry Andric llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
11720b57cec5SDimitry Andric
11730b57cec5SDimitry Andric if (!Path.empty())
11747a6dacacSDimitry Andric WorkingDirectory = std::string(Path);
11750b57cec5SDimitry Andric return {};
11760b57cec5SDimitry Andric }
11770b57cec5SDimitry Andric
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output)1178*0fca6ea1SDimitry Andric std::error_code InMemoryFileSystem::getRealPath(const Twine &Path,
1179*0fca6ea1SDimitry Andric SmallVectorImpl<char> &Output) {
11800b57cec5SDimitry Andric auto CWD = getCurrentWorkingDirectory();
11810b57cec5SDimitry Andric if (!CWD || CWD->empty())
11820b57cec5SDimitry Andric return errc::operation_not_permitted;
11830b57cec5SDimitry Andric Path.toVector(Output);
11840b57cec5SDimitry Andric if (auto EC = makeAbsolute(Output))
11850b57cec5SDimitry Andric return EC;
11860b57cec5SDimitry Andric llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true);
11870b57cec5SDimitry Andric return {};
11880b57cec5SDimitry Andric }
11890b57cec5SDimitry Andric
isLocal(const Twine & Path,bool & Result)11900b57cec5SDimitry Andric std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
11910b57cec5SDimitry Andric Result = false;
11920b57cec5SDimitry Andric return {};
11930b57cec5SDimitry Andric }
11940b57cec5SDimitry Andric
printImpl(raw_ostream & OS,PrintType PrintContents,unsigned IndentLevel) const119581ad6265SDimitry Andric void InMemoryFileSystem::printImpl(raw_ostream &OS, PrintType PrintContents,
119681ad6265SDimitry Andric unsigned IndentLevel) const {
119781ad6265SDimitry Andric printIndent(OS, IndentLevel);
119881ad6265SDimitry Andric OS << "InMemoryFileSystem\n";
119981ad6265SDimitry Andric }
120081ad6265SDimitry Andric
12010b57cec5SDimitry Andric } // namespace vfs
12020b57cec5SDimitry Andric } // namespace llvm
12030b57cec5SDimitry Andric
12040b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
12050b57cec5SDimitry Andric // RedirectingFileSystem implementation
12060b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
12070b57cec5SDimitry Andric
12085ffd83dbSDimitry Andric namespace {
12095ffd83dbSDimitry Andric
getExistingStyle(llvm::StringRef Path)1210fe6060f1SDimitry Andric static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) {
1211fe6060f1SDimitry Andric // Detect the path style in use by checking the first separator.
12125ffd83dbSDimitry Andric llvm::sys::path::Style style = llvm::sys::path::Style::native;
12135ffd83dbSDimitry Andric const size_t n = Path.find_first_of("/\\");
1214349cc55cSDimitry Andric // Can't distinguish between posix and windows_slash here.
12155ffd83dbSDimitry Andric if (n != static_cast<size_t>(-1))
12165ffd83dbSDimitry Andric style = (Path[n] == '/') ? llvm::sys::path::Style::posix
1217349cc55cSDimitry Andric : llvm::sys::path::Style::windows_backslash;
1218fe6060f1SDimitry Andric return style;
1219fe6060f1SDimitry Andric }
1220fe6060f1SDimitry Andric
1221fe6060f1SDimitry Andric /// Removes leading "./" as well as path components like ".." and ".".
canonicalize(llvm::StringRef Path)1222fe6060f1SDimitry Andric static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {
1223fe6060f1SDimitry Andric // First detect the path style in use by checking the first separator.
1224fe6060f1SDimitry Andric llvm::sys::path::Style style = getExistingStyle(Path);
12255ffd83dbSDimitry Andric
12265ffd83dbSDimitry Andric // Now remove the dots. Explicitly specifying the path style prevents the
12275ffd83dbSDimitry Andric // direction of the slashes from changing.
12285ffd83dbSDimitry Andric llvm::SmallString<256> result =
12295ffd83dbSDimitry Andric llvm::sys::path::remove_leading_dotslash(Path, style);
12305ffd83dbSDimitry Andric llvm::sys::path::remove_dots(result, /*remove_dot_dot=*/true, style);
12315ffd83dbSDimitry Andric return result;
12325ffd83dbSDimitry Andric }
12335ffd83dbSDimitry Andric
123481ad6265SDimitry Andric /// Whether the error and entry specify a file/directory that was not found.
isFileNotFound(std::error_code EC,RedirectingFileSystem::Entry * E=nullptr)123581ad6265SDimitry Andric static bool isFileNotFound(std::error_code EC,
123681ad6265SDimitry Andric RedirectingFileSystem::Entry *E = nullptr) {
123781ad6265SDimitry Andric if (E && !isa<RedirectingFileSystem::DirectoryRemapEntry>(E))
123881ad6265SDimitry Andric return false;
123981ad6265SDimitry Andric return EC == llvm::errc::no_such_file_or_directory;
124081ad6265SDimitry Andric }
124181ad6265SDimitry Andric
12425ffd83dbSDimitry Andric } // anonymous namespace
12435ffd83dbSDimitry Andric
12445ffd83dbSDimitry Andric
RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)12458bcb0991SDimitry Andric RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
12468bcb0991SDimitry Andric : ExternalFS(std::move(FS)) {
12478bcb0991SDimitry Andric if (ExternalFS)
12488bcb0991SDimitry Andric if (auto ExternalWorkingDirectory =
12498bcb0991SDimitry Andric ExternalFS->getCurrentWorkingDirectory()) {
12508bcb0991SDimitry Andric WorkingDirectory = *ExternalWorkingDirectory;
12518bcb0991SDimitry Andric }
12528bcb0991SDimitry Andric }
12538bcb0991SDimitry Andric
1254fe6060f1SDimitry Andric /// Directory iterator implementation for \c RedirectingFileSystem's
1255fe6060f1SDimitry Andric /// directory entries.
1256fe6060f1SDimitry Andric class llvm::vfs::RedirectingFSDirIterImpl
12570b57cec5SDimitry Andric : public llvm::vfs::detail::DirIterImpl {
12580b57cec5SDimitry Andric std::string Dir;
1259fe6060f1SDimitry Andric RedirectingFileSystem::DirectoryEntry::iterator Current, End;
12600b57cec5SDimitry Andric
incrementImpl(bool IsFirstTime)1261fe6060f1SDimitry Andric std::error_code incrementImpl(bool IsFirstTime) {
1262fe6060f1SDimitry Andric assert((IsFirstTime || Current != End) && "cannot iterate past end");
1263fe6060f1SDimitry Andric if (!IsFirstTime)
1264fe6060f1SDimitry Andric ++Current;
1265fe6060f1SDimitry Andric if (Current != End) {
1266fe6060f1SDimitry Andric SmallString<128> PathStr(Dir);
1267fe6060f1SDimitry Andric llvm::sys::path::append(PathStr, (*Current)->getName());
1268fe6060f1SDimitry Andric sys::fs::file_type Type = sys::fs::file_type::type_unknown;
1269fe6060f1SDimitry Andric switch ((*Current)->getKind()) {
1270fe6060f1SDimitry Andric case RedirectingFileSystem::EK_Directory:
1271bdd1243dSDimitry Andric [[fallthrough]];
1272fe6060f1SDimitry Andric case RedirectingFileSystem::EK_DirectoryRemap:
1273fe6060f1SDimitry Andric Type = sys::fs::file_type::directory_file;
1274fe6060f1SDimitry Andric break;
1275fe6060f1SDimitry Andric case RedirectingFileSystem::EK_File:
1276fe6060f1SDimitry Andric Type = sys::fs::file_type::regular_file;
1277fe6060f1SDimitry Andric break;
1278fe6060f1SDimitry Andric }
12797a6dacacSDimitry Andric CurrentEntry = directory_entry(std::string(PathStr), Type);
1280fe6060f1SDimitry Andric } else {
1281fe6060f1SDimitry Andric CurrentEntry = directory_entry();
1282fe6060f1SDimitry Andric }
1283fe6060f1SDimitry Andric return {};
1284fe6060f1SDimitry Andric };
12850b57cec5SDimitry Andric
12860b57cec5SDimitry Andric public:
RedirectingFSDirIterImpl(const Twine & Path,RedirectingFileSystem::DirectoryEntry::iterator Begin,RedirectingFileSystem::DirectoryEntry::iterator End,std::error_code & EC)1287fe6060f1SDimitry Andric RedirectingFSDirIterImpl(
1288fe6060f1SDimitry Andric const Twine &Path, RedirectingFileSystem::DirectoryEntry::iterator Begin,
1289fe6060f1SDimitry Andric RedirectingFileSystem::DirectoryEntry::iterator End, std::error_code &EC)
1290fe6060f1SDimitry Andric : Dir(Path.str()), Current(Begin), End(End) {
1291fe6060f1SDimitry Andric EC = incrementImpl(/*IsFirstTime=*/true);
1292fe6060f1SDimitry Andric }
12930b57cec5SDimitry Andric
increment()1294fe6060f1SDimitry Andric std::error_code increment() override {
1295fe6060f1SDimitry Andric return incrementImpl(/*IsFirstTime=*/false);
1296fe6060f1SDimitry Andric }
1297fe6060f1SDimitry Andric };
1298fe6060f1SDimitry Andric
1299349cc55cSDimitry Andric namespace {
1300fe6060f1SDimitry Andric /// Directory iterator implementation for \c RedirectingFileSystem's
1301fe6060f1SDimitry Andric /// directory remap entries that maps the paths reported by the external
1302fe6060f1SDimitry Andric /// file system's directory iterator back to the virtual directory's path.
1303fe6060f1SDimitry Andric class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl {
1304fe6060f1SDimitry Andric std::string Dir;
1305fe6060f1SDimitry Andric llvm::sys::path::Style DirStyle;
1306fe6060f1SDimitry Andric llvm::vfs::directory_iterator ExternalIter;
1307fe6060f1SDimitry Andric
1308fe6060f1SDimitry Andric public:
RedirectingFSDirRemapIterImpl(std::string DirPath,llvm::vfs::directory_iterator ExtIter)1309fe6060f1SDimitry Andric RedirectingFSDirRemapIterImpl(std::string DirPath,
1310fe6060f1SDimitry Andric llvm::vfs::directory_iterator ExtIter)
1311fe6060f1SDimitry Andric : Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)),
1312fe6060f1SDimitry Andric ExternalIter(ExtIter) {
1313fe6060f1SDimitry Andric if (ExternalIter != llvm::vfs::directory_iterator())
1314fe6060f1SDimitry Andric setCurrentEntry();
1315fe6060f1SDimitry Andric }
1316fe6060f1SDimitry Andric
setCurrentEntry()1317fe6060f1SDimitry Andric void setCurrentEntry() {
1318fe6060f1SDimitry Andric StringRef ExternalPath = ExternalIter->path();
1319fe6060f1SDimitry Andric llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath);
1320fe6060f1SDimitry Andric StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle);
1321fe6060f1SDimitry Andric
1322fe6060f1SDimitry Andric SmallString<128> NewPath(Dir);
1323fe6060f1SDimitry Andric llvm::sys::path::append(NewPath, DirStyle, File);
1324fe6060f1SDimitry Andric
1325fe6060f1SDimitry Andric CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type());
1326fe6060f1SDimitry Andric }
1327fe6060f1SDimitry Andric
increment()1328fe6060f1SDimitry Andric std::error_code increment() override {
1329fe6060f1SDimitry Andric std::error_code EC;
1330fe6060f1SDimitry Andric ExternalIter.increment(EC);
1331fe6060f1SDimitry Andric if (!EC && ExternalIter != llvm::vfs::directory_iterator())
1332fe6060f1SDimitry Andric setCurrentEntry();
1333fe6060f1SDimitry Andric else
1334fe6060f1SDimitry Andric CurrentEntry = directory_entry();
1335fe6060f1SDimitry Andric return EC;
1336fe6060f1SDimitry Andric }
13370b57cec5SDimitry Andric };
1338349cc55cSDimitry Andric } // namespace
13390b57cec5SDimitry Andric
13400b57cec5SDimitry Andric llvm::ErrorOr<std::string>
getCurrentWorkingDirectory() const13410b57cec5SDimitry Andric RedirectingFileSystem::getCurrentWorkingDirectory() const {
13428bcb0991SDimitry Andric return WorkingDirectory;
13430b57cec5SDimitry Andric }
13440b57cec5SDimitry Andric
13450b57cec5SDimitry Andric std::error_code
setCurrentWorkingDirectory(const Twine & Path)13460b57cec5SDimitry Andric RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
13478bcb0991SDimitry Andric // Don't change the working directory if the path doesn't exist.
13488bcb0991SDimitry Andric if (!exists(Path))
13498bcb0991SDimitry Andric return errc::no_such_file_or_directory;
13508bcb0991SDimitry Andric
13518bcb0991SDimitry Andric SmallString<128> AbsolutePath;
13528bcb0991SDimitry Andric Path.toVector(AbsolutePath);
13538bcb0991SDimitry Andric if (std::error_code EC = makeAbsolute(AbsolutePath))
13548bcb0991SDimitry Andric return EC;
13557a6dacacSDimitry Andric WorkingDirectory = std::string(AbsolutePath);
13568bcb0991SDimitry Andric return {};
13570b57cec5SDimitry Andric }
13580b57cec5SDimitry Andric
isLocal(const Twine & Path_,bool & Result)1359e8d8bef9SDimitry Andric std::error_code RedirectingFileSystem::isLocal(const Twine &Path_,
13600b57cec5SDimitry Andric bool &Result) {
1361e8d8bef9SDimitry Andric SmallString<256> Path;
1362e8d8bef9SDimitry Andric Path_.toVector(Path);
1363e8d8bef9SDimitry Andric
1364*0fca6ea1SDimitry Andric if (makeAbsolute(Path))
1365e8d8bef9SDimitry Andric return {};
1366e8d8bef9SDimitry Andric
13670b57cec5SDimitry Andric return ExternalFS->isLocal(Path, Result);
13680b57cec5SDimitry Andric }
13690b57cec5SDimitry Andric
makeAbsolute(SmallVectorImpl<char> & Path) const1370480093f4SDimitry Andric std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1371349cc55cSDimitry Andric // is_absolute(..., Style::windows_*) accepts paths with both slash types.
1372480093f4SDimitry Andric if (llvm::sys::path::is_absolute(Path, llvm::sys::path::Style::posix) ||
1373349cc55cSDimitry Andric llvm::sys::path::is_absolute(Path,
1374349cc55cSDimitry Andric llvm::sys::path::Style::windows_backslash))
1375bdd1243dSDimitry Andric // This covers windows absolute path with forward slash as well, as the
1376*0fca6ea1SDimitry Andric // forward slashes are treated as path separation in llvm::path
1377bdd1243dSDimitry Andric // regardless of what path::Style is used.
1378480093f4SDimitry Andric return {};
1379480093f4SDimitry Andric
1380480093f4SDimitry Andric auto WorkingDir = getCurrentWorkingDirectory();
1381480093f4SDimitry Andric if (!WorkingDir)
1382480093f4SDimitry Andric return WorkingDir.getError();
1383480093f4SDimitry Andric
1384bdd1243dSDimitry Andric return makeAbsolute(WorkingDir.get(), Path);
1385bdd1243dSDimitry Andric }
1386bdd1243dSDimitry Andric
1387bdd1243dSDimitry Andric std::error_code
makeAbsolute(StringRef WorkingDir,SmallVectorImpl<char> & Path) const1388bdd1243dSDimitry Andric RedirectingFileSystem::makeAbsolute(StringRef WorkingDir,
1389bdd1243dSDimitry Andric SmallVectorImpl<char> &Path) const {
13905ffd83dbSDimitry Andric // We can't use sys::fs::make_absolute because that assumes the path style
13915ffd83dbSDimitry Andric // is native and there is no way to override that. Since we know WorkingDir
13925ffd83dbSDimitry Andric // is absolute, we can use it to determine which style we actually have and
13935ffd83dbSDimitry Andric // append Path ourselves.
1394bdd1243dSDimitry Andric if (!WorkingDir.empty() &&
1395bdd1243dSDimitry Andric !sys::path::is_absolute(WorkingDir, sys::path::Style::posix) &&
1396bdd1243dSDimitry Andric !sys::path::is_absolute(WorkingDir,
1397bdd1243dSDimitry Andric sys::path::Style::windows_backslash)) {
1398bdd1243dSDimitry Andric return std::error_code();
1399bdd1243dSDimitry Andric }
1400349cc55cSDimitry Andric sys::path::Style style = sys::path::Style::windows_backslash;
1401bdd1243dSDimitry Andric if (sys::path::is_absolute(WorkingDir, sys::path::Style::posix)) {
14025ffd83dbSDimitry Andric style = sys::path::Style::posix;
1403349cc55cSDimitry Andric } else {
1404349cc55cSDimitry Andric // Distinguish between windows_backslash and windows_slash; getExistingStyle
1405349cc55cSDimitry Andric // returns posix for a path with windows_slash.
1406bdd1243dSDimitry Andric if (getExistingStyle(WorkingDir) != sys::path::Style::windows_backslash)
1407349cc55cSDimitry Andric style = sys::path::Style::windows_slash;
14085ffd83dbSDimitry Andric }
14095ffd83dbSDimitry Andric
1410bdd1243dSDimitry Andric std::string Result = std::string(WorkingDir);
14115ffd83dbSDimitry Andric StringRef Dir(Result);
14125f757f3fSDimitry Andric if (!Dir.ends_with(sys::path::get_separator(style))) {
14135ffd83dbSDimitry Andric Result += sys::path::get_separator(style);
14145ffd83dbSDimitry Andric }
1415bdd1243dSDimitry Andric // backslashes '\' are legit path charactors under POSIX. Windows APIs
1416bdd1243dSDimitry Andric // like CreateFile accepts forward slashes '/' as path
1417bdd1243dSDimitry Andric // separator (even when mixed with backslashes). Therefore,
1418bdd1243dSDimitry Andric // `Path` should be directly appended to `WorkingDir` without converting
1419bdd1243dSDimitry Andric // path separator.
14205ffd83dbSDimitry Andric Result.append(Path.data(), Path.size());
14215ffd83dbSDimitry Andric Path.assign(Result.begin(), Result.end());
14225ffd83dbSDimitry Andric
1423480093f4SDimitry Andric return {};
1424480093f4SDimitry Andric }
1425480093f4SDimitry Andric
dir_begin(const Twine & Dir,std::error_code & EC)14260b57cec5SDimitry Andric directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
14270b57cec5SDimitry Andric std::error_code &EC) {
1428e8d8bef9SDimitry Andric SmallString<256> Path;
1429e8d8bef9SDimitry Andric Dir.toVector(Path);
1430e8d8bef9SDimitry Andric
1431*0fca6ea1SDimitry Andric EC = makeAbsolute(Path);
1432e8d8bef9SDimitry Andric if (EC)
1433e8d8bef9SDimitry Andric return {};
1434e8d8bef9SDimitry Andric
1435fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
1436fe6060f1SDimitry Andric if (!Result) {
143781ad6265SDimitry Andric if (Redirection != RedirectKind::RedirectOnly &&
143881ad6265SDimitry Andric isFileNotFound(Result.getError()))
1439e8d8bef9SDimitry Andric return ExternalFS->dir_begin(Path, EC);
144081ad6265SDimitry Andric
144181ad6265SDimitry Andric EC = Result.getError();
14420b57cec5SDimitry Andric return {};
14430b57cec5SDimitry Andric }
1444fe6060f1SDimitry Andric
1445fe6060f1SDimitry Andric // Use status to make sure the path exists and refers to a directory.
1446349cc55cSDimitry Andric ErrorOr<Status> S = status(Path, Dir, *Result);
14470b57cec5SDimitry Andric if (!S) {
144881ad6265SDimitry Andric if (Redirection != RedirectKind::RedirectOnly &&
144981ad6265SDimitry Andric isFileNotFound(S.getError(), Result->E))
1450fe6060f1SDimitry Andric return ExternalFS->dir_begin(Dir, EC);
145181ad6265SDimitry Andric
14520b57cec5SDimitry Andric EC = S.getError();
14530b57cec5SDimitry Andric return {};
14540b57cec5SDimitry Andric }
145581ad6265SDimitry Andric
14560b57cec5SDimitry Andric if (!S->isDirectory()) {
145781ad6265SDimitry Andric EC = errc::not_a_directory;
14580b57cec5SDimitry Andric return {};
14590b57cec5SDimitry Andric }
14600b57cec5SDimitry Andric
1461fe6060f1SDimitry Andric // Create the appropriate directory iterator based on whether we found a
1462fe6060f1SDimitry Andric // DirectoryRemapEntry or DirectoryEntry.
146381ad6265SDimitry Andric directory_iterator RedirectIter;
146481ad6265SDimitry Andric std::error_code RedirectEC;
1465fe6060f1SDimitry Andric if (auto ExtRedirect = Result->getExternalRedirect()) {
1466fe6060f1SDimitry Andric auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
146781ad6265SDimitry Andric RedirectIter = ExternalFS->dir_begin(*ExtRedirect, RedirectEC);
1468fe6060f1SDimitry Andric
1469fe6060f1SDimitry Andric if (!RE->useExternalName(UseExternalNames)) {
1470fe6060f1SDimitry Andric // Update the paths in the results to use the virtual directory's path.
147181ad6265SDimitry Andric RedirectIter =
1472fe6060f1SDimitry Andric directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>(
147381ad6265SDimitry Andric std::string(Path), RedirectIter));
1474fe6060f1SDimitry Andric }
1475fe6060f1SDimitry Andric } else {
1476fe6060f1SDimitry Andric auto DE = cast<DirectoryEntry>(Result->E);
147781ad6265SDimitry Andric RedirectIter =
147881ad6265SDimitry Andric directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
147981ad6265SDimitry Andric Path, DE->contents_begin(), DE->contents_end(), RedirectEC));
1480fe6060f1SDimitry Andric }
1481fe6060f1SDimitry Andric
148281ad6265SDimitry Andric if (RedirectEC) {
148381ad6265SDimitry Andric if (RedirectEC != errc::no_such_file_or_directory) {
148481ad6265SDimitry Andric EC = RedirectEC;
148581ad6265SDimitry Andric return {};
148681ad6265SDimitry Andric }
148781ad6265SDimitry Andric RedirectIter = {};
148881ad6265SDimitry Andric }
148981ad6265SDimitry Andric
149081ad6265SDimitry Andric if (Redirection == RedirectKind::RedirectOnly) {
149181ad6265SDimitry Andric EC = RedirectEC;
149281ad6265SDimitry Andric return RedirectIter;
149381ad6265SDimitry Andric }
149481ad6265SDimitry Andric
149581ad6265SDimitry Andric std::error_code ExternalEC;
149681ad6265SDimitry Andric directory_iterator ExternalIter = ExternalFS->dir_begin(Path, ExternalEC);
149781ad6265SDimitry Andric if (ExternalEC) {
149881ad6265SDimitry Andric if (ExternalEC != errc::no_such_file_or_directory) {
149981ad6265SDimitry Andric EC = ExternalEC;
150081ad6265SDimitry Andric return {};
150181ad6265SDimitry Andric }
150281ad6265SDimitry Andric ExternalIter = {};
150381ad6265SDimitry Andric }
150481ad6265SDimitry Andric
150581ad6265SDimitry Andric SmallVector<directory_iterator, 2> Iters;
150681ad6265SDimitry Andric switch (Redirection) {
150781ad6265SDimitry Andric case RedirectKind::Fallthrough:
150881ad6265SDimitry Andric Iters.push_back(ExternalIter);
150981ad6265SDimitry Andric Iters.push_back(RedirectIter);
151081ad6265SDimitry Andric break;
151181ad6265SDimitry Andric case RedirectKind::Fallback:
151281ad6265SDimitry Andric Iters.push_back(RedirectIter);
151381ad6265SDimitry Andric Iters.push_back(ExternalIter);
151481ad6265SDimitry Andric break;
151581ad6265SDimitry Andric default:
151681ad6265SDimitry Andric llvm_unreachable("unhandled RedirectKind");
151781ad6265SDimitry Andric }
151881ad6265SDimitry Andric
151981ad6265SDimitry Andric directory_iterator Combined{
152081ad6265SDimitry Andric std::make_shared<CombiningDirIterImpl>(Iters, EC)};
152181ad6265SDimitry Andric if (EC)
152281ad6265SDimitry Andric return {};
152381ad6265SDimitry Andric return Combined;
15240b57cec5SDimitry Andric }
15250b57cec5SDimitry Andric
setOverlayFileDir(StringRef Dir)1526bdd1243dSDimitry Andric void RedirectingFileSystem::setOverlayFileDir(StringRef Dir) {
1527bdd1243dSDimitry Andric OverlayFileDir = Dir.str();
15280b57cec5SDimitry Andric }
15290b57cec5SDimitry Andric
getOverlayFileDir() const1530bdd1243dSDimitry Andric StringRef RedirectingFileSystem::getOverlayFileDir() const {
1531bdd1243dSDimitry Andric return OverlayFileDir;
15320b57cec5SDimitry Andric }
15330b57cec5SDimitry Andric
setFallthrough(bool Fallthrough)1534e8d8bef9SDimitry Andric void RedirectingFileSystem::setFallthrough(bool Fallthrough) {
153581ad6265SDimitry Andric if (Fallthrough) {
153681ad6265SDimitry Andric Redirection = RedirectingFileSystem::RedirectKind::Fallthrough;
153781ad6265SDimitry Andric } else {
153881ad6265SDimitry Andric Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly;
153981ad6265SDimitry Andric }
154081ad6265SDimitry Andric }
154181ad6265SDimitry Andric
setRedirection(RedirectingFileSystem::RedirectKind Kind)154281ad6265SDimitry Andric void RedirectingFileSystem::setRedirection(
154381ad6265SDimitry Andric RedirectingFileSystem::RedirectKind Kind) {
154481ad6265SDimitry Andric Redirection = Kind;
1545e8d8bef9SDimitry Andric }
1546e8d8bef9SDimitry Andric
getRoots() const1547e8d8bef9SDimitry Andric std::vector<StringRef> RedirectingFileSystem::getRoots() const {
1548e8d8bef9SDimitry Andric std::vector<StringRef> R;
1549bdd1243dSDimitry Andric R.reserve(Roots.size());
1550e8d8bef9SDimitry Andric for (const auto &Root : Roots)
1551e8d8bef9SDimitry Andric R.push_back(Root->getName());
1552e8d8bef9SDimitry Andric return R;
1553e8d8bef9SDimitry Andric }
1554e8d8bef9SDimitry Andric
printImpl(raw_ostream & OS,PrintType Type,unsigned IndentLevel) const155581ad6265SDimitry Andric void RedirectingFileSystem::printImpl(raw_ostream &OS, PrintType Type,
155681ad6265SDimitry Andric unsigned IndentLevel) const {
155781ad6265SDimitry Andric printIndent(OS, IndentLevel);
155881ad6265SDimitry Andric OS << "RedirectingFileSystem (UseExternalNames: "
155981ad6265SDimitry Andric << (UseExternalNames ? "true" : "false") << ")\n";
156081ad6265SDimitry Andric if (Type == PrintType::Summary)
156181ad6265SDimitry Andric return;
156281ad6265SDimitry Andric
15630b57cec5SDimitry Andric for (const auto &Root : Roots)
156481ad6265SDimitry Andric printEntry(OS, Root.get(), IndentLevel);
156581ad6265SDimitry Andric
156681ad6265SDimitry Andric printIndent(OS, IndentLevel);
156781ad6265SDimitry Andric OS << "ExternalFS:\n";
156881ad6265SDimitry Andric ExternalFS->print(OS, Type == PrintType::Contents ? PrintType::Summary : Type,
156981ad6265SDimitry Andric IndentLevel + 1);
15700b57cec5SDimitry Andric }
15710b57cec5SDimitry Andric
printEntry(raw_ostream & OS,RedirectingFileSystem::Entry * E,unsigned IndentLevel) const157281ad6265SDimitry Andric void RedirectingFileSystem::printEntry(raw_ostream &OS,
15738bcb0991SDimitry Andric RedirectingFileSystem::Entry *E,
157481ad6265SDimitry Andric unsigned IndentLevel) const {
157581ad6265SDimitry Andric printIndent(OS, IndentLevel);
157681ad6265SDimitry Andric OS << "'" << E->getName() << "'";
15770b57cec5SDimitry Andric
157881ad6265SDimitry Andric switch (E->getKind()) {
157981ad6265SDimitry Andric case EK_Directory: {
158081ad6265SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(E);
15810b57cec5SDimitry Andric
158281ad6265SDimitry Andric OS << "\n";
15830b57cec5SDimitry Andric for (std::unique_ptr<Entry> &SubEntry :
15840b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end()))
158581ad6265SDimitry Andric printEntry(OS, SubEntry.get(), IndentLevel + 1);
158681ad6265SDimitry Andric break;
158781ad6265SDimitry Andric }
158881ad6265SDimitry Andric case EK_DirectoryRemap:
158981ad6265SDimitry Andric case EK_File: {
159081ad6265SDimitry Andric auto *RE = cast<RedirectingFileSystem::RemapEntry>(E);
159181ad6265SDimitry Andric OS << " -> '" << RE->getExternalContentsPath() << "'";
159281ad6265SDimitry Andric switch (RE->getUseName()) {
159381ad6265SDimitry Andric case NK_NotSet:
159481ad6265SDimitry Andric break;
159581ad6265SDimitry Andric case NK_External:
159681ad6265SDimitry Andric OS << " (UseExternalName: true)";
159781ad6265SDimitry Andric break;
159881ad6265SDimitry Andric case NK_Virtual:
159981ad6265SDimitry Andric OS << " (UseExternalName: false)";
160081ad6265SDimitry Andric break;
160181ad6265SDimitry Andric }
160281ad6265SDimitry Andric OS << "\n";
160381ad6265SDimitry Andric break;
16040b57cec5SDimitry Andric }
16050b57cec5SDimitry Andric }
160681ad6265SDimitry Andric }
16070b57cec5SDimitry Andric
visitChildFileSystems(VisitCallbackTy Callback)1608*0fca6ea1SDimitry Andric void RedirectingFileSystem::visitChildFileSystems(VisitCallbackTy Callback) {
1609*0fca6ea1SDimitry Andric if (ExternalFS) {
1610*0fca6ea1SDimitry Andric Callback(*ExternalFS);
1611*0fca6ea1SDimitry Andric ExternalFS->visitChildFileSystems(Callback);
1612*0fca6ea1SDimitry Andric }
1613*0fca6ea1SDimitry Andric }
1614*0fca6ea1SDimitry Andric
16150b57cec5SDimitry Andric /// A helper class to hold the common YAML parsing state.
16160b57cec5SDimitry Andric class llvm::vfs::RedirectingFileSystemParser {
16170b57cec5SDimitry Andric yaml::Stream &Stream;
16180b57cec5SDimitry Andric
error(yaml::Node * N,const Twine & Msg)16190b57cec5SDimitry Andric void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); }
16200b57cec5SDimitry Andric
16210b57cec5SDimitry Andric // false on error
parseScalarString(yaml::Node * N,StringRef & Result,SmallVectorImpl<char> & Storage)16220b57cec5SDimitry Andric bool parseScalarString(yaml::Node *N, StringRef &Result,
16230b57cec5SDimitry Andric SmallVectorImpl<char> &Storage) {
16240b57cec5SDimitry Andric const auto *S = dyn_cast<yaml::ScalarNode>(N);
16250b57cec5SDimitry Andric
16260b57cec5SDimitry Andric if (!S) {
16270b57cec5SDimitry Andric error(N, "expected string");
16280b57cec5SDimitry Andric return false;
16290b57cec5SDimitry Andric }
16300b57cec5SDimitry Andric Result = S->getValue(Storage);
16310b57cec5SDimitry Andric return true;
16320b57cec5SDimitry Andric }
16330b57cec5SDimitry Andric
16340b57cec5SDimitry Andric // false on error
parseScalarBool(yaml::Node * N,bool & Result)16350b57cec5SDimitry Andric bool parseScalarBool(yaml::Node *N, bool &Result) {
16360b57cec5SDimitry Andric SmallString<5> Storage;
16370b57cec5SDimitry Andric StringRef Value;
16380b57cec5SDimitry Andric if (!parseScalarString(N, Value, Storage))
16390b57cec5SDimitry Andric return false;
16400b57cec5SDimitry Andric
1641fe6060f1SDimitry Andric if (Value.equals_insensitive("true") || Value.equals_insensitive("on") ||
1642fe6060f1SDimitry Andric Value.equals_insensitive("yes") || Value == "1") {
16430b57cec5SDimitry Andric Result = true;
16440b57cec5SDimitry Andric return true;
1645fe6060f1SDimitry Andric } else if (Value.equals_insensitive("false") ||
1646fe6060f1SDimitry Andric Value.equals_insensitive("off") ||
1647fe6060f1SDimitry Andric Value.equals_insensitive("no") || Value == "0") {
16480b57cec5SDimitry Andric Result = false;
16490b57cec5SDimitry Andric return true;
16500b57cec5SDimitry Andric }
16510b57cec5SDimitry Andric
16520b57cec5SDimitry Andric error(N, "expected boolean value");
16530b57cec5SDimitry Andric return false;
16540b57cec5SDimitry Andric }
16550b57cec5SDimitry Andric
1656bdd1243dSDimitry Andric std::optional<RedirectingFileSystem::RedirectKind>
parseRedirectKind(yaml::Node * N)165781ad6265SDimitry Andric parseRedirectKind(yaml::Node *N) {
165881ad6265SDimitry Andric SmallString<12> Storage;
165981ad6265SDimitry Andric StringRef Value;
166081ad6265SDimitry Andric if (!parseScalarString(N, Value, Storage))
1661bdd1243dSDimitry Andric return std::nullopt;
166281ad6265SDimitry Andric
166381ad6265SDimitry Andric if (Value.equals_insensitive("fallthrough")) {
166481ad6265SDimitry Andric return RedirectingFileSystem::RedirectKind::Fallthrough;
166581ad6265SDimitry Andric } else if (Value.equals_insensitive("fallback")) {
166681ad6265SDimitry Andric return RedirectingFileSystem::RedirectKind::Fallback;
166781ad6265SDimitry Andric } else if (Value.equals_insensitive("redirect-only")) {
166881ad6265SDimitry Andric return RedirectingFileSystem::RedirectKind::RedirectOnly;
166981ad6265SDimitry Andric }
1670bdd1243dSDimitry Andric return std::nullopt;
1671bdd1243dSDimitry Andric }
1672bdd1243dSDimitry Andric
1673bdd1243dSDimitry Andric std::optional<RedirectingFileSystem::RootRelativeKind>
parseRootRelativeKind(yaml::Node * N)1674bdd1243dSDimitry Andric parseRootRelativeKind(yaml::Node *N) {
1675bdd1243dSDimitry Andric SmallString<12> Storage;
1676bdd1243dSDimitry Andric StringRef Value;
1677bdd1243dSDimitry Andric if (!parseScalarString(N, Value, Storage))
1678bdd1243dSDimitry Andric return std::nullopt;
1679bdd1243dSDimitry Andric if (Value.equals_insensitive("cwd")) {
1680bdd1243dSDimitry Andric return RedirectingFileSystem::RootRelativeKind::CWD;
1681bdd1243dSDimitry Andric } else if (Value.equals_insensitive("overlay-dir")) {
1682bdd1243dSDimitry Andric return RedirectingFileSystem::RootRelativeKind::OverlayDir;
1683bdd1243dSDimitry Andric }
1684bdd1243dSDimitry Andric return std::nullopt;
168581ad6265SDimitry Andric }
168681ad6265SDimitry Andric
16870b57cec5SDimitry Andric struct KeyStatus {
16880b57cec5SDimitry Andric bool Required;
16890b57cec5SDimitry Andric bool Seen = false;
16900b57cec5SDimitry Andric
KeyStatusllvm::vfs::RedirectingFileSystemParser::KeyStatus16910b57cec5SDimitry Andric KeyStatus(bool Required = false) : Required(Required) {}
16920b57cec5SDimitry Andric };
16930b57cec5SDimitry Andric
16940b57cec5SDimitry Andric using KeyStatusPair = std::pair<StringRef, KeyStatus>;
16950b57cec5SDimitry Andric
16960b57cec5SDimitry Andric // false on error
checkDuplicateOrUnknownKey(yaml::Node * KeyNode,StringRef Key,DenseMap<StringRef,KeyStatus> & Keys)16970b57cec5SDimitry Andric bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
16980b57cec5SDimitry Andric DenseMap<StringRef, KeyStatus> &Keys) {
16990b57cec5SDimitry Andric if (!Keys.count(Key)) {
17000b57cec5SDimitry Andric error(KeyNode, "unknown key");
17010b57cec5SDimitry Andric return false;
17020b57cec5SDimitry Andric }
17030b57cec5SDimitry Andric KeyStatus &S = Keys[Key];
17040b57cec5SDimitry Andric if (S.Seen) {
17050b57cec5SDimitry Andric error(KeyNode, Twine("duplicate key '") + Key + "'");
17060b57cec5SDimitry Andric return false;
17070b57cec5SDimitry Andric }
17080b57cec5SDimitry Andric S.Seen = true;
17090b57cec5SDimitry Andric return true;
17100b57cec5SDimitry Andric }
17110b57cec5SDimitry Andric
17120b57cec5SDimitry Andric // false on error
checkMissingKeys(yaml::Node * Obj,DenseMap<StringRef,KeyStatus> & Keys)17130b57cec5SDimitry Andric bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
17140b57cec5SDimitry Andric for (const auto &I : Keys) {
17150b57cec5SDimitry Andric if (I.second.Required && !I.second.Seen) {
17160b57cec5SDimitry Andric error(Obj, Twine("missing key '") + I.first + "'");
17170b57cec5SDimitry Andric return false;
17180b57cec5SDimitry Andric }
17190b57cec5SDimitry Andric }
17200b57cec5SDimitry Andric return true;
17210b57cec5SDimitry Andric }
17220b57cec5SDimitry Andric
1723e8d8bef9SDimitry Andric public:
1724e8d8bef9SDimitry Andric static RedirectingFileSystem::Entry *
lookupOrCreateEntry(RedirectingFileSystem * FS,StringRef Name,RedirectingFileSystem::Entry * ParentEntry=nullptr)17250b57cec5SDimitry Andric lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,
17260b57cec5SDimitry Andric RedirectingFileSystem::Entry *ParentEntry = nullptr) {
17270b57cec5SDimitry Andric if (!ParentEntry) { // Look for a existent root
17280b57cec5SDimitry Andric for (const auto &Root : FS->Roots) {
1729*0fca6ea1SDimitry Andric if (Name == Root->getName()) {
17300b57cec5SDimitry Andric ParentEntry = Root.get();
17310b57cec5SDimitry Andric return ParentEntry;
17320b57cec5SDimitry Andric }
17330b57cec5SDimitry Andric }
17340b57cec5SDimitry Andric } else { // Advance to the next component
1735fe6060f1SDimitry Andric auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
17360b57cec5SDimitry Andric for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :
17370b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) {
17380b57cec5SDimitry Andric auto *DirContent =
1739fe6060f1SDimitry Andric dyn_cast<RedirectingFileSystem::DirectoryEntry>(Content.get());
1740*0fca6ea1SDimitry Andric if (DirContent && Name == Content->getName())
17410b57cec5SDimitry Andric return DirContent;
17420b57cec5SDimitry Andric }
17430b57cec5SDimitry Andric }
17440b57cec5SDimitry Andric
17450b57cec5SDimitry Andric // ... or create a new one
17460b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem::Entry> E =
1747fe6060f1SDimitry Andric std::make_unique<RedirectingFileSystem::DirectoryEntry>(
17480b57cec5SDimitry Andric Name, Status("", getNextVirtualUniqueID(),
17490b57cec5SDimitry Andric std::chrono::system_clock::now(), 0, 0, 0,
17500b57cec5SDimitry Andric file_type::directory_file, sys::fs::all_all));
17510b57cec5SDimitry Andric
17520b57cec5SDimitry Andric if (!ParentEntry) { // Add a new root to the overlay
17530b57cec5SDimitry Andric FS->Roots.push_back(std::move(E));
17540b57cec5SDimitry Andric ParentEntry = FS->Roots.back().get();
17550b57cec5SDimitry Andric return ParentEntry;
17560b57cec5SDimitry Andric }
17570b57cec5SDimitry Andric
1758fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
17590b57cec5SDimitry Andric DE->addContent(std::move(E));
17600b57cec5SDimitry Andric return DE->getLastContent();
17610b57cec5SDimitry Andric }
17620b57cec5SDimitry Andric
1763e8d8bef9SDimitry Andric private:
uniqueOverlayTree(RedirectingFileSystem * FS,RedirectingFileSystem::Entry * SrcE,RedirectingFileSystem::Entry * NewParentE=nullptr)17640b57cec5SDimitry Andric void uniqueOverlayTree(RedirectingFileSystem *FS,
17650b57cec5SDimitry Andric RedirectingFileSystem::Entry *SrcE,
17660b57cec5SDimitry Andric RedirectingFileSystem::Entry *NewParentE = nullptr) {
17670b57cec5SDimitry Andric StringRef Name = SrcE->getName();
17680b57cec5SDimitry Andric switch (SrcE->getKind()) {
17690b57cec5SDimitry Andric case RedirectingFileSystem::EK_Directory: {
1770fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
17710b57cec5SDimitry Andric // Empty directories could be present in the YAML as a way to
17720b57cec5SDimitry Andric // describe a file for a current directory after some of its subdir
17730b57cec5SDimitry Andric // is parsed. This only leads to redundant walks, ignore it.
17740b57cec5SDimitry Andric if (!Name.empty())
17750b57cec5SDimitry Andric NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
17760b57cec5SDimitry Andric for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
17770b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end()))
17780b57cec5SDimitry Andric uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
17790b57cec5SDimitry Andric break;
17800b57cec5SDimitry Andric }
1781fe6060f1SDimitry Andric case RedirectingFileSystem::EK_DirectoryRemap: {
1782fe6060f1SDimitry Andric assert(NewParentE && "Parent entry must exist");
1783fe6060f1SDimitry Andric auto *DR = cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
1784fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1785fe6060f1SDimitry Andric DE->addContent(
1786fe6060f1SDimitry Andric std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
1787fe6060f1SDimitry Andric Name, DR->getExternalContentsPath(), DR->getUseName()));
1788fe6060f1SDimitry Andric break;
1789fe6060f1SDimitry Andric }
17900b57cec5SDimitry Andric case RedirectingFileSystem::EK_File: {
17910b57cec5SDimitry Andric assert(NewParentE && "Parent entry must exist");
1792fe6060f1SDimitry Andric auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);
1793fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1794fe6060f1SDimitry Andric DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>(
17950b57cec5SDimitry Andric Name, FE->getExternalContentsPath(), FE->getUseName()));
17960b57cec5SDimitry Andric break;
17970b57cec5SDimitry Andric }
17980b57cec5SDimitry Andric }
17990b57cec5SDimitry Andric }
18000b57cec5SDimitry Andric
18010b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem::Entry>
parseEntry(yaml::Node * N,RedirectingFileSystem * FS,bool IsRootEntry)18020b57cec5SDimitry Andric parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) {
18030b57cec5SDimitry Andric auto *M = dyn_cast<yaml::MappingNode>(N);
18040b57cec5SDimitry Andric if (!M) {
18050b57cec5SDimitry Andric error(N, "expected mapping node for file or directory entry");
18060b57cec5SDimitry Andric return nullptr;
18070b57cec5SDimitry Andric }
18080b57cec5SDimitry Andric
18090b57cec5SDimitry Andric KeyStatusPair Fields[] = {
18100b57cec5SDimitry Andric KeyStatusPair("name", true),
18110b57cec5SDimitry Andric KeyStatusPair("type", true),
18120b57cec5SDimitry Andric KeyStatusPair("contents", false),
18130b57cec5SDimitry Andric KeyStatusPair("external-contents", false),
18140b57cec5SDimitry Andric KeyStatusPair("use-external-name", false),
18150b57cec5SDimitry Andric };
18160b57cec5SDimitry Andric
18170b57cec5SDimitry Andric DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
18180b57cec5SDimitry Andric
1819fe6060f1SDimitry Andric enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;
18200b57cec5SDimitry Andric std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
18210b57cec5SDimitry Andric EntryArrayContents;
18225ffd83dbSDimitry Andric SmallString<256> ExternalContentsPath;
18235ffd83dbSDimitry Andric SmallString<256> Name;
18240b57cec5SDimitry Andric yaml::Node *NameValueNode = nullptr;
1825fe6060f1SDimitry Andric auto UseExternalName = RedirectingFileSystem::NK_NotSet;
18260b57cec5SDimitry Andric RedirectingFileSystem::EntryKind Kind;
18270b57cec5SDimitry Andric
18280b57cec5SDimitry Andric for (auto &I : *M) {
18290b57cec5SDimitry Andric StringRef Key;
18300b57cec5SDimitry Andric // Reuse the buffer for key and value, since we don't look at key after
18310b57cec5SDimitry Andric // parsing value.
18320b57cec5SDimitry Andric SmallString<256> Buffer;
18330b57cec5SDimitry Andric if (!parseScalarString(I.getKey(), Key, Buffer))
18340b57cec5SDimitry Andric return nullptr;
18350b57cec5SDimitry Andric
18360b57cec5SDimitry Andric if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
18370b57cec5SDimitry Andric return nullptr;
18380b57cec5SDimitry Andric
18390b57cec5SDimitry Andric StringRef Value;
18400b57cec5SDimitry Andric if (Key == "name") {
18410b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), Value, Buffer))
18420b57cec5SDimitry Andric return nullptr;
18430b57cec5SDimitry Andric
18440b57cec5SDimitry Andric NameValueNode = I.getValue();
18450b57cec5SDimitry Andric // Guarantee that old YAML files containing paths with ".." and "."
18460b57cec5SDimitry Andric // are properly canonicalized before read into the VFS.
18475ffd83dbSDimitry Andric Name = canonicalize(Value).str();
18480b57cec5SDimitry Andric } else if (Key == "type") {
18490b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), Value, Buffer))
18500b57cec5SDimitry Andric return nullptr;
18510b57cec5SDimitry Andric if (Value == "file")
18520b57cec5SDimitry Andric Kind = RedirectingFileSystem::EK_File;
18530b57cec5SDimitry Andric else if (Value == "directory")
18540b57cec5SDimitry Andric Kind = RedirectingFileSystem::EK_Directory;
1855fe6060f1SDimitry Andric else if (Value == "directory-remap")
1856fe6060f1SDimitry Andric Kind = RedirectingFileSystem::EK_DirectoryRemap;
18570b57cec5SDimitry Andric else {
18580b57cec5SDimitry Andric error(I.getValue(), "unknown value for 'type'");
18590b57cec5SDimitry Andric return nullptr;
18600b57cec5SDimitry Andric }
18610b57cec5SDimitry Andric } else if (Key == "contents") {
1862fe6060f1SDimitry Andric if (ContentsField != CF_NotSet) {
18630b57cec5SDimitry Andric error(I.getKey(),
18640b57cec5SDimitry Andric "entry already has 'contents' or 'external-contents'");
18650b57cec5SDimitry Andric return nullptr;
18660b57cec5SDimitry Andric }
1867fe6060f1SDimitry Andric ContentsField = CF_List;
18680b57cec5SDimitry Andric auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
18690b57cec5SDimitry Andric if (!Contents) {
18700b57cec5SDimitry Andric // FIXME: this is only for directories, what about files?
18710b57cec5SDimitry Andric error(I.getValue(), "expected array");
18720b57cec5SDimitry Andric return nullptr;
18730b57cec5SDimitry Andric }
18740b57cec5SDimitry Andric
18750b57cec5SDimitry Andric for (auto &I : *Contents) {
18760b57cec5SDimitry Andric if (std::unique_ptr<RedirectingFileSystem::Entry> E =
18770b57cec5SDimitry Andric parseEntry(&I, FS, /*IsRootEntry*/ false))
18780b57cec5SDimitry Andric EntryArrayContents.push_back(std::move(E));
18790b57cec5SDimitry Andric else
18800b57cec5SDimitry Andric return nullptr;
18810b57cec5SDimitry Andric }
18820b57cec5SDimitry Andric } else if (Key == "external-contents") {
1883fe6060f1SDimitry Andric if (ContentsField != CF_NotSet) {
18840b57cec5SDimitry Andric error(I.getKey(),
18850b57cec5SDimitry Andric "entry already has 'contents' or 'external-contents'");
18860b57cec5SDimitry Andric return nullptr;
18870b57cec5SDimitry Andric }
1888fe6060f1SDimitry Andric ContentsField = CF_External;
18890b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), Value, Buffer))
18900b57cec5SDimitry Andric return nullptr;
18910b57cec5SDimitry Andric
18920b57cec5SDimitry Andric SmallString<256> FullPath;
18930b57cec5SDimitry Andric if (FS->IsRelativeOverlay) {
1894bdd1243dSDimitry Andric FullPath = FS->getOverlayFileDir();
18950b57cec5SDimitry Andric assert(!FullPath.empty() &&
18960b57cec5SDimitry Andric "External contents prefix directory must exist");
18970b57cec5SDimitry Andric llvm::sys::path::append(FullPath, Value);
18980b57cec5SDimitry Andric } else {
18990b57cec5SDimitry Andric FullPath = Value;
19000b57cec5SDimitry Andric }
19010b57cec5SDimitry Andric
19020b57cec5SDimitry Andric // Guarantee that old YAML files containing paths with ".." and "."
19030b57cec5SDimitry Andric // are properly canonicalized before read into the VFS.
19045ffd83dbSDimitry Andric FullPath = canonicalize(FullPath);
19050b57cec5SDimitry Andric ExternalContentsPath = FullPath.str();
19060b57cec5SDimitry Andric } else if (Key == "use-external-name") {
19070b57cec5SDimitry Andric bool Val;
19080b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), Val))
19090b57cec5SDimitry Andric return nullptr;
1910fe6060f1SDimitry Andric UseExternalName = Val ? RedirectingFileSystem::NK_External
1911fe6060f1SDimitry Andric : RedirectingFileSystem::NK_Virtual;
19120b57cec5SDimitry Andric } else {
19130b57cec5SDimitry Andric llvm_unreachable("key missing from Keys");
19140b57cec5SDimitry Andric }
19150b57cec5SDimitry Andric }
19160b57cec5SDimitry Andric
19170b57cec5SDimitry Andric if (Stream.failed())
19180b57cec5SDimitry Andric return nullptr;
19190b57cec5SDimitry Andric
19200b57cec5SDimitry Andric // check for missing keys
1921fe6060f1SDimitry Andric if (ContentsField == CF_NotSet) {
19220b57cec5SDimitry Andric error(N, "missing key 'contents' or 'external-contents'");
19230b57cec5SDimitry Andric return nullptr;
19240b57cec5SDimitry Andric }
19250b57cec5SDimitry Andric if (!checkMissingKeys(N, Keys))
19260b57cec5SDimitry Andric return nullptr;
19270b57cec5SDimitry Andric
19280b57cec5SDimitry Andric // check invalid configuration
19290b57cec5SDimitry Andric if (Kind == RedirectingFileSystem::EK_Directory &&
1930fe6060f1SDimitry Andric UseExternalName != RedirectingFileSystem::NK_NotSet) {
1931fe6060f1SDimitry Andric error(N, "'use-external-name' is not supported for 'directory' entries");
1932fe6060f1SDimitry Andric return nullptr;
1933fe6060f1SDimitry Andric }
1934fe6060f1SDimitry Andric
1935fe6060f1SDimitry Andric if (Kind == RedirectingFileSystem::EK_DirectoryRemap &&
1936fe6060f1SDimitry Andric ContentsField == CF_List) {
1937fe6060f1SDimitry Andric error(N, "'contents' is not supported for 'directory-remap' entries");
19380b57cec5SDimitry Andric return nullptr;
19390b57cec5SDimitry Andric }
19400b57cec5SDimitry Andric
1941480093f4SDimitry Andric sys::path::Style path_style = sys::path::Style::native;
1942480093f4SDimitry Andric if (IsRootEntry) {
1943480093f4SDimitry Andric // VFS root entries may be in either Posix or Windows style. Figure out
1944480093f4SDimitry Andric // which style we have, and use it consistently.
1945480093f4SDimitry Andric if (sys::path::is_absolute(Name, sys::path::Style::posix)) {
1946480093f4SDimitry Andric path_style = sys::path::Style::posix;
1947349cc55cSDimitry Andric } else if (sys::path::is_absolute(Name,
1948349cc55cSDimitry Andric sys::path::Style::windows_backslash)) {
1949349cc55cSDimitry Andric path_style = sys::path::Style::windows_backslash;
1950480093f4SDimitry Andric } else {
1951bdd1243dSDimitry Andric // Relative VFS root entries are made absolute to either the overlay
1952bdd1243dSDimitry Andric // directory, or the current working directory, then we can determine
1953bdd1243dSDimitry Andric // the path style from that.
1954bdd1243dSDimitry Andric std::error_code EC;
1955bdd1243dSDimitry Andric if (FS->RootRelative ==
1956bdd1243dSDimitry Andric RedirectingFileSystem::RootRelativeKind::OverlayDir) {
1957bdd1243dSDimitry Andric StringRef FullPath = FS->getOverlayFileDir();
1958bdd1243dSDimitry Andric assert(!FullPath.empty() && "Overlay file directory must exist");
1959bdd1243dSDimitry Andric EC = FS->makeAbsolute(FullPath, Name);
1960bdd1243dSDimitry Andric Name = canonicalize(Name);
1961bdd1243dSDimitry Andric } else {
1962bdd1243dSDimitry Andric EC = sys::fs::make_absolute(Name);
1963bdd1243dSDimitry Andric }
196404eeddc0SDimitry Andric if (EC) {
19650b57cec5SDimitry Andric assert(NameValueNode && "Name presence should be checked earlier");
196604eeddc0SDimitry Andric error(
196704eeddc0SDimitry Andric NameValueNode,
19680b57cec5SDimitry Andric "entry with relative path at the root level is not discoverable");
19690b57cec5SDimitry Andric return nullptr;
19700b57cec5SDimitry Andric }
197104eeddc0SDimitry Andric path_style = sys::path::is_absolute(Name, sys::path::Style::posix)
197204eeddc0SDimitry Andric ? sys::path::Style::posix
197304eeddc0SDimitry Andric : sys::path::Style::windows_backslash;
197404eeddc0SDimitry Andric }
1975bdd1243dSDimitry Andric // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will
1976bdd1243dSDimitry Andric // return true even if `Name` is using forward slashes. Distinguish
1977bdd1243dSDimitry Andric // between windows_backslash and windows_slash.
1978bdd1243dSDimitry Andric if (path_style == sys::path::Style::windows_backslash &&
1979bdd1243dSDimitry Andric getExistingStyle(Name) != sys::path::Style::windows_backslash)
1980bdd1243dSDimitry Andric path_style = sys::path::Style::windows_slash;
1981480093f4SDimitry Andric }
19820b57cec5SDimitry Andric
19830b57cec5SDimitry Andric // Remove trailing slash(es), being careful not to remove the root path
1984fe6060f1SDimitry Andric StringRef Trimmed = Name;
1985480093f4SDimitry Andric size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();
19860b57cec5SDimitry Andric while (Trimmed.size() > RootPathLen &&
1987480093f4SDimitry Andric sys::path::is_separator(Trimmed.back(), path_style))
19880b57cec5SDimitry Andric Trimmed = Trimmed.slice(0, Trimmed.size() - 1);
1989480093f4SDimitry Andric
19900b57cec5SDimitry Andric // Get the last component
1991480093f4SDimitry Andric StringRef LastComponent = sys::path::filename(Trimmed, path_style);
19920b57cec5SDimitry Andric
19930b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem::Entry> Result;
19940b57cec5SDimitry Andric switch (Kind) {
19950b57cec5SDimitry Andric case RedirectingFileSystem::EK_File:
1996fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::FileEntry>(
1997fe6060f1SDimitry Andric LastComponent, std::move(ExternalContentsPath), UseExternalName);
1998fe6060f1SDimitry Andric break;
1999fe6060f1SDimitry Andric case RedirectingFileSystem::EK_DirectoryRemap:
2000fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
20010b57cec5SDimitry Andric LastComponent, std::move(ExternalContentsPath), UseExternalName);
20020b57cec5SDimitry Andric break;
20030b57cec5SDimitry Andric case RedirectingFileSystem::EK_Directory:
2004fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
20050b57cec5SDimitry Andric LastComponent, std::move(EntryArrayContents),
2006fe6060f1SDimitry Andric Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2007fe6060f1SDimitry Andric 0, 0, 0, file_type::directory_file, sys::fs::all_all));
20080b57cec5SDimitry Andric break;
20090b57cec5SDimitry Andric }
20100b57cec5SDimitry Andric
2011480093f4SDimitry Andric StringRef Parent = sys::path::parent_path(Trimmed, path_style);
20120b57cec5SDimitry Andric if (Parent.empty())
20130b57cec5SDimitry Andric return Result;
20140b57cec5SDimitry Andric
20150b57cec5SDimitry Andric // if 'name' contains multiple components, create implicit directory entries
2016480093f4SDimitry Andric for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style),
20170b57cec5SDimitry Andric E = sys::path::rend(Parent);
20180b57cec5SDimitry Andric I != E; ++I) {
20190b57cec5SDimitry Andric std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;
20200b57cec5SDimitry Andric Entries.push_back(std::move(Result));
2021fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
20220b57cec5SDimitry Andric *I, std::move(Entries),
2023fe6060f1SDimitry Andric Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
2024fe6060f1SDimitry Andric 0, 0, 0, file_type::directory_file, sys::fs::all_all));
20250b57cec5SDimitry Andric }
20260b57cec5SDimitry Andric return Result;
20270b57cec5SDimitry Andric }
20280b57cec5SDimitry Andric
20290b57cec5SDimitry Andric public:
RedirectingFileSystemParser(yaml::Stream & S)20300b57cec5SDimitry Andric RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
20310b57cec5SDimitry Andric
20320b57cec5SDimitry Andric // false on error
parse(yaml::Node * Root,RedirectingFileSystem * FS)20330b57cec5SDimitry Andric bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
20340b57cec5SDimitry Andric auto *Top = dyn_cast<yaml::MappingNode>(Root);
20350b57cec5SDimitry Andric if (!Top) {
20360b57cec5SDimitry Andric error(Root, "expected mapping node");
20370b57cec5SDimitry Andric return false;
20380b57cec5SDimitry Andric }
20390b57cec5SDimitry Andric
20400b57cec5SDimitry Andric KeyStatusPair Fields[] = {
20410b57cec5SDimitry Andric KeyStatusPair("version", true),
20420b57cec5SDimitry Andric KeyStatusPair("case-sensitive", false),
20430b57cec5SDimitry Andric KeyStatusPair("use-external-names", false),
2044bdd1243dSDimitry Andric KeyStatusPair("root-relative", false),
20450b57cec5SDimitry Andric KeyStatusPair("overlay-relative", false),
20460b57cec5SDimitry Andric KeyStatusPair("fallthrough", false),
204781ad6265SDimitry Andric KeyStatusPair("redirecting-with", false),
20480b57cec5SDimitry Andric KeyStatusPair("roots", true),
20490b57cec5SDimitry Andric };
20500b57cec5SDimitry Andric
20510b57cec5SDimitry Andric DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
20520b57cec5SDimitry Andric std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries;
20530b57cec5SDimitry Andric
20540b57cec5SDimitry Andric // Parse configuration and 'roots'
20550b57cec5SDimitry Andric for (auto &I : *Top) {
20560b57cec5SDimitry Andric SmallString<10> KeyBuffer;
20570b57cec5SDimitry Andric StringRef Key;
20580b57cec5SDimitry Andric if (!parseScalarString(I.getKey(), Key, KeyBuffer))
20590b57cec5SDimitry Andric return false;
20600b57cec5SDimitry Andric
20610b57cec5SDimitry Andric if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
20620b57cec5SDimitry Andric return false;
20630b57cec5SDimitry Andric
20640b57cec5SDimitry Andric if (Key == "roots") {
20650b57cec5SDimitry Andric auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue());
20660b57cec5SDimitry Andric if (!Roots) {
20670b57cec5SDimitry Andric error(I.getValue(), "expected array");
20680b57cec5SDimitry Andric return false;
20690b57cec5SDimitry Andric }
20700b57cec5SDimitry Andric
20710b57cec5SDimitry Andric for (auto &I : *Roots) {
20720b57cec5SDimitry Andric if (std::unique_ptr<RedirectingFileSystem::Entry> E =
20730b57cec5SDimitry Andric parseEntry(&I, FS, /*IsRootEntry*/ true))
20740b57cec5SDimitry Andric RootEntries.push_back(std::move(E));
20750b57cec5SDimitry Andric else
20760b57cec5SDimitry Andric return false;
20770b57cec5SDimitry Andric }
20780b57cec5SDimitry Andric } else if (Key == "version") {
20790b57cec5SDimitry Andric StringRef VersionString;
20800b57cec5SDimitry Andric SmallString<4> Storage;
20810b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), VersionString, Storage))
20820b57cec5SDimitry Andric return false;
20830b57cec5SDimitry Andric int Version;
20840b57cec5SDimitry Andric if (VersionString.getAsInteger<int>(10, Version)) {
20850b57cec5SDimitry Andric error(I.getValue(), "expected integer");
20860b57cec5SDimitry Andric return false;
20870b57cec5SDimitry Andric }
20880b57cec5SDimitry Andric if (Version < 0) {
20890b57cec5SDimitry Andric error(I.getValue(), "invalid version number");
20900b57cec5SDimitry Andric return false;
20910b57cec5SDimitry Andric }
20920b57cec5SDimitry Andric if (Version != 0) {
20930b57cec5SDimitry Andric error(I.getValue(), "version mismatch, expected 0");
20940b57cec5SDimitry Andric return false;
20950b57cec5SDimitry Andric }
20960b57cec5SDimitry Andric } else if (Key == "case-sensitive") {
20970b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), FS->CaseSensitive))
20980b57cec5SDimitry Andric return false;
20990b57cec5SDimitry Andric } else if (Key == "overlay-relative") {
21000b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay))
21010b57cec5SDimitry Andric return false;
21020b57cec5SDimitry Andric } else if (Key == "use-external-names") {
21030b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), FS->UseExternalNames))
21040b57cec5SDimitry Andric return false;
21050b57cec5SDimitry Andric } else if (Key == "fallthrough") {
210681ad6265SDimitry Andric if (Keys["redirecting-with"].Seen) {
210781ad6265SDimitry Andric error(I.getValue(),
210881ad6265SDimitry Andric "'fallthrough' and 'redirecting-with' are mutually exclusive");
21090b57cec5SDimitry Andric return false;
211081ad6265SDimitry Andric }
211181ad6265SDimitry Andric
211281ad6265SDimitry Andric bool ShouldFallthrough = false;
211381ad6265SDimitry Andric if (!parseScalarBool(I.getValue(), ShouldFallthrough))
211481ad6265SDimitry Andric return false;
211581ad6265SDimitry Andric
211681ad6265SDimitry Andric if (ShouldFallthrough) {
211781ad6265SDimitry Andric FS->Redirection = RedirectingFileSystem::RedirectKind::Fallthrough;
211881ad6265SDimitry Andric } else {
211981ad6265SDimitry Andric FS->Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly;
212081ad6265SDimitry Andric }
212181ad6265SDimitry Andric } else if (Key == "redirecting-with") {
212281ad6265SDimitry Andric if (Keys["fallthrough"].Seen) {
212381ad6265SDimitry Andric error(I.getValue(),
212481ad6265SDimitry Andric "'fallthrough' and 'redirecting-with' are mutually exclusive");
212581ad6265SDimitry Andric return false;
212681ad6265SDimitry Andric }
212781ad6265SDimitry Andric
212881ad6265SDimitry Andric if (auto Kind = parseRedirectKind(I.getValue())) {
212981ad6265SDimitry Andric FS->Redirection = *Kind;
213081ad6265SDimitry Andric } else {
213181ad6265SDimitry Andric error(I.getValue(), "expected valid redirect kind");
213281ad6265SDimitry Andric return false;
213381ad6265SDimitry Andric }
2134bdd1243dSDimitry Andric } else if (Key == "root-relative") {
2135bdd1243dSDimitry Andric if (auto Kind = parseRootRelativeKind(I.getValue())) {
2136bdd1243dSDimitry Andric FS->RootRelative = *Kind;
2137bdd1243dSDimitry Andric } else {
2138bdd1243dSDimitry Andric error(I.getValue(), "expected valid root-relative kind");
2139bdd1243dSDimitry Andric return false;
2140bdd1243dSDimitry Andric }
21410b57cec5SDimitry Andric } else {
21420b57cec5SDimitry Andric llvm_unreachable("key missing from Keys");
21430b57cec5SDimitry Andric }
21440b57cec5SDimitry Andric }
21450b57cec5SDimitry Andric
21460b57cec5SDimitry Andric if (Stream.failed())
21470b57cec5SDimitry Andric return false;
21480b57cec5SDimitry Andric
21490b57cec5SDimitry Andric if (!checkMissingKeys(Top, Keys))
21500b57cec5SDimitry Andric return false;
21510b57cec5SDimitry Andric
21520b57cec5SDimitry Andric // Now that we sucessefully parsed the YAML file, canonicalize the internal
21530b57cec5SDimitry Andric // representation to a proper directory tree so that we can search faster
21540b57cec5SDimitry Andric // inside the VFS.
21550b57cec5SDimitry Andric for (auto &E : RootEntries)
21560b57cec5SDimitry Andric uniqueOverlayTree(FS, E.get());
21570b57cec5SDimitry Andric
21580b57cec5SDimitry Andric return true;
21590b57cec5SDimitry Andric }
21600b57cec5SDimitry Andric };
21610b57cec5SDimitry Andric
2162e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem>
create(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)21630b57cec5SDimitry Andric RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
21640b57cec5SDimitry Andric SourceMgr::DiagHandlerTy DiagHandler,
21650b57cec5SDimitry Andric StringRef YAMLFilePath, void *DiagContext,
21660b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> ExternalFS) {
21670b57cec5SDimitry Andric SourceMgr SM;
21680b57cec5SDimitry Andric yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
21690b57cec5SDimitry Andric
21700b57cec5SDimitry Andric SM.setDiagHandler(DiagHandler, DiagContext);
21710b57cec5SDimitry Andric yaml::document_iterator DI = Stream.begin();
21720b57cec5SDimitry Andric yaml::Node *Root = DI->getRoot();
21730b57cec5SDimitry Andric if (DI == Stream.end() || !Root) {
21740b57cec5SDimitry Andric SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
21750b57cec5SDimitry Andric return nullptr;
21760b57cec5SDimitry Andric }
21770b57cec5SDimitry Andric
21780b57cec5SDimitry Andric RedirectingFileSystemParser P(Stream);
21790b57cec5SDimitry Andric
21800b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem> FS(
21818bcb0991SDimitry Andric new RedirectingFileSystem(ExternalFS));
21820b57cec5SDimitry Andric
21830b57cec5SDimitry Andric if (!YAMLFilePath.empty()) {
21840b57cec5SDimitry Andric // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
21850b57cec5SDimitry Andric // to each 'external-contents' path.
21860b57cec5SDimitry Andric //
21870b57cec5SDimitry Andric // Example:
21880b57cec5SDimitry Andric // -ivfsoverlay dummy.cache/vfs/vfs.yaml
21890b57cec5SDimitry Andric // yields:
2190bdd1243dSDimitry Andric // FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs
21910b57cec5SDimitry Andric //
21920b57cec5SDimitry Andric SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
21930b57cec5SDimitry Andric std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir);
21940b57cec5SDimitry Andric assert(!EC && "Overlay dir final path must be absolute");
21950b57cec5SDimitry Andric (void)EC;
2196bdd1243dSDimitry Andric FS->setOverlayFileDir(OverlayAbsDir);
21970b57cec5SDimitry Andric }
21980b57cec5SDimitry Andric
21990b57cec5SDimitry Andric if (!P.parse(Root, FS.get()))
22000b57cec5SDimitry Andric return nullptr;
22010b57cec5SDimitry Andric
2202e8d8bef9SDimitry Andric return FS;
22030b57cec5SDimitry Andric }
22040b57cec5SDimitry Andric
create(ArrayRef<std::pair<std::string,std::string>> RemappedFiles,bool UseExternalNames,FileSystem & ExternalFS)2205e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
2206e8d8bef9SDimitry Andric ArrayRef<std::pair<std::string, std::string>> RemappedFiles,
2207e8d8bef9SDimitry Andric bool UseExternalNames, FileSystem &ExternalFS) {
2208e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> FS(
2209e8d8bef9SDimitry Andric new RedirectingFileSystem(&ExternalFS));
2210e8d8bef9SDimitry Andric FS->UseExternalNames = UseExternalNames;
22110b57cec5SDimitry Andric
2212e8d8bef9SDimitry Andric StringMap<RedirectingFileSystem::Entry *> Entries;
2213e8d8bef9SDimitry Andric
2214e8d8bef9SDimitry Andric for (auto &Mapping : llvm::reverse(RemappedFiles)) {
2215e8d8bef9SDimitry Andric SmallString<128> From = StringRef(Mapping.first);
2216e8d8bef9SDimitry Andric SmallString<128> To = StringRef(Mapping.second);
2217e8d8bef9SDimitry Andric {
2218e8d8bef9SDimitry Andric auto EC = ExternalFS.makeAbsolute(From);
2219e8d8bef9SDimitry Andric (void)EC;
2220e8d8bef9SDimitry Andric assert(!EC && "Could not make absolute path");
2221e8d8bef9SDimitry Andric }
2222e8d8bef9SDimitry Andric
2223e8d8bef9SDimitry Andric // Check if we've already mapped this file. The first one we see (in the
2224e8d8bef9SDimitry Andric // reverse iteration) wins.
2225e8d8bef9SDimitry Andric RedirectingFileSystem::Entry *&ToEntry = Entries[From];
2226e8d8bef9SDimitry Andric if (ToEntry)
2227e8d8bef9SDimitry Andric continue;
2228e8d8bef9SDimitry Andric
2229e8d8bef9SDimitry Andric // Add parent directories.
2230e8d8bef9SDimitry Andric RedirectingFileSystem::Entry *Parent = nullptr;
2231e8d8bef9SDimitry Andric StringRef FromDirectory = llvm::sys::path::parent_path(From);
2232e8d8bef9SDimitry Andric for (auto I = llvm::sys::path::begin(FromDirectory),
2233e8d8bef9SDimitry Andric E = llvm::sys::path::end(FromDirectory);
2234e8d8bef9SDimitry Andric I != E; ++I) {
2235e8d8bef9SDimitry Andric Parent = RedirectingFileSystemParser::lookupOrCreateEntry(FS.get(), *I,
2236e8d8bef9SDimitry Andric Parent);
2237e8d8bef9SDimitry Andric }
2238e8d8bef9SDimitry Andric assert(Parent && "File without a directory?");
2239e8d8bef9SDimitry Andric {
2240e8d8bef9SDimitry Andric auto EC = ExternalFS.makeAbsolute(To);
2241e8d8bef9SDimitry Andric (void)EC;
2242e8d8bef9SDimitry Andric assert(!EC && "Could not make absolute path");
2243e8d8bef9SDimitry Andric }
2244e8d8bef9SDimitry Andric
2245e8d8bef9SDimitry Andric // Add the file.
2246fe6060f1SDimitry Andric auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
2247e8d8bef9SDimitry Andric llvm::sys::path::filename(From), To,
2248fe6060f1SDimitry Andric UseExternalNames ? RedirectingFileSystem::NK_External
2249fe6060f1SDimitry Andric : RedirectingFileSystem::NK_Virtual);
2250e8d8bef9SDimitry Andric ToEntry = NewFile.get();
2251fe6060f1SDimitry Andric cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(
2252e8d8bef9SDimitry Andric std::move(NewFile));
2253e8d8bef9SDimitry Andric }
2254e8d8bef9SDimitry Andric
2255e8d8bef9SDimitry Andric return FS;
2256e8d8bef9SDimitry Andric }
2257e8d8bef9SDimitry Andric
LookupResult(Entry * E,sys::path::const_iterator Start,sys::path::const_iterator End)2258fe6060f1SDimitry Andric RedirectingFileSystem::LookupResult::LookupResult(
2259fe6060f1SDimitry Andric Entry *E, sys::path::const_iterator Start, sys::path::const_iterator End)
2260fe6060f1SDimitry Andric : E(E) {
2261fe6060f1SDimitry Andric assert(E != nullptr);
2262fe6060f1SDimitry Andric // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
2263fe6060f1SDimitry Andric // path of the directory it maps to in the external file system plus any
2264fe6060f1SDimitry Andric // remaining path components in the provided iterator.
2265fe6060f1SDimitry Andric if (auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(E)) {
2266fe6060f1SDimitry Andric SmallString<256> Redirect(DRE->getExternalContentsPath());
2267fe6060f1SDimitry Andric sys::path::append(Redirect, Start, End,
2268fe6060f1SDimitry Andric getExistingStyle(DRE->getExternalContentsPath()));
2269fe6060f1SDimitry Andric ExternalRedirect = std::string(Redirect);
2270fe6060f1SDimitry Andric }
2271fe6060f1SDimitry Andric }
2272fe6060f1SDimitry Andric
getPath(llvm::SmallVectorImpl<char> & Result) const227306c3fb27SDimitry Andric void RedirectingFileSystem::LookupResult::getPath(
227406c3fb27SDimitry Andric llvm::SmallVectorImpl<char> &Result) const {
227506c3fb27SDimitry Andric Result.clear();
227606c3fb27SDimitry Andric for (Entry *Parent : Parents)
227706c3fb27SDimitry Andric llvm::sys::path::append(Result, Parent->getName());
227806c3fb27SDimitry Andric llvm::sys::path::append(Result, E->getName());
227906c3fb27SDimitry Andric }
228006c3fb27SDimitry Andric
makeCanonicalForLookup(SmallVectorImpl<char> & Path) const2281*0fca6ea1SDimitry Andric std::error_code RedirectingFileSystem::makeCanonicalForLookup(
2282*0fca6ea1SDimitry Andric SmallVectorImpl<char> &Path) const {
22830b57cec5SDimitry Andric if (std::error_code EC = makeAbsolute(Path))
22840b57cec5SDimitry Andric return EC;
22850b57cec5SDimitry Andric
2286e8d8bef9SDimitry Andric llvm::SmallString<256> CanonicalPath =
2287e8d8bef9SDimitry Andric canonicalize(StringRef(Path.data(), Path.size()));
2288e8d8bef9SDimitry Andric if (CanonicalPath.empty())
22890b57cec5SDimitry Andric return make_error_code(llvm::errc::invalid_argument);
22900b57cec5SDimitry Andric
2291e8d8bef9SDimitry Andric Path.assign(CanonicalPath.begin(), CanonicalPath.end());
2292e8d8bef9SDimitry Andric return {};
2293e8d8bef9SDimitry Andric }
2294e8d8bef9SDimitry Andric
2295fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult>
lookupPath(StringRef Path) const2296e8d8bef9SDimitry Andric RedirectingFileSystem::lookupPath(StringRef Path) const {
2297*0fca6ea1SDimitry Andric llvm::SmallString<128> CanonicalPath(Path);
2298*0fca6ea1SDimitry Andric if (std::error_code EC = makeCanonicalForLookup(CanonicalPath))
2299*0fca6ea1SDimitry Andric return EC;
2300*0fca6ea1SDimitry Andric
2301*0fca6ea1SDimitry Andric // RedirectOnly means the VFS is always used.
2302*0fca6ea1SDimitry Andric if (UsageTrackingActive && Redirection == RedirectKind::RedirectOnly)
2303*0fca6ea1SDimitry Andric HasBeenUsed = true;
2304*0fca6ea1SDimitry Andric
2305*0fca6ea1SDimitry Andric sys::path::const_iterator Start = sys::path::begin(CanonicalPath);
2306*0fca6ea1SDimitry Andric sys::path::const_iterator End = sys::path::end(CanonicalPath);
230706c3fb27SDimitry Andric llvm::SmallVector<Entry *, 32> Entries;
23080b57cec5SDimitry Andric for (const auto &Root : Roots) {
2309fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result =
231006c3fb27SDimitry Andric lookupPathImpl(Start, End, Root.get(), Entries);
2311*0fca6ea1SDimitry Andric if (UsageTrackingActive && Result && isa<RemapEntry>(Result->E))
2312*0fca6ea1SDimitry Andric HasBeenUsed = true;
231306c3fb27SDimitry Andric if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) {
231406c3fb27SDimitry Andric Result->Parents = std::move(Entries);
23150b57cec5SDimitry Andric return Result;
23160b57cec5SDimitry Andric }
231706c3fb27SDimitry Andric }
23180b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory);
23190b57cec5SDimitry Andric }
23200b57cec5SDimitry Andric
2321fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult>
lookupPathImpl(sys::path::const_iterator Start,sys::path::const_iterator End,RedirectingFileSystem::Entry * From,llvm::SmallVectorImpl<Entry * > & Entries) const2322fe6060f1SDimitry Andric RedirectingFileSystem::lookupPathImpl(
2323fe6060f1SDimitry Andric sys::path::const_iterator Start, sys::path::const_iterator End,
232406c3fb27SDimitry Andric RedirectingFileSystem::Entry *From,
232506c3fb27SDimitry Andric llvm::SmallVectorImpl<Entry *> &Entries) const {
23260b57cec5SDimitry Andric assert(!isTraversalComponent(*Start) &&
23270b57cec5SDimitry Andric !isTraversalComponent(From->getName()) &&
23280b57cec5SDimitry Andric "Paths should not contain traversal components");
23290b57cec5SDimitry Andric
23300b57cec5SDimitry Andric StringRef FromName = From->getName();
23310b57cec5SDimitry Andric
23320b57cec5SDimitry Andric // Forward the search to the next component in case this is an empty one.
23330b57cec5SDimitry Andric if (!FromName.empty()) {
2334480093f4SDimitry Andric if (!pathComponentMatches(*Start, FromName))
23350b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory);
23360b57cec5SDimitry Andric
23370b57cec5SDimitry Andric ++Start;
23380b57cec5SDimitry Andric
23390b57cec5SDimitry Andric if (Start == End) {
23400b57cec5SDimitry Andric // Match!
2341fe6060f1SDimitry Andric return LookupResult(From, Start, End);
23420b57cec5SDimitry Andric }
23430b57cec5SDimitry Andric }
23440b57cec5SDimitry Andric
2345fe6060f1SDimitry Andric if (isa<RedirectingFileSystem::FileEntry>(From))
23460b57cec5SDimitry Andric return make_error_code(llvm::errc::not_a_directory);
23470b57cec5SDimitry Andric
2348fe6060f1SDimitry Andric if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From))
2349fe6060f1SDimitry Andric return LookupResult(From, Start, End);
2350fe6060f1SDimitry Andric
2351fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From);
23520b57cec5SDimitry Andric for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
23530b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) {
235406c3fb27SDimitry Andric Entries.push_back(From);
2355fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result =
235606c3fb27SDimitry Andric lookupPathImpl(Start, End, DirEntry.get(), Entries);
23570b57cec5SDimitry Andric if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
23580b57cec5SDimitry Andric return Result;
235906c3fb27SDimitry Andric Entries.pop_back();
23600b57cec5SDimitry Andric }
2361480093f4SDimitry Andric
23620b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory);
23630b57cec5SDimitry Andric }
23640b57cec5SDimitry Andric
getRedirectedFileStatus(const Twine & OriginalPath,bool UseExternalNames,Status ExternalStatus)2365349cc55cSDimitry Andric static Status getRedirectedFileStatus(const Twine &OriginalPath,
2366349cc55cSDimitry Andric bool UseExternalNames,
23670b57cec5SDimitry Andric Status ExternalStatus) {
236881ad6265SDimitry Andric // The path has been mapped by some nested VFS and exposes an external path,
236981ad6265SDimitry Andric // don't override it with the original path.
237081ad6265SDimitry Andric if (ExternalStatus.ExposesExternalVFSPath)
237181ad6265SDimitry Andric return ExternalStatus;
237281ad6265SDimitry Andric
23730b57cec5SDimitry Andric Status S = ExternalStatus;
23740b57cec5SDimitry Andric if (!UseExternalNames)
2375349cc55cSDimitry Andric S = Status::copyWithNewName(S, OriginalPath);
237681ad6265SDimitry Andric else
237781ad6265SDimitry Andric S.ExposesExternalVFSPath = true;
23780b57cec5SDimitry Andric return S;
23790b57cec5SDimitry Andric }
23800b57cec5SDimitry Andric
status(const Twine & LookupPath,const Twine & OriginalPath,const RedirectingFileSystem::LookupResult & Result)2381fe6060f1SDimitry Andric ErrorOr<Status> RedirectingFileSystem::status(
2382*0fca6ea1SDimitry Andric const Twine &LookupPath, const Twine &OriginalPath,
2383349cc55cSDimitry Andric const RedirectingFileSystem::LookupResult &Result) {
2384bdd1243dSDimitry Andric if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
2385*0fca6ea1SDimitry Andric SmallString<256> RemappedPath((*ExtRedirect).str());
2386*0fca6ea1SDimitry Andric if (std::error_code EC = makeAbsolute(RemappedPath))
2387349cc55cSDimitry Andric return EC;
2388349cc55cSDimitry Andric
2389*0fca6ea1SDimitry Andric ErrorOr<Status> S = ExternalFS->status(RemappedPath);
2390fe6060f1SDimitry Andric if (!S)
23910b57cec5SDimitry Andric return S;
2392349cc55cSDimitry Andric S = Status::copyWithNewName(*S, *ExtRedirect);
2393fe6060f1SDimitry Andric auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E);
2394349cc55cSDimitry Andric return getRedirectedFileStatus(OriginalPath,
2395349cc55cSDimitry Andric RE->useExternalName(UseExternalNames), *S);
23960b57cec5SDimitry Andric }
2397fe6060f1SDimitry Andric
2398fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
2399*0fca6ea1SDimitry Andric return Status::copyWithNewName(DE->getStatus(), LookupPath);
24000b57cec5SDimitry Andric }
24010b57cec5SDimitry Andric
2402349cc55cSDimitry Andric ErrorOr<Status>
getExternalStatus(const Twine & LookupPath,const Twine & OriginalPath) const2403*0fca6ea1SDimitry Andric RedirectingFileSystem::getExternalStatus(const Twine &LookupPath,
2404349cc55cSDimitry Andric const Twine &OriginalPath) const {
2405*0fca6ea1SDimitry Andric auto Result = ExternalFS->status(LookupPath);
240681ad6265SDimitry Andric
240781ad6265SDimitry Andric // The path has been mapped by some nested VFS, don't override it with the
240881ad6265SDimitry Andric // original path.
240981ad6265SDimitry Andric if (!Result || Result->ExposesExternalVFSPath)
241081ad6265SDimitry Andric return Result;
241181ad6265SDimitry Andric return Status::copyWithNewName(Result.get(), OriginalPath);
2412349cc55cSDimitry Andric }
2413e8d8bef9SDimitry Andric
status(const Twine & OriginalPath)2414349cc55cSDimitry Andric ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {
2415*0fca6ea1SDimitry Andric SmallString<256> Path;
2416*0fca6ea1SDimitry Andric OriginalPath.toVector(Path);
2417349cc55cSDimitry Andric
2418*0fca6ea1SDimitry Andric if (std::error_code EC = makeAbsolute(Path))
2419e8d8bef9SDimitry Andric return EC;
2420e8d8bef9SDimitry Andric
242181ad6265SDimitry Andric if (Redirection == RedirectKind::Fallback) {
242281ad6265SDimitry Andric // Attempt to find the original file first, only falling back to the
242381ad6265SDimitry Andric // mapped file if that fails.
2424*0fca6ea1SDimitry Andric ErrorOr<Status> S = getExternalStatus(Path, OriginalPath);
242581ad6265SDimitry Andric if (S)
242681ad6265SDimitry Andric return S;
242781ad6265SDimitry Andric }
242881ad6265SDimitry Andric
2429*0fca6ea1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
24300b57cec5SDimitry Andric if (!Result) {
243181ad6265SDimitry Andric // Was not able to map file, fallthrough to using the original path if
243281ad6265SDimitry Andric // that was the specified redirection type.
243381ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough &&
243481ad6265SDimitry Andric isFileNotFound(Result.getError()))
2435*0fca6ea1SDimitry Andric return getExternalStatus(Path, OriginalPath);
24360b57cec5SDimitry Andric return Result.getError();
24370b57cec5SDimitry Andric }
2438fe6060f1SDimitry Andric
2439*0fca6ea1SDimitry Andric ErrorOr<Status> S = status(Path, OriginalPath, *Result);
244081ad6265SDimitry Andric if (!S && Redirection == RedirectKind::Fallthrough &&
244181ad6265SDimitry Andric isFileNotFound(S.getError(), Result->E)) {
244281ad6265SDimitry Andric // Mapped the file but it wasn't found in the underlying filesystem,
244381ad6265SDimitry Andric // fallthrough to using the original path if that was the specified
244481ad6265SDimitry Andric // redirection type.
2445*0fca6ea1SDimitry Andric return getExternalStatus(Path, OriginalPath);
2446349cc55cSDimitry Andric }
2447349cc55cSDimitry Andric
2448fe6060f1SDimitry Andric return S;
24490b57cec5SDimitry Andric }
24500b57cec5SDimitry Andric
exists(const Twine & OriginalPath)2451*0fca6ea1SDimitry Andric bool RedirectingFileSystem::exists(const Twine &OriginalPath) {
2452*0fca6ea1SDimitry Andric SmallString<256> Path;
2453*0fca6ea1SDimitry Andric OriginalPath.toVector(Path);
2454*0fca6ea1SDimitry Andric
2455*0fca6ea1SDimitry Andric if (makeAbsolute(Path))
2456*0fca6ea1SDimitry Andric return false;
2457*0fca6ea1SDimitry Andric
2458*0fca6ea1SDimitry Andric if (Redirection == RedirectKind::Fallback) {
2459*0fca6ea1SDimitry Andric // Attempt to find the original file first, only falling back to the
2460*0fca6ea1SDimitry Andric // mapped file if that fails.
2461*0fca6ea1SDimitry Andric if (ExternalFS->exists(Path))
2462*0fca6ea1SDimitry Andric return true;
2463*0fca6ea1SDimitry Andric }
2464*0fca6ea1SDimitry Andric
2465*0fca6ea1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
2466*0fca6ea1SDimitry Andric if (!Result) {
2467*0fca6ea1SDimitry Andric // Was not able to map file, fallthrough to using the original path if
2468*0fca6ea1SDimitry Andric // that was the specified redirection type.
2469*0fca6ea1SDimitry Andric if (Redirection == RedirectKind::Fallthrough &&
2470*0fca6ea1SDimitry Andric isFileNotFound(Result.getError()))
2471*0fca6ea1SDimitry Andric return ExternalFS->exists(Path);
2472*0fca6ea1SDimitry Andric return false;
2473*0fca6ea1SDimitry Andric }
2474*0fca6ea1SDimitry Andric
2475*0fca6ea1SDimitry Andric std::optional<StringRef> ExtRedirect = Result->getExternalRedirect();
2476*0fca6ea1SDimitry Andric if (!ExtRedirect) {
2477*0fca6ea1SDimitry Andric assert(isa<RedirectingFileSystem::DirectoryEntry>(Result->E));
2478*0fca6ea1SDimitry Andric return true;
2479*0fca6ea1SDimitry Andric }
2480*0fca6ea1SDimitry Andric
2481*0fca6ea1SDimitry Andric SmallString<256> RemappedPath((*ExtRedirect).str());
2482*0fca6ea1SDimitry Andric if (makeAbsolute(RemappedPath))
2483*0fca6ea1SDimitry Andric return false;
2484*0fca6ea1SDimitry Andric
2485*0fca6ea1SDimitry Andric if (ExternalFS->exists(RemappedPath))
2486*0fca6ea1SDimitry Andric return true;
2487*0fca6ea1SDimitry Andric
2488*0fca6ea1SDimitry Andric if (Redirection == RedirectKind::Fallthrough) {
2489*0fca6ea1SDimitry Andric // Mapped the file but it wasn't found in the underlying filesystem,
2490*0fca6ea1SDimitry Andric // fallthrough to using the original path if that was the specified
2491*0fca6ea1SDimitry Andric // redirection type.
2492*0fca6ea1SDimitry Andric return ExternalFS->exists(Path);
2493*0fca6ea1SDimitry Andric }
2494*0fca6ea1SDimitry Andric
2495*0fca6ea1SDimitry Andric return false;
2496*0fca6ea1SDimitry Andric }
2497*0fca6ea1SDimitry Andric
24980b57cec5SDimitry Andric namespace {
24990b57cec5SDimitry Andric
25000b57cec5SDimitry Andric /// Provide a file wrapper with an overriden status.
25010b57cec5SDimitry Andric class FileWithFixedStatus : public File {
25020b57cec5SDimitry Andric std::unique_ptr<File> InnerFile;
25030b57cec5SDimitry Andric Status S;
25040b57cec5SDimitry Andric
25050b57cec5SDimitry Andric public:
FileWithFixedStatus(std::unique_ptr<File> InnerFile,Status S)25060b57cec5SDimitry Andric FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
25070b57cec5SDimitry Andric : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
25080b57cec5SDimitry Andric
status()25090b57cec5SDimitry Andric ErrorOr<Status> status() override { return S; }
25100b57cec5SDimitry Andric ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
25110b57cec5SDimitry Andric
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)25120b57cec5SDimitry Andric getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
25130b57cec5SDimitry Andric bool IsVolatile) override {
25140b57cec5SDimitry Andric return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
25150b57cec5SDimitry Andric IsVolatile);
25160b57cec5SDimitry Andric }
25170b57cec5SDimitry Andric
close()25180b57cec5SDimitry Andric std::error_code close() override { return InnerFile->close(); }
2519349cc55cSDimitry Andric
setPath(const Twine & Path)2520349cc55cSDimitry Andric void setPath(const Twine &Path) override { S = S.copyWithNewName(S, Path); }
25210b57cec5SDimitry Andric };
25220b57cec5SDimitry Andric
25230b57cec5SDimitry Andric } // namespace
25240b57cec5SDimitry Andric
25250b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
getWithPath(ErrorOr<std::unique_ptr<File>> Result,const Twine & P)2526349cc55cSDimitry Andric File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
252781ad6265SDimitry Andric // See \c getRedirectedFileStatus - don't update path if it's exposing an
252881ad6265SDimitry Andric // external path.
252981ad6265SDimitry Andric if (!Result || (*Result)->status()->ExposesExternalVFSPath)
2530349cc55cSDimitry Andric return Result;
2531e8d8bef9SDimitry Andric
2532349cc55cSDimitry Andric ErrorOr<std::unique_ptr<File>> F = std::move(*Result);
2533349cc55cSDimitry Andric auto Name = F->get()->getName();
2534349cc55cSDimitry Andric if (Name && Name.get() != P.str())
2535349cc55cSDimitry Andric F->get()->setPath(P);
2536349cc55cSDimitry Andric return F;
2537349cc55cSDimitry Andric }
2538349cc55cSDimitry Andric
2539349cc55cSDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & OriginalPath)2540349cc55cSDimitry Andric RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) {
2541*0fca6ea1SDimitry Andric SmallString<256> Path;
2542*0fca6ea1SDimitry Andric OriginalPath.toVector(Path);
2543349cc55cSDimitry Andric
2544*0fca6ea1SDimitry Andric if (std::error_code EC = makeAbsolute(Path))
2545e8d8bef9SDimitry Andric return EC;
2546e8d8bef9SDimitry Andric
254781ad6265SDimitry Andric if (Redirection == RedirectKind::Fallback) {
254881ad6265SDimitry Andric // Attempt to find the original file first, only falling back to the
254981ad6265SDimitry Andric // mapped file if that fails.
2550*0fca6ea1SDimitry Andric auto F = File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
255181ad6265SDimitry Andric if (F)
255281ad6265SDimitry Andric return F;
255381ad6265SDimitry Andric }
255481ad6265SDimitry Andric
2555*0fca6ea1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
2556fe6060f1SDimitry Andric if (!Result) {
255781ad6265SDimitry Andric // Was not able to map file, fallthrough to using the original path if
255881ad6265SDimitry Andric // that was the specified redirection type.
255981ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough &&
256081ad6265SDimitry Andric isFileNotFound(Result.getError()))
2561*0fca6ea1SDimitry Andric return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
2562fe6060f1SDimitry Andric return Result.getError();
25630b57cec5SDimitry Andric }
25640b57cec5SDimitry Andric
2565fe6060f1SDimitry Andric if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
25660b57cec5SDimitry Andric return make_error_code(llvm::errc::invalid_argument);
25670b57cec5SDimitry Andric
2568fe6060f1SDimitry Andric StringRef ExtRedirect = *Result->getExternalRedirect();
2569*0fca6ea1SDimitry Andric SmallString<256> RemappedPath(ExtRedirect.str());
2570*0fca6ea1SDimitry Andric if (std::error_code EC = makeAbsolute(RemappedPath))
2571349cc55cSDimitry Andric return EC;
2572349cc55cSDimitry Andric
2573fe6060f1SDimitry Andric auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
25740b57cec5SDimitry Andric
2575*0fca6ea1SDimitry Andric auto ExternalFile =
2576*0fca6ea1SDimitry Andric File::getWithPath(ExternalFS->openFileForRead(RemappedPath), ExtRedirect);
2577fe6060f1SDimitry Andric if (!ExternalFile) {
257881ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough &&
257981ad6265SDimitry Andric isFileNotFound(ExternalFile.getError(), Result->E)) {
258081ad6265SDimitry Andric // Mapped the file but it wasn't found in the underlying filesystem,
258181ad6265SDimitry Andric // fallthrough to using the original path if that was the specified
258281ad6265SDimitry Andric // redirection type.
2583*0fca6ea1SDimitry Andric return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
258481ad6265SDimitry Andric }
2585fe6060f1SDimitry Andric return ExternalFile;
2586fe6060f1SDimitry Andric }
2587fe6060f1SDimitry Andric
2588fe6060f1SDimitry Andric auto ExternalStatus = (*ExternalFile)->status();
25890b57cec5SDimitry Andric if (!ExternalStatus)
25900b57cec5SDimitry Andric return ExternalStatus.getError();
25910b57cec5SDimitry Andric
259281ad6265SDimitry Andric // Otherwise, the file was successfully remapped. Mark it as such. Also
259381ad6265SDimitry Andric // replace the underlying path if the external name is being used.
2594fe6060f1SDimitry Andric Status S = getRedirectedFileStatus(
2595349cc55cSDimitry Andric OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus);
25960b57cec5SDimitry Andric return std::unique_ptr<File>(
2597fe6060f1SDimitry Andric std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
25980b57cec5SDimitry Andric }
25990b57cec5SDimitry Andric
26000b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & OriginalPath,SmallVectorImpl<char> & Output)260181ad6265SDimitry Andric RedirectingFileSystem::getRealPath(const Twine &OriginalPath,
2602*0fca6ea1SDimitry Andric SmallVectorImpl<char> &Output) {
2603*0fca6ea1SDimitry Andric SmallString<256> Path;
2604*0fca6ea1SDimitry Andric OriginalPath.toVector(Path);
2605e8d8bef9SDimitry Andric
2606*0fca6ea1SDimitry Andric if (std::error_code EC = makeAbsolute(Path))
2607e8d8bef9SDimitry Andric return EC;
2608e8d8bef9SDimitry Andric
260981ad6265SDimitry Andric if (Redirection == RedirectKind::Fallback) {
261081ad6265SDimitry Andric // Attempt to find the original file first, only falling back to the
261181ad6265SDimitry Andric // mapped file if that fails.
2612*0fca6ea1SDimitry Andric std::error_code EC = ExternalFS->getRealPath(Path, Output);
261381ad6265SDimitry Andric if (!EC)
261481ad6265SDimitry Andric return EC;
261581ad6265SDimitry Andric }
261681ad6265SDimitry Andric
2617*0fca6ea1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
26180b57cec5SDimitry Andric if (!Result) {
261981ad6265SDimitry Andric // Was not able to map file, fallthrough to using the original path if
262081ad6265SDimitry Andric // that was the specified redirection type.
262181ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough &&
262281ad6265SDimitry Andric isFileNotFound(Result.getError()))
2623*0fca6ea1SDimitry Andric return ExternalFS->getRealPath(Path, Output);
26240b57cec5SDimitry Andric return Result.getError();
26250b57cec5SDimitry Andric }
26260b57cec5SDimitry Andric
2627fe6060f1SDimitry Andric // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2628fe6060f1SDimitry Andric // path in the external file system.
2629fe6060f1SDimitry Andric if (auto ExtRedirect = Result->getExternalRedirect()) {
2630fe6060f1SDimitry Andric auto P = ExternalFS->getRealPath(*ExtRedirect, Output);
263181ad6265SDimitry Andric if (P && Redirection == RedirectKind::Fallthrough &&
263281ad6265SDimitry Andric isFileNotFound(P, Result->E)) {
263381ad6265SDimitry Andric // Mapped the file but it wasn't found in the underlying filesystem,
263481ad6265SDimitry Andric // fallthrough to using the original path if that was the specified
263581ad6265SDimitry Andric // redirection type.
2636*0fca6ea1SDimitry Andric return ExternalFS->getRealPath(Path, Output);
26370b57cec5SDimitry Andric }
2638fe6060f1SDimitry Andric return P;
2639fe6060f1SDimitry Andric }
2640fe6060f1SDimitry Andric
264106c3fb27SDimitry Andric // We found a DirectoryEntry, which does not have a single external contents
264206c3fb27SDimitry Andric // path. Use the canonical virtual path.
264306c3fb27SDimitry Andric if (Redirection == RedirectKind::Fallthrough) {
264406c3fb27SDimitry Andric Result->getPath(Output);
264506c3fb27SDimitry Andric return {};
264606c3fb27SDimitry Andric }
264781ad6265SDimitry Andric return llvm::errc::invalid_argument;
26480b57cec5SDimitry Andric }
26490b57cec5SDimitry Andric
2650e8d8bef9SDimitry Andric std::unique_ptr<FileSystem>
getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)26510b57cec5SDimitry Andric vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
26520b57cec5SDimitry Andric SourceMgr::DiagHandlerTy DiagHandler,
26530b57cec5SDimitry Andric StringRef YAMLFilePath, void *DiagContext,
26540b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> ExternalFS) {
26550b57cec5SDimitry Andric return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
26560b57cec5SDimitry Andric YAMLFilePath, DiagContext,
26570b57cec5SDimitry Andric std::move(ExternalFS));
26580b57cec5SDimitry Andric }
26590b57cec5SDimitry Andric
getVFSEntries(RedirectingFileSystem::Entry * SrcE,SmallVectorImpl<StringRef> & Path,SmallVectorImpl<YAMLVFSEntry> & Entries)26600b57cec5SDimitry Andric static void getVFSEntries(RedirectingFileSystem::Entry *SrcE,
26610b57cec5SDimitry Andric SmallVectorImpl<StringRef> &Path,
26620b57cec5SDimitry Andric SmallVectorImpl<YAMLVFSEntry> &Entries) {
26630b57cec5SDimitry Andric auto Kind = SrcE->getKind();
26640b57cec5SDimitry Andric if (Kind == RedirectingFileSystem::EK_Directory) {
2665fe6060f1SDimitry Andric auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
26660b57cec5SDimitry Andric assert(DE && "Must be a directory");
26670b57cec5SDimitry Andric for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
26680b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) {
26690b57cec5SDimitry Andric Path.push_back(SubEntry->getName());
26700b57cec5SDimitry Andric getVFSEntries(SubEntry.get(), Path, Entries);
26710b57cec5SDimitry Andric Path.pop_back();
26720b57cec5SDimitry Andric }
26730b57cec5SDimitry Andric return;
26740b57cec5SDimitry Andric }
26750b57cec5SDimitry Andric
2676fe6060f1SDimitry Andric if (Kind == RedirectingFileSystem::EK_DirectoryRemap) {
2677fe6060f1SDimitry Andric auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
2678fe6060f1SDimitry Andric assert(DR && "Must be a directory remap");
2679fe6060f1SDimitry Andric SmallString<128> VPath;
2680fe6060f1SDimitry Andric for (auto &Comp : Path)
2681fe6060f1SDimitry Andric llvm::sys::path::append(VPath, Comp);
2682fe6060f1SDimitry Andric Entries.push_back(
2683fe6060f1SDimitry Andric YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
2684fe6060f1SDimitry Andric return;
2685fe6060f1SDimitry Andric }
2686fe6060f1SDimitry Andric
26870b57cec5SDimitry Andric assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
2688fe6060f1SDimitry Andric auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
26890b57cec5SDimitry Andric assert(FE && "Must be a file");
26900b57cec5SDimitry Andric SmallString<128> VPath;
26910b57cec5SDimitry Andric for (auto &Comp : Path)
26920b57cec5SDimitry Andric llvm::sys::path::append(VPath, Comp);
26930b57cec5SDimitry Andric Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
26940b57cec5SDimitry Andric }
26950b57cec5SDimitry Andric
collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,SmallVectorImpl<YAMLVFSEntry> & CollectedEntries,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)26960b57cec5SDimitry Andric void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
26970b57cec5SDimitry Andric SourceMgr::DiagHandlerTy DiagHandler,
26980b57cec5SDimitry Andric StringRef YAMLFilePath,
26990b57cec5SDimitry Andric SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
27000b57cec5SDimitry Andric void *DiagContext,
27010b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2702e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create(
27030b57cec5SDimitry Andric std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
27040b57cec5SDimitry Andric std::move(ExternalFS));
2705fe6060f1SDimitry Andric if (!VFS)
2706fe6060f1SDimitry Andric return;
2707fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> RootResult =
2708fe6060f1SDimitry Andric VFS->lookupPath("/");
2709fe6060f1SDimitry Andric if (!RootResult)
27100b57cec5SDimitry Andric return;
27110b57cec5SDimitry Andric SmallVector<StringRef, 8> Components;
27120b57cec5SDimitry Andric Components.push_back("/");
2713fe6060f1SDimitry Andric getVFSEntries(RootResult->E, Components, CollectedEntries);
27140b57cec5SDimitry Andric }
27150b57cec5SDimitry Andric
getNextVirtualUniqueID()27160b57cec5SDimitry Andric UniqueID vfs::getNextVirtualUniqueID() {
27170b57cec5SDimitry Andric static std::atomic<unsigned> UID;
27180b57cec5SDimitry Andric unsigned ID = ++UID;
27190b57cec5SDimitry Andric // The following assumes that uint64_t max will never collide with a real
27200b57cec5SDimitry Andric // dev_t value from the OS.
27210b57cec5SDimitry Andric return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
27220b57cec5SDimitry Andric }
27230b57cec5SDimitry Andric
addEntry(StringRef VirtualPath,StringRef RealPath,bool IsDirectory)27245ffd83dbSDimitry Andric void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath,
27255ffd83dbSDimitry Andric bool IsDirectory) {
27260b57cec5SDimitry Andric assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
27270b57cec5SDimitry Andric assert(sys::path::is_absolute(RealPath) && "real path not absolute");
27280b57cec5SDimitry Andric assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
27295ffd83dbSDimitry Andric Mappings.emplace_back(VirtualPath, RealPath, IsDirectory);
27305ffd83dbSDimitry Andric }
27315ffd83dbSDimitry Andric
addFileMapping(StringRef VirtualPath,StringRef RealPath)27325ffd83dbSDimitry Andric void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
27335ffd83dbSDimitry Andric addEntry(VirtualPath, RealPath, /*IsDirectory=*/false);
27345ffd83dbSDimitry Andric }
27355ffd83dbSDimitry Andric
addDirectoryMapping(StringRef VirtualPath,StringRef RealPath)27365ffd83dbSDimitry Andric void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath,
27375ffd83dbSDimitry Andric StringRef RealPath) {
27385ffd83dbSDimitry Andric addEntry(VirtualPath, RealPath, /*IsDirectory=*/true);
27390b57cec5SDimitry Andric }
27400b57cec5SDimitry Andric
27410b57cec5SDimitry Andric namespace {
27420b57cec5SDimitry Andric
27430b57cec5SDimitry Andric class JSONWriter {
27440b57cec5SDimitry Andric llvm::raw_ostream &OS;
27450b57cec5SDimitry Andric SmallVector<StringRef, 16> DirStack;
27460b57cec5SDimitry Andric
getDirIndent()27470b57cec5SDimitry Andric unsigned getDirIndent() { return 4 * DirStack.size(); }
getFileIndent()27480b57cec5SDimitry Andric unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
27490b57cec5SDimitry Andric bool containedIn(StringRef Parent, StringRef Path);
27500b57cec5SDimitry Andric StringRef containedPart(StringRef Parent, StringRef Path);
27510b57cec5SDimitry Andric void startDirectory(StringRef Path);
27520b57cec5SDimitry Andric void endDirectory();
27530b57cec5SDimitry Andric void writeEntry(StringRef VPath, StringRef RPath);
27540b57cec5SDimitry Andric
27550b57cec5SDimitry Andric public:
JSONWriter(llvm::raw_ostream & OS)27560b57cec5SDimitry Andric JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
27570b57cec5SDimitry Andric
2758bdd1243dSDimitry Andric void write(ArrayRef<YAMLVFSEntry> Entries,
2759bdd1243dSDimitry Andric std::optional<bool> UseExternalNames,
2760bdd1243dSDimitry Andric std::optional<bool> IsCaseSensitive,
2761bdd1243dSDimitry Andric std::optional<bool> IsOverlayRelative, StringRef OverlayDir);
27620b57cec5SDimitry Andric };
27630b57cec5SDimitry Andric
27640b57cec5SDimitry Andric } // namespace
27650b57cec5SDimitry Andric
containedIn(StringRef Parent,StringRef Path)27660b57cec5SDimitry Andric bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
27670b57cec5SDimitry Andric using namespace llvm::sys;
27680b57cec5SDimitry Andric
27690b57cec5SDimitry Andric // Compare each path component.
27700b57cec5SDimitry Andric auto IParent = path::begin(Parent), EParent = path::end(Parent);
27710b57cec5SDimitry Andric for (auto IChild = path::begin(Path), EChild = path::end(Path);
27720b57cec5SDimitry Andric IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
27730b57cec5SDimitry Andric if (*IParent != *IChild)
27740b57cec5SDimitry Andric return false;
27750b57cec5SDimitry Andric }
27760b57cec5SDimitry Andric // Have we exhausted the parent path?
27770b57cec5SDimitry Andric return IParent == EParent;
27780b57cec5SDimitry Andric }
27790b57cec5SDimitry Andric
containedPart(StringRef Parent,StringRef Path)27800b57cec5SDimitry Andric StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
27810b57cec5SDimitry Andric assert(!Parent.empty());
27820b57cec5SDimitry Andric assert(containedIn(Parent, Path));
27830b57cec5SDimitry Andric return Path.slice(Parent.size() + 1, StringRef::npos);
27840b57cec5SDimitry Andric }
27850b57cec5SDimitry Andric
startDirectory(StringRef Path)27860b57cec5SDimitry Andric void JSONWriter::startDirectory(StringRef Path) {
27870b57cec5SDimitry Andric StringRef Name =
27880b57cec5SDimitry Andric DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
27890b57cec5SDimitry Andric DirStack.push_back(Path);
27900b57cec5SDimitry Andric unsigned Indent = getDirIndent();
27910b57cec5SDimitry Andric OS.indent(Indent) << "{\n";
27920b57cec5SDimitry Andric OS.indent(Indent + 2) << "'type': 'directory',\n";
27930b57cec5SDimitry Andric OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
27940b57cec5SDimitry Andric OS.indent(Indent + 2) << "'contents': [\n";
27950b57cec5SDimitry Andric }
27960b57cec5SDimitry Andric
endDirectory()27970b57cec5SDimitry Andric void JSONWriter::endDirectory() {
27980b57cec5SDimitry Andric unsigned Indent = getDirIndent();
27990b57cec5SDimitry Andric OS.indent(Indent + 2) << "]\n";
28000b57cec5SDimitry Andric OS.indent(Indent) << "}";
28010b57cec5SDimitry Andric
28020b57cec5SDimitry Andric DirStack.pop_back();
28030b57cec5SDimitry Andric }
28040b57cec5SDimitry Andric
writeEntry(StringRef VPath,StringRef RPath)28050b57cec5SDimitry Andric void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
28060b57cec5SDimitry Andric unsigned Indent = getFileIndent();
28070b57cec5SDimitry Andric OS.indent(Indent) << "{\n";
28080b57cec5SDimitry Andric OS.indent(Indent + 2) << "'type': 'file',\n";
28090b57cec5SDimitry Andric OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
28100b57cec5SDimitry Andric OS.indent(Indent + 2) << "'external-contents': \""
28110b57cec5SDimitry Andric << llvm::yaml::escape(RPath) << "\"\n";
28120b57cec5SDimitry Andric OS.indent(Indent) << "}";
28130b57cec5SDimitry Andric }
28140b57cec5SDimitry Andric
write(ArrayRef<YAMLVFSEntry> Entries,std::optional<bool> UseExternalNames,std::optional<bool> IsCaseSensitive,std::optional<bool> IsOverlayRelative,StringRef OverlayDir)28150b57cec5SDimitry Andric void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
2816bdd1243dSDimitry Andric std::optional<bool> UseExternalNames,
2817bdd1243dSDimitry Andric std::optional<bool> IsCaseSensitive,
2818bdd1243dSDimitry Andric std::optional<bool> IsOverlayRelative,
28190b57cec5SDimitry Andric StringRef OverlayDir) {
28200b57cec5SDimitry Andric using namespace llvm::sys;
28210b57cec5SDimitry Andric
28220b57cec5SDimitry Andric OS << "{\n"
28230b57cec5SDimitry Andric " 'version': 0,\n";
282481ad6265SDimitry Andric if (IsCaseSensitive)
2825bdd1243dSDimitry Andric OS << " 'case-sensitive': '" << (*IsCaseSensitive ? "true" : "false")
2826bdd1243dSDimitry Andric << "',\n";
282781ad6265SDimitry Andric if (UseExternalNames)
2828bdd1243dSDimitry Andric OS << " 'use-external-names': '" << (*UseExternalNames ? "true" : "false")
2829bdd1243dSDimitry Andric << "',\n";
28300b57cec5SDimitry Andric bool UseOverlayRelative = false;
283181ad6265SDimitry Andric if (IsOverlayRelative) {
2832bdd1243dSDimitry Andric UseOverlayRelative = *IsOverlayRelative;
28330b57cec5SDimitry Andric OS << " 'overlay-relative': '" << (UseOverlayRelative ? "true" : "false")
28340b57cec5SDimitry Andric << "',\n";
28350b57cec5SDimitry Andric }
28360b57cec5SDimitry Andric OS << " 'roots': [\n";
28370b57cec5SDimitry Andric
28380b57cec5SDimitry Andric if (!Entries.empty()) {
28390b57cec5SDimitry Andric const YAMLVFSEntry &Entry = Entries.front();
28405ffd83dbSDimitry Andric
28415ffd83dbSDimitry Andric startDirectory(
28425ffd83dbSDimitry Andric Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath)
28435ffd83dbSDimitry Andric );
28440b57cec5SDimitry Andric
28450b57cec5SDimitry Andric StringRef RPath = Entry.RPath;
28460b57cec5SDimitry Andric if (UseOverlayRelative) {
2847*0fca6ea1SDimitry Andric assert(RPath.starts_with(OverlayDir) &&
28480b57cec5SDimitry Andric "Overlay dir must be contained in RPath");
2849*0fca6ea1SDimitry Andric RPath = RPath.slice(OverlayDir.size(), RPath.size());
28500b57cec5SDimitry Andric }
28510b57cec5SDimitry Andric
28525ffd83dbSDimitry Andric bool IsCurrentDirEmpty = true;
28535ffd83dbSDimitry Andric if (!Entry.IsDirectory) {
28540b57cec5SDimitry Andric writeEntry(path::filename(Entry.VPath), RPath);
28555ffd83dbSDimitry Andric IsCurrentDirEmpty = false;
28565ffd83dbSDimitry Andric }
28570b57cec5SDimitry Andric
28580b57cec5SDimitry Andric for (const auto &Entry : Entries.slice(1)) {
28595ffd83dbSDimitry Andric StringRef Dir =
28605ffd83dbSDimitry Andric Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath);
28615ffd83dbSDimitry Andric if (Dir == DirStack.back()) {
28625ffd83dbSDimitry Andric if (!IsCurrentDirEmpty) {
28630b57cec5SDimitry Andric OS << ",\n";
28645ffd83dbSDimitry Andric }
28655ffd83dbSDimitry Andric } else {
28665ffd83dbSDimitry Andric bool IsDirPoppedFromStack = false;
28670b57cec5SDimitry Andric while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
28680b57cec5SDimitry Andric OS << "\n";
28690b57cec5SDimitry Andric endDirectory();
28705ffd83dbSDimitry Andric IsDirPoppedFromStack = true;
28710b57cec5SDimitry Andric }
28725ffd83dbSDimitry Andric if (IsDirPoppedFromStack || !IsCurrentDirEmpty) {
28730b57cec5SDimitry Andric OS << ",\n";
28745ffd83dbSDimitry Andric }
28750b57cec5SDimitry Andric startDirectory(Dir);
28765ffd83dbSDimitry Andric IsCurrentDirEmpty = true;
28770b57cec5SDimitry Andric }
28780b57cec5SDimitry Andric StringRef RPath = Entry.RPath;
28790b57cec5SDimitry Andric if (UseOverlayRelative) {
2880*0fca6ea1SDimitry Andric assert(RPath.starts_with(OverlayDir) &&
28810b57cec5SDimitry Andric "Overlay dir must be contained in RPath");
2882*0fca6ea1SDimitry Andric RPath = RPath.slice(OverlayDir.size(), RPath.size());
28830b57cec5SDimitry Andric }
28845ffd83dbSDimitry Andric if (!Entry.IsDirectory) {
28850b57cec5SDimitry Andric writeEntry(path::filename(Entry.VPath), RPath);
28865ffd83dbSDimitry Andric IsCurrentDirEmpty = false;
28875ffd83dbSDimitry Andric }
28880b57cec5SDimitry Andric }
28890b57cec5SDimitry Andric
28900b57cec5SDimitry Andric while (!DirStack.empty()) {
28910b57cec5SDimitry Andric OS << "\n";
28920b57cec5SDimitry Andric endDirectory();
28930b57cec5SDimitry Andric }
28940b57cec5SDimitry Andric OS << "\n";
28950b57cec5SDimitry Andric }
28960b57cec5SDimitry Andric
28970b57cec5SDimitry Andric OS << " ]\n"
28980b57cec5SDimitry Andric << "}\n";
28990b57cec5SDimitry Andric }
29000b57cec5SDimitry Andric
write(llvm::raw_ostream & OS)29010b57cec5SDimitry Andric void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
29020b57cec5SDimitry Andric llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
29030b57cec5SDimitry Andric return LHS.VPath < RHS.VPath;
29040b57cec5SDimitry Andric });
29050b57cec5SDimitry Andric
29060b57cec5SDimitry Andric JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
29070b57cec5SDimitry Andric IsOverlayRelative, OverlayDir);
29080b57cec5SDimitry Andric }
29090b57cec5SDimitry Andric
recursive_directory_iterator(FileSystem & FS_,const Twine & Path,std::error_code & EC)29100b57cec5SDimitry Andric vfs::recursive_directory_iterator::recursive_directory_iterator(
29110b57cec5SDimitry Andric FileSystem &FS_, const Twine &Path, std::error_code &EC)
29120b57cec5SDimitry Andric : FS(&FS_) {
29130b57cec5SDimitry Andric directory_iterator I = FS->dir_begin(Path, EC);
29140b57cec5SDimitry Andric if (I != directory_iterator()) {
29150b57cec5SDimitry Andric State = std::make_shared<detail::RecDirIterState>();
2916*0fca6ea1SDimitry Andric State->Stack.push_back(I);
29170b57cec5SDimitry Andric }
29180b57cec5SDimitry Andric }
29190b57cec5SDimitry Andric
29200b57cec5SDimitry Andric vfs::recursive_directory_iterator &
increment(std::error_code & EC)29210b57cec5SDimitry Andric recursive_directory_iterator::increment(std::error_code &EC) {
29220b57cec5SDimitry Andric assert(FS && State && !State->Stack.empty() && "incrementing past end");
2923*0fca6ea1SDimitry Andric assert(!State->Stack.back()->path().empty() && "non-canonical end iterator");
29240b57cec5SDimitry Andric vfs::directory_iterator End;
29250b57cec5SDimitry Andric
29260b57cec5SDimitry Andric if (State->HasNoPushRequest)
29270b57cec5SDimitry Andric State->HasNoPushRequest = false;
29280b57cec5SDimitry Andric else {
2929*0fca6ea1SDimitry Andric if (State->Stack.back()->type() == sys::fs::file_type::directory_file) {
2930*0fca6ea1SDimitry Andric vfs::directory_iterator I =
2931*0fca6ea1SDimitry Andric FS->dir_begin(State->Stack.back()->path(), EC);
29320b57cec5SDimitry Andric if (I != End) {
2933*0fca6ea1SDimitry Andric State->Stack.push_back(I);
29340b57cec5SDimitry Andric return *this;
29350b57cec5SDimitry Andric }
29360b57cec5SDimitry Andric }
29370b57cec5SDimitry Andric }
29380b57cec5SDimitry Andric
2939*0fca6ea1SDimitry Andric while (!State->Stack.empty() && State->Stack.back().increment(EC) == End)
2940*0fca6ea1SDimitry Andric State->Stack.pop_back();
29410b57cec5SDimitry Andric
29420b57cec5SDimitry Andric if (State->Stack.empty())
29430b57cec5SDimitry Andric State.reset(); // end iterator
29440b57cec5SDimitry Andric
29450b57cec5SDimitry Andric return *this;
29460b57cec5SDimitry Andric }
2947*0fca6ea1SDimitry Andric
2948*0fca6ea1SDimitry Andric const char FileSystem::ID = 0;
2949*0fca6ea1SDimitry Andric const char OverlayFileSystem::ID = 0;
2950*0fca6ea1SDimitry Andric const char ProxyFileSystem::ID = 0;
2951*0fca6ea1SDimitry Andric const char InMemoryFileSystem::ID = 0;
2952*0fca6ea1SDimitry Andric const char RedirectingFileSystem::ID = 0;
2953