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>
46fe013be4SDimitry 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) const1400b57cec5SDimitry Andric std::error_code FileSystem::getRealPath(const Twine &Path,
1410b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const {
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
15481ad6265SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
dump() const15581ad6265SDimitry Andric void FileSystem::dump() const { print(dbgs(), PrintType::RecursiveContents); }
15681ad6265SDimitry Andric #endif
15781ad6265SDimitry Andric
1580b57cec5SDimitry Andric #ifndef NDEBUG
isTraversalComponent(StringRef Component)1590b57cec5SDimitry Andric static bool isTraversalComponent(StringRef Component) {
1600b57cec5SDimitry Andric return Component.equals("..") || Component.equals(".");
1610b57cec5SDimitry Andric }
1620b57cec5SDimitry Andric
pathHasTraversal(StringRef Path)1630b57cec5SDimitry Andric static bool pathHasTraversal(StringRef Path) {
1640b57cec5SDimitry Andric using namespace llvm::sys;
1650b57cec5SDimitry Andric
1660b57cec5SDimitry Andric for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1670b57cec5SDimitry Andric if (isTraversalComponent(Comp))
1680b57cec5SDimitry Andric return true;
1690b57cec5SDimitry Andric return false;
1700b57cec5SDimitry Andric }
1710b57cec5SDimitry Andric #endif
1720b57cec5SDimitry Andric
1730b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
1740b57cec5SDimitry Andric // RealFileSystem implementation
1750b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
1760b57cec5SDimitry Andric
1770b57cec5SDimitry Andric namespace {
1780b57cec5SDimitry Andric
1790b57cec5SDimitry Andric /// Wrapper around a raw file descriptor.
1800b57cec5SDimitry Andric class RealFile : public File {
1810b57cec5SDimitry Andric friend class RealFileSystem;
1820b57cec5SDimitry Andric
1830b57cec5SDimitry Andric file_t FD;
1840b57cec5SDimitry Andric Status S;
1850b57cec5SDimitry Andric std::string RealName;
1860b57cec5SDimitry Andric
RealFile(file_t RawFD,StringRef NewName,StringRef NewRealPathName)1878bcb0991SDimitry Andric RealFile(file_t RawFD, StringRef NewName, StringRef NewRealPathName)
1888bcb0991SDimitry Andric : FD(RawFD), S(NewName, {}, {}, {}, {}, {},
1890b57cec5SDimitry Andric llvm::sys::fs::file_type::status_error, {}),
1900b57cec5SDimitry Andric RealName(NewRealPathName.str()) {
1910b57cec5SDimitry Andric assert(FD != kInvalidFile && "Invalid or inactive file descriptor");
1920b57cec5SDimitry Andric }
1930b57cec5SDimitry Andric
1940b57cec5SDimitry Andric public:
1950b57cec5SDimitry Andric ~RealFile() override;
1960b57cec5SDimitry Andric
1970b57cec5SDimitry Andric ErrorOr<Status> status() override;
1980b57cec5SDimitry Andric ErrorOr<std::string> getName() override;
1990b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
2000b57cec5SDimitry Andric int64_t FileSize,
2010b57cec5SDimitry Andric bool RequiresNullTerminator,
2020b57cec5SDimitry Andric bool IsVolatile) override;
2030b57cec5SDimitry Andric std::error_code close() override;
204349cc55cSDimitry Andric void setPath(const Twine &Path) override;
2050b57cec5SDimitry Andric };
2060b57cec5SDimitry Andric
2070b57cec5SDimitry Andric } // namespace
2080b57cec5SDimitry Andric
~RealFile()2090b57cec5SDimitry Andric RealFile::~RealFile() { close(); }
2100b57cec5SDimitry Andric
status()2110b57cec5SDimitry Andric ErrorOr<Status> RealFile::status() {
2120b57cec5SDimitry Andric assert(FD != kInvalidFile && "cannot stat closed file");
2130b57cec5SDimitry Andric if (!S.isStatusKnown()) {
2140b57cec5SDimitry Andric file_status RealStatus;
2150b57cec5SDimitry Andric if (std::error_code EC = sys::fs::status(FD, RealStatus))
2160b57cec5SDimitry Andric return EC;
2170b57cec5SDimitry Andric S = Status::copyWithNewName(RealStatus, S.getName());
2180b57cec5SDimitry Andric }
2190b57cec5SDimitry Andric return S;
2200b57cec5SDimitry Andric }
2210b57cec5SDimitry Andric
getName()2220b57cec5SDimitry Andric ErrorOr<std::string> RealFile::getName() {
2230b57cec5SDimitry Andric return RealName.empty() ? S.getName().str() : RealName;
2240b57cec5SDimitry Andric }
2250b57cec5SDimitry Andric
2260b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>>
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)2270b57cec5SDimitry Andric RealFile::getBuffer(const Twine &Name, int64_t FileSize,
2280b57cec5SDimitry Andric bool RequiresNullTerminator, bool IsVolatile) {
2290b57cec5SDimitry Andric assert(FD != kInvalidFile && "cannot get buffer for closed file");
2300b57cec5SDimitry Andric return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
2310b57cec5SDimitry Andric IsVolatile);
2320b57cec5SDimitry Andric }
2330b57cec5SDimitry Andric
close()2340b57cec5SDimitry Andric std::error_code RealFile::close() {
2350b57cec5SDimitry Andric std::error_code EC = sys::fs::closeFile(FD);
2360b57cec5SDimitry Andric FD = kInvalidFile;
2370b57cec5SDimitry Andric return EC;
2380b57cec5SDimitry Andric }
2390b57cec5SDimitry Andric
setPath(const Twine & Path)240349cc55cSDimitry Andric void RealFile::setPath(const Twine &Path) {
241349cc55cSDimitry Andric RealName = Path.str();
242349cc55cSDimitry Andric if (auto Status = status())
243349cc55cSDimitry Andric S = Status.get().copyWithNewName(Status.get(), Path);
244349cc55cSDimitry Andric }
245349cc55cSDimitry Andric
2460b57cec5SDimitry Andric namespace {
2470b57cec5SDimitry Andric
2480b57cec5SDimitry Andric /// A file system according to your operating system.
2490b57cec5SDimitry Andric /// This may be linked to the process's working directory, or maintain its own.
2500b57cec5SDimitry Andric ///
2510b57cec5SDimitry Andric /// Currently, its own working directory is emulated by storing the path and
2520b57cec5SDimitry Andric /// sending absolute paths to llvm::sys::fs:: functions.
2530b57cec5SDimitry Andric /// A more principled approach would be to push this down a level, modelling
2540b57cec5SDimitry Andric /// the working dir as an llvm::sys::fs::WorkingDir or similar.
2550b57cec5SDimitry Andric /// This would enable the use of openat()-style functions on some platforms.
2560b57cec5SDimitry Andric class RealFileSystem : public FileSystem {
2570b57cec5SDimitry Andric public:
RealFileSystem(bool LinkCWDToProcess)2580b57cec5SDimitry Andric explicit RealFileSystem(bool LinkCWDToProcess) {
2590b57cec5SDimitry Andric if (!LinkCWDToProcess) {
2600b57cec5SDimitry Andric SmallString<128> PWD, RealPWD;
261fe013be4SDimitry Andric if (std::error_code EC = llvm::sys::fs::current_path(PWD))
262fe013be4SDimitry Andric WD = EC;
263fe013be4SDimitry Andric else if (llvm::sys::fs::real_path(PWD, RealPWD))
264fe013be4SDimitry Andric WD = WorkingDirectory{PWD, PWD};
2650b57cec5SDimitry Andric else
266fe013be4SDimitry Andric WD = WorkingDirectory{PWD, RealPWD};
2670b57cec5SDimitry Andric }
2680b57cec5SDimitry Andric }
2690b57cec5SDimitry Andric
2700b57cec5SDimitry Andric ErrorOr<Status> status(const Twine &Path) override;
2710b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
2720b57cec5SDimitry Andric directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
2730b57cec5SDimitry Andric
2740b57cec5SDimitry Andric llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
2750b57cec5SDimitry Andric std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
2760b57cec5SDimitry Andric std::error_code isLocal(const Twine &Path, bool &Result) override;
2770b57cec5SDimitry Andric std::error_code getRealPath(const Twine &Path,
2780b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const override;
2790b57cec5SDimitry Andric
28081ad6265SDimitry Andric protected:
28181ad6265SDimitry Andric void printImpl(raw_ostream &OS, PrintType Type,
28281ad6265SDimitry Andric unsigned IndentLevel) const override;
28381ad6265SDimitry Andric
2840b57cec5SDimitry Andric private:
2850b57cec5SDimitry Andric // If this FS has its own working dir, use it to make Path absolute.
2860b57cec5SDimitry Andric // The returned twine is safe to use as long as both Storage and Path live.
adjustPath(const Twine & Path,SmallVectorImpl<char> & Storage) const2870b57cec5SDimitry Andric Twine adjustPath(const Twine &Path, SmallVectorImpl<char> &Storage) const {
288fe013be4SDimitry Andric if (!WD || !*WD)
2890b57cec5SDimitry Andric return Path;
2900b57cec5SDimitry Andric Path.toVector(Storage);
291fe013be4SDimitry Andric sys::fs::make_absolute(WD->get().Resolved, Storage);
2920b57cec5SDimitry Andric return Storage;
2930b57cec5SDimitry Andric }
2940b57cec5SDimitry Andric
2950b57cec5SDimitry Andric struct WorkingDirectory {
2960b57cec5SDimitry Andric // The current working directory, without symlinks resolved. (echo $PWD).
2970b57cec5SDimitry Andric SmallString<128> Specified;
2980b57cec5SDimitry Andric // The current working directory, with links resolved. (readlink .).
2990b57cec5SDimitry Andric SmallString<128> Resolved;
3000b57cec5SDimitry Andric };
301fe013be4SDimitry Andric std::optional<llvm::ErrorOr<WorkingDirectory>> WD;
3020b57cec5SDimitry Andric };
3030b57cec5SDimitry Andric
3040b57cec5SDimitry Andric } // namespace
3050b57cec5SDimitry Andric
status(const Twine & Path)3060b57cec5SDimitry Andric ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
3070b57cec5SDimitry Andric SmallString<256> Storage;
3080b57cec5SDimitry Andric sys::fs::file_status RealStatus;
3090b57cec5SDimitry Andric if (std::error_code EC =
3100b57cec5SDimitry Andric sys::fs::status(adjustPath(Path, Storage), RealStatus))
3110b57cec5SDimitry Andric return EC;
3120b57cec5SDimitry Andric return Status::copyWithNewName(RealStatus, Path);
3130b57cec5SDimitry Andric }
3140b57cec5SDimitry Andric
3150b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Name)3160b57cec5SDimitry Andric RealFileSystem::openFileForRead(const Twine &Name) {
3170b57cec5SDimitry Andric SmallString<256> RealName, Storage;
3180b57cec5SDimitry Andric Expected<file_t> FDOrErr = sys::fs::openNativeFileForRead(
3190b57cec5SDimitry Andric adjustPath(Name, Storage), sys::fs::OF_None, &RealName);
3200b57cec5SDimitry Andric if (!FDOrErr)
3210b57cec5SDimitry Andric return errorToErrorCode(FDOrErr.takeError());
3220b57cec5SDimitry Andric return std::unique_ptr<File>(
3230b57cec5SDimitry Andric new RealFile(*FDOrErr, Name.str(), RealName.str()));
3240b57cec5SDimitry Andric }
3250b57cec5SDimitry Andric
getCurrentWorkingDirectory() const3260b57cec5SDimitry Andric llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
327fe013be4SDimitry Andric if (WD && *WD)
328*a58f00eaSDimitry Andric return std::string(WD->get().Specified);
3290b57cec5SDimitry Andric if (WD)
330fe013be4SDimitry Andric return WD->getError();
3310b57cec5SDimitry Andric
3320b57cec5SDimitry Andric SmallString<128> Dir;
3330b57cec5SDimitry Andric if (std::error_code EC = llvm::sys::fs::current_path(Dir))
3340b57cec5SDimitry Andric return EC;
335*a58f00eaSDimitry Andric return std::string(Dir);
3360b57cec5SDimitry Andric }
3370b57cec5SDimitry Andric
setCurrentWorkingDirectory(const Twine & Path)3380b57cec5SDimitry Andric std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
3390b57cec5SDimitry Andric if (!WD)
3400b57cec5SDimitry Andric return llvm::sys::fs::set_current_path(Path);
3410b57cec5SDimitry Andric
3420b57cec5SDimitry Andric SmallString<128> Absolute, Resolved, Storage;
3430b57cec5SDimitry Andric adjustPath(Path, Storage).toVector(Absolute);
3440b57cec5SDimitry Andric bool IsDir;
3450b57cec5SDimitry Andric if (auto Err = llvm::sys::fs::is_directory(Absolute, IsDir))
3460b57cec5SDimitry Andric return Err;
3470b57cec5SDimitry Andric if (!IsDir)
3480b57cec5SDimitry Andric return std::make_error_code(std::errc::not_a_directory);
3490b57cec5SDimitry Andric if (auto Err = llvm::sys::fs::real_path(Absolute, Resolved))
3500b57cec5SDimitry Andric return Err;
351fe013be4SDimitry Andric WD = WorkingDirectory{Absolute, Resolved};
3520b57cec5SDimitry Andric return std::error_code();
3530b57cec5SDimitry Andric }
3540b57cec5SDimitry Andric
isLocal(const Twine & Path,bool & Result)3550b57cec5SDimitry Andric std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) {
3560b57cec5SDimitry Andric SmallString<256> Storage;
3570b57cec5SDimitry Andric return llvm::sys::fs::is_local(adjustPath(Path, Storage), Result);
3580b57cec5SDimitry Andric }
3590b57cec5SDimitry Andric
3600b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const3610b57cec5SDimitry Andric RealFileSystem::getRealPath(const Twine &Path,
3620b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const {
3630b57cec5SDimitry Andric SmallString<256> Storage;
3640b57cec5SDimitry Andric return llvm::sys::fs::real_path(adjustPath(Path, Storage), Output);
3650b57cec5SDimitry Andric }
3660b57cec5SDimitry Andric
printImpl(raw_ostream & OS,PrintType Type,unsigned IndentLevel) const36781ad6265SDimitry Andric void RealFileSystem::printImpl(raw_ostream &OS, PrintType Type,
36881ad6265SDimitry Andric unsigned IndentLevel) const {
36981ad6265SDimitry Andric printIndent(OS, IndentLevel);
37081ad6265SDimitry Andric OS << "RealFileSystem using ";
37181ad6265SDimitry Andric if (WD)
37281ad6265SDimitry Andric OS << "own";
37381ad6265SDimitry Andric else
37481ad6265SDimitry Andric OS << "process";
37581ad6265SDimitry Andric OS << " CWD\n";
37681ad6265SDimitry Andric }
37781ad6265SDimitry Andric
getRealFileSystem()3780b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
3790b57cec5SDimitry Andric static IntrusiveRefCntPtr<FileSystem> FS(new RealFileSystem(true));
3800b57cec5SDimitry Andric return FS;
3810b57cec5SDimitry Andric }
3820b57cec5SDimitry Andric
createPhysicalFileSystem()3830b57cec5SDimitry Andric std::unique_ptr<FileSystem> vfs::createPhysicalFileSystem() {
3848bcb0991SDimitry Andric return std::make_unique<RealFileSystem>(false);
3850b57cec5SDimitry Andric }
3860b57cec5SDimitry Andric
3870b57cec5SDimitry Andric namespace {
3880b57cec5SDimitry Andric
3890b57cec5SDimitry Andric class RealFSDirIter : public llvm::vfs::detail::DirIterImpl {
3900b57cec5SDimitry Andric llvm::sys::fs::directory_iterator Iter;
3910b57cec5SDimitry Andric
3920b57cec5SDimitry Andric public:
RealFSDirIter(const Twine & Path,std::error_code & EC)3930b57cec5SDimitry Andric RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
3940b57cec5SDimitry Andric if (Iter != llvm::sys::fs::directory_iterator())
3950b57cec5SDimitry Andric CurrentEntry = directory_entry(Iter->path(), Iter->type());
3960b57cec5SDimitry Andric }
3970b57cec5SDimitry Andric
increment()3980b57cec5SDimitry Andric std::error_code increment() override {
3990b57cec5SDimitry Andric std::error_code EC;
4000b57cec5SDimitry Andric Iter.increment(EC);
4010b57cec5SDimitry Andric CurrentEntry = (Iter == llvm::sys::fs::directory_iterator())
4020b57cec5SDimitry Andric ? directory_entry()
4030b57cec5SDimitry Andric : directory_entry(Iter->path(), Iter->type());
4040b57cec5SDimitry Andric return EC;
4050b57cec5SDimitry Andric }
4060b57cec5SDimitry Andric };
4070b57cec5SDimitry Andric
4080b57cec5SDimitry Andric } // namespace
4090b57cec5SDimitry Andric
dir_begin(const Twine & Dir,std::error_code & EC)4100b57cec5SDimitry Andric directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
4110b57cec5SDimitry Andric std::error_code &EC) {
4120b57cec5SDimitry Andric SmallString<128> Storage;
4130b57cec5SDimitry Andric return directory_iterator(
4140b57cec5SDimitry Andric std::make_shared<RealFSDirIter>(adjustPath(Dir, Storage), EC));
4150b57cec5SDimitry Andric }
4160b57cec5SDimitry Andric
4170b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
4180b57cec5SDimitry Andric // OverlayFileSystem implementation
4190b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
4200b57cec5SDimitry Andric
OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS)4210b57cec5SDimitry Andric OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
4220b57cec5SDimitry Andric FSList.push_back(std::move(BaseFS));
4230b57cec5SDimitry Andric }
4240b57cec5SDimitry Andric
pushOverlay(IntrusiveRefCntPtr<FileSystem> FS)4250b57cec5SDimitry Andric void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
4260b57cec5SDimitry Andric FSList.push_back(FS);
4270b57cec5SDimitry Andric // Synchronize added file systems by duplicating the working directory from
4280b57cec5SDimitry Andric // the first one in the list.
4290b57cec5SDimitry Andric FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
4300b57cec5SDimitry Andric }
4310b57cec5SDimitry Andric
status(const Twine & Path)4320b57cec5SDimitry Andric ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
4330b57cec5SDimitry Andric // FIXME: handle symlinks that cross file systems
4340b57cec5SDimitry Andric for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
4350b57cec5SDimitry Andric ErrorOr<Status> Status = (*I)->status(Path);
4360b57cec5SDimitry Andric if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
4370b57cec5SDimitry Andric return Status;
4380b57cec5SDimitry Andric }
4390b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory);
4400b57cec5SDimitry Andric }
4410b57cec5SDimitry Andric
4420b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const llvm::Twine & Path)4430b57cec5SDimitry Andric OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
4440b57cec5SDimitry Andric // FIXME: handle symlinks that cross file systems
4450b57cec5SDimitry Andric for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
4460b57cec5SDimitry Andric auto Result = (*I)->openFileForRead(Path);
4470b57cec5SDimitry Andric if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
4480b57cec5SDimitry Andric return Result;
4490b57cec5SDimitry Andric }
4500b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory);
4510b57cec5SDimitry Andric }
4520b57cec5SDimitry Andric
4530b57cec5SDimitry Andric llvm::ErrorOr<std::string>
getCurrentWorkingDirectory() const4540b57cec5SDimitry Andric OverlayFileSystem::getCurrentWorkingDirectory() const {
4550b57cec5SDimitry Andric // All file systems are synchronized, just take the first working directory.
4560b57cec5SDimitry Andric return FSList.front()->getCurrentWorkingDirectory();
4570b57cec5SDimitry Andric }
4580b57cec5SDimitry Andric
4590b57cec5SDimitry Andric std::error_code
setCurrentWorkingDirectory(const Twine & Path)4600b57cec5SDimitry Andric OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
4610b57cec5SDimitry Andric for (auto &FS : FSList)
4620b57cec5SDimitry Andric if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
4630b57cec5SDimitry Andric return EC;
4640b57cec5SDimitry Andric return {};
4650b57cec5SDimitry Andric }
4660b57cec5SDimitry Andric
isLocal(const Twine & Path,bool & Result)4670b57cec5SDimitry Andric std::error_code OverlayFileSystem::isLocal(const Twine &Path, bool &Result) {
4680b57cec5SDimitry Andric for (auto &FS : FSList)
4690b57cec5SDimitry Andric if (FS->exists(Path))
4700b57cec5SDimitry Andric return FS->isLocal(Path, Result);
4710b57cec5SDimitry Andric return errc::no_such_file_or_directory;
4720b57cec5SDimitry Andric }
4730b57cec5SDimitry Andric
4740b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const4750b57cec5SDimitry Andric OverlayFileSystem::getRealPath(const Twine &Path,
4760b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const {
477349cc55cSDimitry Andric for (const auto &FS : FSList)
4780b57cec5SDimitry Andric if (FS->exists(Path))
4790b57cec5SDimitry Andric return FS->getRealPath(Path, Output);
4800b57cec5SDimitry Andric return errc::no_such_file_or_directory;
4810b57cec5SDimitry Andric }
4820b57cec5SDimitry Andric
printImpl(raw_ostream & OS,PrintType Type,unsigned IndentLevel) const48381ad6265SDimitry Andric void OverlayFileSystem::printImpl(raw_ostream &OS, PrintType Type,
48481ad6265SDimitry Andric unsigned IndentLevel) const {
48581ad6265SDimitry Andric printIndent(OS, IndentLevel);
48681ad6265SDimitry Andric OS << "OverlayFileSystem\n";
48781ad6265SDimitry Andric if (Type == PrintType::Summary)
48881ad6265SDimitry Andric return;
48981ad6265SDimitry Andric
49081ad6265SDimitry Andric if (Type == PrintType::Contents)
49181ad6265SDimitry Andric Type = PrintType::Summary;
492c9157d92SDimitry Andric for (const auto &FS : overlays_range())
49381ad6265SDimitry Andric FS->print(OS, Type, IndentLevel + 1);
49481ad6265SDimitry Andric }
49581ad6265SDimitry Andric
4960b57cec5SDimitry Andric llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default;
4970b57cec5SDimitry Andric
4980b57cec5SDimitry Andric namespace {
4990b57cec5SDimitry Andric
500fe6060f1SDimitry Andric /// Combines and deduplicates directory entries across multiple file systems.
501fe6060f1SDimitry Andric class CombiningDirIterImpl : public llvm::vfs::detail::DirIterImpl {
502fe6060f1SDimitry Andric using FileSystemPtr = llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>;
503fe6060f1SDimitry Andric
50481ad6265SDimitry Andric /// Iterators to combine, processed in reverse order.
50581ad6265SDimitry Andric SmallVector<directory_iterator, 8> IterList;
50681ad6265SDimitry Andric /// The iterator currently being traversed.
5070b57cec5SDimitry Andric directory_iterator CurrentDirIter;
508fe6060f1SDimitry Andric /// The set of names already returned as entries.
5090b57cec5SDimitry Andric llvm::StringSet<> SeenNames;
5100b57cec5SDimitry Andric
51181ad6265SDimitry Andric /// Sets \c CurrentDirIter to the next iterator in the list, or leaves it as
51281ad6265SDimitry Andric /// is (at its end position) if we've already gone through them all.
incrementIter(bool IsFirstTime)51381ad6265SDimitry Andric std::error_code incrementIter(bool IsFirstTime) {
51481ad6265SDimitry Andric while (!IterList.empty()) {
51581ad6265SDimitry Andric CurrentDirIter = IterList.back();
51681ad6265SDimitry Andric IterList.pop_back();
5170b57cec5SDimitry Andric if (CurrentDirIter != directory_iterator())
5180b57cec5SDimitry Andric break; // found
5190b57cec5SDimitry Andric }
52081ad6265SDimitry Andric
52181ad6265SDimitry Andric if (IsFirstTime && CurrentDirIter == directory_iterator())
52281ad6265SDimitry Andric return errc::no_such_file_or_directory;
5230b57cec5SDimitry Andric return {};
5240b57cec5SDimitry Andric }
5250b57cec5SDimitry Andric
incrementDirIter(bool IsFirstTime)5260b57cec5SDimitry Andric std::error_code incrementDirIter(bool IsFirstTime) {
5270b57cec5SDimitry Andric assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
5280b57cec5SDimitry Andric "incrementing past end");
5290b57cec5SDimitry Andric std::error_code EC;
5300b57cec5SDimitry Andric if (!IsFirstTime)
5310b57cec5SDimitry Andric CurrentDirIter.increment(EC);
5320b57cec5SDimitry Andric if (!EC && CurrentDirIter == directory_iterator())
53381ad6265SDimitry Andric EC = incrementIter(IsFirstTime);
5340b57cec5SDimitry Andric return EC;
5350b57cec5SDimitry Andric }
5360b57cec5SDimitry Andric
incrementImpl(bool IsFirstTime)5370b57cec5SDimitry Andric std::error_code incrementImpl(bool IsFirstTime) {
5380b57cec5SDimitry Andric while (true) {
5390b57cec5SDimitry Andric std::error_code EC = incrementDirIter(IsFirstTime);
5400b57cec5SDimitry Andric if (EC || CurrentDirIter == directory_iterator()) {
5410b57cec5SDimitry Andric CurrentEntry = directory_entry();
5420b57cec5SDimitry Andric return EC;
5430b57cec5SDimitry Andric }
5440b57cec5SDimitry Andric CurrentEntry = *CurrentDirIter;
5450b57cec5SDimitry Andric StringRef Name = llvm::sys::path::filename(CurrentEntry.path());
5460b57cec5SDimitry Andric if (SeenNames.insert(Name).second)
5470b57cec5SDimitry Andric return EC; // name not seen before
5480b57cec5SDimitry Andric }
5490b57cec5SDimitry Andric llvm_unreachable("returned above");
5500b57cec5SDimitry Andric }
5510b57cec5SDimitry Andric
5520b57cec5SDimitry Andric public:
CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems,std::string Dir,std::error_code & EC)553fe6060f1SDimitry Andric CombiningDirIterImpl(ArrayRef<FileSystemPtr> FileSystems, std::string Dir,
55481ad6265SDimitry Andric std::error_code &EC) {
555c9157d92SDimitry Andric for (const auto &FS : FileSystems) {
55681ad6265SDimitry Andric std::error_code FEC;
55781ad6265SDimitry Andric directory_iterator Iter = FS->dir_begin(Dir, FEC);
55881ad6265SDimitry Andric if (FEC && FEC != errc::no_such_file_or_directory) {
55981ad6265SDimitry Andric EC = FEC;
56081ad6265SDimitry Andric return;
56181ad6265SDimitry Andric }
56281ad6265SDimitry Andric if (!FEC)
56381ad6265SDimitry Andric IterList.push_back(Iter);
56481ad6265SDimitry Andric }
565fe6060f1SDimitry Andric EC = incrementImpl(true);
566fe6060f1SDimitry Andric }
567fe6060f1SDimitry Andric
CombiningDirIterImpl(ArrayRef<directory_iterator> DirIters,std::error_code & EC)56881ad6265SDimitry Andric CombiningDirIterImpl(ArrayRef<directory_iterator> DirIters,
56981ad6265SDimitry Andric std::error_code &EC)
57081ad6265SDimitry Andric : IterList(DirIters.begin(), DirIters.end()) {
5710b57cec5SDimitry Andric EC = incrementImpl(true);
5720b57cec5SDimitry Andric }
5730b57cec5SDimitry Andric
increment()5740b57cec5SDimitry Andric std::error_code increment() override { return incrementImpl(false); }
5750b57cec5SDimitry Andric };
5760b57cec5SDimitry Andric
5770b57cec5SDimitry Andric } // namespace
5780b57cec5SDimitry Andric
dir_begin(const Twine & Dir,std::error_code & EC)5790b57cec5SDimitry Andric directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
5800b57cec5SDimitry Andric std::error_code &EC) {
58181ad6265SDimitry Andric directory_iterator Combined = directory_iterator(
582fe6060f1SDimitry Andric std::make_shared<CombiningDirIterImpl>(FSList, Dir.str(), EC));
58381ad6265SDimitry Andric if (EC)
58481ad6265SDimitry Andric return {};
58581ad6265SDimitry Andric return Combined;
5860b57cec5SDimitry Andric }
5870b57cec5SDimitry Andric
anchor()5880b57cec5SDimitry Andric void ProxyFileSystem::anchor() {}
5890b57cec5SDimitry Andric
5900b57cec5SDimitry Andric namespace llvm {
5910b57cec5SDimitry Andric namespace vfs {
5920b57cec5SDimitry Andric
5930b57cec5SDimitry Andric namespace detail {
5940b57cec5SDimitry Andric
59581ad6265SDimitry Andric enum InMemoryNodeKind {
59681ad6265SDimitry Andric IME_File,
59781ad6265SDimitry Andric IME_Directory,
59881ad6265SDimitry Andric IME_HardLink,
59981ad6265SDimitry Andric IME_SymbolicLink,
60081ad6265SDimitry Andric };
6010b57cec5SDimitry Andric
6020b57cec5SDimitry Andric /// The in memory file system is a tree of Nodes. Every node can either be a
60381ad6265SDimitry Andric /// file, symlink, hardlink or a directory.
6040b57cec5SDimitry Andric class InMemoryNode {
6050b57cec5SDimitry Andric InMemoryNodeKind Kind;
6060b57cec5SDimitry Andric std::string FileName;
6070b57cec5SDimitry Andric
6080b57cec5SDimitry Andric public:
InMemoryNode(llvm::StringRef FileName,InMemoryNodeKind Kind)6090b57cec5SDimitry Andric InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind)
6105ffd83dbSDimitry Andric : Kind(Kind), FileName(std::string(llvm::sys::path::filename(FileName))) {
6115ffd83dbSDimitry Andric }
6120b57cec5SDimitry Andric virtual ~InMemoryNode() = default;
6130b57cec5SDimitry Andric
61404eeddc0SDimitry Andric /// Return the \p Status for this node. \p RequestedName should be the name
61504eeddc0SDimitry Andric /// through which the caller referred to this node. It will override
61604eeddc0SDimitry Andric /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
61704eeddc0SDimitry Andric virtual Status getStatus(const Twine &RequestedName) const = 0;
61804eeddc0SDimitry Andric
6190b57cec5SDimitry Andric /// Get the filename of this node (the name without the directory part).
getFileName() const6200b57cec5SDimitry Andric StringRef getFileName() const { return FileName; }
getKind() const6210b57cec5SDimitry Andric InMemoryNodeKind getKind() const { return Kind; }
6220b57cec5SDimitry Andric virtual std::string toString(unsigned Indent) const = 0;
6230b57cec5SDimitry Andric };
6240b57cec5SDimitry Andric
6250b57cec5SDimitry Andric class InMemoryFile : public InMemoryNode {
6260b57cec5SDimitry Andric Status Stat;
6270b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer;
6280b57cec5SDimitry Andric
6290b57cec5SDimitry Andric public:
InMemoryFile(Status Stat,std::unique_ptr<llvm::MemoryBuffer> Buffer)6300b57cec5SDimitry Andric InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
6310b57cec5SDimitry Andric : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)),
6320b57cec5SDimitry Andric Buffer(std::move(Buffer)) {}
6330b57cec5SDimitry Andric
getStatus(const Twine & RequestedName) const63404eeddc0SDimitry Andric Status getStatus(const Twine &RequestedName) const override {
6350b57cec5SDimitry Andric return Status::copyWithNewName(Stat, RequestedName);
6360b57cec5SDimitry Andric }
getBuffer() const6370b57cec5SDimitry Andric llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); }
6380b57cec5SDimitry Andric
toString(unsigned Indent) const6390b57cec5SDimitry Andric std::string toString(unsigned Indent) const override {
6400b57cec5SDimitry Andric return (std::string(Indent, ' ') + Stat.getName() + "\n").str();
6410b57cec5SDimitry Andric }
6420b57cec5SDimitry Andric
classof(const InMemoryNode * N)6430b57cec5SDimitry Andric static bool classof(const InMemoryNode *N) {
6440b57cec5SDimitry Andric return N->getKind() == IME_File;
6450b57cec5SDimitry Andric }
6460b57cec5SDimitry Andric };
6470b57cec5SDimitry Andric
6480b57cec5SDimitry Andric namespace {
6490b57cec5SDimitry Andric
6500b57cec5SDimitry Andric class InMemoryHardLink : public InMemoryNode {
6510b57cec5SDimitry Andric const InMemoryFile &ResolvedFile;
6520b57cec5SDimitry Andric
6530b57cec5SDimitry Andric public:
InMemoryHardLink(StringRef Path,const InMemoryFile & ResolvedFile)6540b57cec5SDimitry Andric InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile)
6550b57cec5SDimitry Andric : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {}
getResolvedFile() const6560b57cec5SDimitry Andric const InMemoryFile &getResolvedFile() const { return ResolvedFile; }
6570b57cec5SDimitry Andric
getStatus(const Twine & RequestedName) const65804eeddc0SDimitry Andric Status getStatus(const Twine &RequestedName) const override {
65904eeddc0SDimitry Andric return ResolvedFile.getStatus(RequestedName);
66004eeddc0SDimitry Andric }
66104eeddc0SDimitry Andric
toString(unsigned Indent) const6620b57cec5SDimitry Andric std::string toString(unsigned Indent) const override {
6630b57cec5SDimitry Andric return std::string(Indent, ' ') + "HardLink to -> " +
6640b57cec5SDimitry Andric ResolvedFile.toString(0);
6650b57cec5SDimitry Andric }
6660b57cec5SDimitry Andric
classof(const InMemoryNode * N)6670b57cec5SDimitry Andric static bool classof(const InMemoryNode *N) {
6680b57cec5SDimitry Andric return N->getKind() == IME_HardLink;
6690b57cec5SDimitry Andric }
6700b57cec5SDimitry Andric };
6710b57cec5SDimitry Andric
67281ad6265SDimitry Andric class InMemorySymbolicLink : public InMemoryNode {
67381ad6265SDimitry Andric std::string TargetPath;
67481ad6265SDimitry Andric Status Stat;
67581ad6265SDimitry Andric
67681ad6265SDimitry Andric public:
InMemorySymbolicLink(StringRef Path,StringRef TargetPath,Status Stat)67781ad6265SDimitry Andric InMemorySymbolicLink(StringRef Path, StringRef TargetPath, Status Stat)
67881ad6265SDimitry Andric : InMemoryNode(Path, IME_SymbolicLink), TargetPath(std::move(TargetPath)),
67981ad6265SDimitry Andric Stat(Stat) {}
68081ad6265SDimitry Andric
toString(unsigned Indent) const68181ad6265SDimitry Andric std::string toString(unsigned Indent) const override {
68281ad6265SDimitry Andric return std::string(Indent, ' ') + "SymbolicLink to -> " + TargetPath;
68381ad6265SDimitry Andric }
68481ad6265SDimitry Andric
getStatus(const Twine & RequestedName) const68581ad6265SDimitry Andric Status getStatus(const Twine &RequestedName) const override {
68681ad6265SDimitry Andric return Status::copyWithNewName(Stat, RequestedName);
68781ad6265SDimitry Andric }
68881ad6265SDimitry Andric
getTargetPath() const68981ad6265SDimitry Andric StringRef getTargetPath() const { return TargetPath; }
69081ad6265SDimitry Andric
classof(const InMemoryNode * N)69181ad6265SDimitry Andric static bool classof(const InMemoryNode *N) {
69281ad6265SDimitry Andric return N->getKind() == IME_SymbolicLink;
69381ad6265SDimitry Andric }
69481ad6265SDimitry Andric };
69581ad6265SDimitry Andric
6960b57cec5SDimitry Andric /// Adapt a InMemoryFile for VFS' File interface. The goal is to make
6970b57cec5SDimitry Andric /// \p InMemoryFileAdaptor mimic as much as possible the behavior of
6980b57cec5SDimitry Andric /// \p RealFile.
6990b57cec5SDimitry Andric class InMemoryFileAdaptor : public File {
7000b57cec5SDimitry Andric const InMemoryFile &Node;
7010b57cec5SDimitry Andric /// The name to use when returning a Status for this file.
7020b57cec5SDimitry Andric std::string RequestedName;
7030b57cec5SDimitry Andric
7040b57cec5SDimitry Andric public:
InMemoryFileAdaptor(const InMemoryFile & Node,std::string RequestedName)7050b57cec5SDimitry Andric explicit InMemoryFileAdaptor(const InMemoryFile &Node,
7060b57cec5SDimitry Andric std::string RequestedName)
7070b57cec5SDimitry Andric : Node(Node), RequestedName(std::move(RequestedName)) {}
7080b57cec5SDimitry Andric
status()7090b57cec5SDimitry Andric llvm::ErrorOr<Status> status() override {
7100b57cec5SDimitry Andric return Node.getStatus(RequestedName);
7110b57cec5SDimitry Andric }
7120b57cec5SDimitry Andric
7130b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)7140b57cec5SDimitry Andric getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
7150b57cec5SDimitry Andric bool IsVolatile) override {
7160b57cec5SDimitry Andric llvm::MemoryBuffer *Buf = Node.getBuffer();
7170b57cec5SDimitry Andric return llvm::MemoryBuffer::getMemBuffer(
7180b57cec5SDimitry Andric Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
7190b57cec5SDimitry Andric }
7200b57cec5SDimitry Andric
close()7210b57cec5SDimitry Andric std::error_code close() override { return {}; }
722349cc55cSDimitry Andric
setPath(const Twine & Path)723349cc55cSDimitry Andric void setPath(const Twine &Path) override { RequestedName = Path.str(); }
7240b57cec5SDimitry Andric };
7250b57cec5SDimitry Andric } // namespace
7260b57cec5SDimitry Andric
7270b57cec5SDimitry Andric class InMemoryDirectory : public InMemoryNode {
7280b57cec5SDimitry Andric Status Stat;
729fe013be4SDimitry Andric std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
7300b57cec5SDimitry Andric
7310b57cec5SDimitry Andric public:
InMemoryDirectory(Status Stat)7320b57cec5SDimitry Andric InMemoryDirectory(Status Stat)
7330b57cec5SDimitry Andric : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {}
7340b57cec5SDimitry Andric
7350b57cec5SDimitry Andric /// Return the \p Status for this node. \p RequestedName should be the name
7360b57cec5SDimitry Andric /// through which the caller referred to this node. It will override
7370b57cec5SDimitry Andric /// \p Status::Name in the return value, to mimic the behavior of \p RealFile.
getStatus(const Twine & RequestedName) const73804eeddc0SDimitry Andric Status getStatus(const Twine &RequestedName) const override {
7390b57cec5SDimitry Andric return Status::copyWithNewName(Stat, RequestedName);
7400b57cec5SDimitry Andric }
741349cc55cSDimitry Andric
getUniqueID() const742349cc55cSDimitry Andric UniqueID getUniqueID() const { return Stat.getUniqueID(); }
743349cc55cSDimitry Andric
getChild(StringRef Name) const74481ad6265SDimitry Andric InMemoryNode *getChild(StringRef Name) const {
745fe013be4SDimitry Andric auto I = Entries.find(Name.str());
7460b57cec5SDimitry Andric if (I != Entries.end())
7470b57cec5SDimitry Andric return I->second.get();
7480b57cec5SDimitry Andric return nullptr;
7490b57cec5SDimitry Andric }
7500b57cec5SDimitry Andric
addChild(StringRef Name,std::unique_ptr<InMemoryNode> Child)7510b57cec5SDimitry Andric InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
752fe013be4SDimitry Andric return Entries.emplace(Name, std::move(Child)).first->second.get();
7530b57cec5SDimitry Andric }
7540b57cec5SDimitry Andric
7550b57cec5SDimitry Andric using const_iterator = decltype(Entries)::const_iterator;
7560b57cec5SDimitry Andric
begin() const7570b57cec5SDimitry Andric const_iterator begin() const { return Entries.begin(); }
end() const7580b57cec5SDimitry Andric const_iterator end() const { return Entries.end(); }
7590b57cec5SDimitry Andric
toString(unsigned Indent) const7600b57cec5SDimitry Andric std::string toString(unsigned Indent) const override {
7610b57cec5SDimitry Andric std::string Result =
7620b57cec5SDimitry Andric (std::string(Indent, ' ') + Stat.getName() + "\n").str();
7630b57cec5SDimitry Andric for (const auto &Entry : Entries)
7640b57cec5SDimitry Andric Result += Entry.second->toString(Indent + 2);
7650b57cec5SDimitry Andric return Result;
7660b57cec5SDimitry Andric }
7670b57cec5SDimitry Andric
classof(const InMemoryNode * N)7680b57cec5SDimitry Andric static bool classof(const InMemoryNode *N) {
7690b57cec5SDimitry Andric return N->getKind() == IME_Directory;
7700b57cec5SDimitry Andric }
7710b57cec5SDimitry Andric };
7720b57cec5SDimitry Andric
7730b57cec5SDimitry Andric } // namespace detail
7740b57cec5SDimitry Andric
775349cc55cSDimitry Andric // The UniqueID of in-memory files is derived from path and content.
776349cc55cSDimitry Andric // This avoids difficulties in creating exactly equivalent in-memory FSes,
777349cc55cSDimitry Andric // as often needed in multithreaded programs.
getUniqueID(hash_code Hash)778349cc55cSDimitry Andric static sys::fs::UniqueID getUniqueID(hash_code Hash) {
779349cc55cSDimitry Andric return sys::fs::UniqueID(std::numeric_limits<uint64_t>::max(),
780349cc55cSDimitry Andric uint64_t(size_t(Hash)));
781349cc55cSDimitry Andric }
getFileID(sys::fs::UniqueID Parent,llvm::StringRef Name,llvm::StringRef Contents)782349cc55cSDimitry Andric static sys::fs::UniqueID getFileID(sys::fs::UniqueID Parent,
783349cc55cSDimitry Andric llvm::StringRef Name,
784349cc55cSDimitry Andric llvm::StringRef Contents) {
785349cc55cSDimitry Andric return getUniqueID(llvm::hash_combine(Parent.getFile(), Name, Contents));
786349cc55cSDimitry Andric }
getDirectoryID(sys::fs::UniqueID Parent,llvm::StringRef Name)787349cc55cSDimitry Andric static sys::fs::UniqueID getDirectoryID(sys::fs::UniqueID Parent,
788349cc55cSDimitry Andric llvm::StringRef Name) {
789349cc55cSDimitry Andric return getUniqueID(llvm::hash_combine(Parent.getFile(), Name));
790349cc55cSDimitry Andric }
791349cc55cSDimitry Andric
makeStatus() const79204eeddc0SDimitry Andric Status detail::NewInMemoryNodeInfo::makeStatus() const {
79304eeddc0SDimitry Andric UniqueID UID =
79404eeddc0SDimitry Andric (Type == sys::fs::file_type::directory_file)
79504eeddc0SDimitry Andric ? getDirectoryID(DirUID, Name)
79604eeddc0SDimitry Andric : getFileID(DirUID, Name, Buffer ? Buffer->getBuffer() : "");
79704eeddc0SDimitry Andric
79804eeddc0SDimitry Andric return Status(Path, UID, llvm::sys::toTimePoint(ModificationTime), User,
79904eeddc0SDimitry Andric Group, Buffer ? Buffer->getBufferSize() : 0, Type, Perms);
80004eeddc0SDimitry Andric }
80104eeddc0SDimitry Andric
InMemoryFileSystem(bool UseNormalizedPaths)8020b57cec5SDimitry Andric InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
8030b57cec5SDimitry Andric : Root(new detail::InMemoryDirectory(
804349cc55cSDimitry Andric Status("", getDirectoryID(llvm::sys::fs::UniqueID(), ""),
805349cc55cSDimitry Andric llvm::sys::TimePoint<>(), 0, 0, 0,
806349cc55cSDimitry Andric llvm::sys::fs::file_type::directory_file,
8070b57cec5SDimitry Andric llvm::sys::fs::perms::all_all))),
8080b57cec5SDimitry Andric UseNormalizedPaths(UseNormalizedPaths) {}
8090b57cec5SDimitry Andric
8100b57cec5SDimitry Andric InMemoryFileSystem::~InMemoryFileSystem() = default;
8110b57cec5SDimitry Andric
toString() const8120b57cec5SDimitry Andric std::string InMemoryFileSystem::toString() const {
8130b57cec5SDimitry Andric return Root->toString(/*Indent=*/0);
8140b57cec5SDimitry Andric }
8150b57cec5SDimitry 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)8160b57cec5SDimitry Andric bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
8170b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer,
818bdd1243dSDimitry Andric std::optional<uint32_t> User,
819bdd1243dSDimitry Andric std::optional<uint32_t> Group,
820bdd1243dSDimitry Andric std::optional<llvm::sys::fs::file_type> Type,
821bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms,
82204eeddc0SDimitry Andric MakeNodeFn MakeNode) {
8230b57cec5SDimitry Andric SmallString<128> Path;
8240b57cec5SDimitry Andric P.toVector(Path);
8250b57cec5SDimitry Andric
8260b57cec5SDimitry Andric // Fix up relative paths. This just prepends the current working directory.
8270b57cec5SDimitry Andric std::error_code EC = makeAbsolute(Path);
8280b57cec5SDimitry Andric assert(!EC);
8290b57cec5SDimitry Andric (void)EC;
8300b57cec5SDimitry Andric
8310b57cec5SDimitry Andric if (useNormalizedPaths())
8320b57cec5SDimitry Andric llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
8330b57cec5SDimitry Andric
8340b57cec5SDimitry Andric if (Path.empty())
8350b57cec5SDimitry Andric return false;
8360b57cec5SDimitry Andric
8370b57cec5SDimitry Andric detail::InMemoryDirectory *Dir = Root.get();
8380b57cec5SDimitry Andric auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path);
83981ad6265SDimitry Andric const auto ResolvedUser = User.value_or(0);
84081ad6265SDimitry Andric const auto ResolvedGroup = Group.value_or(0);
84181ad6265SDimitry Andric const auto ResolvedType = Type.value_or(sys::fs::file_type::regular_file);
84281ad6265SDimitry Andric const auto ResolvedPerms = Perms.value_or(sys::fs::all_all);
8430b57cec5SDimitry Andric // Any intermediate directories we create should be accessible by
8440b57cec5SDimitry Andric // the owner, even if Perms says otherwise for the final path.
8450b57cec5SDimitry Andric const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all;
8460b57cec5SDimitry Andric while (true) {
8470b57cec5SDimitry Andric StringRef Name = *I;
8480b57cec5SDimitry Andric detail::InMemoryNode *Node = Dir->getChild(Name);
8490b57cec5SDimitry Andric ++I;
8500b57cec5SDimitry Andric if (!Node) {
8510b57cec5SDimitry Andric if (I == E) {
8520b57cec5SDimitry Andric // End of the path.
85304eeddc0SDimitry Andric Dir->addChild(
85404eeddc0SDimitry Andric Name, MakeNode({Dir->getUniqueID(), Path, Name, ModificationTime,
85504eeddc0SDimitry Andric std::move(Buffer), ResolvedUser, ResolvedGroup,
85604eeddc0SDimitry Andric ResolvedType, ResolvedPerms}));
8570b57cec5SDimitry Andric return true;
8580b57cec5SDimitry Andric }
8590b57cec5SDimitry Andric
8600b57cec5SDimitry Andric // Create a new directory. Use the path up to here.
8610b57cec5SDimitry Andric Status Stat(
8620b57cec5SDimitry Andric StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
863349cc55cSDimitry Andric getDirectoryID(Dir->getUniqueID(), Name),
864349cc55cSDimitry Andric llvm::sys::toTimePoint(ModificationTime), ResolvedUser, ResolvedGroup,
865349cc55cSDimitry Andric 0, sys::fs::file_type::directory_file, NewDirectoryPerms);
8660b57cec5SDimitry Andric Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
8678bcb0991SDimitry Andric Name, std::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
8680b57cec5SDimitry Andric continue;
8690b57cec5SDimitry Andric }
8700b57cec5SDimitry Andric
8710b57cec5SDimitry Andric if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) {
8720b57cec5SDimitry Andric Dir = NewDir;
8730b57cec5SDimitry Andric } else {
8740b57cec5SDimitry Andric assert((isa<detail::InMemoryFile>(Node) ||
8750b57cec5SDimitry Andric isa<detail::InMemoryHardLink>(Node)) &&
8760b57cec5SDimitry Andric "Must be either file, hardlink or directory!");
8770b57cec5SDimitry Andric
8780b57cec5SDimitry Andric // Trying to insert a directory in place of a file.
8790b57cec5SDimitry Andric if (I != E)
8800b57cec5SDimitry Andric return false;
8810b57cec5SDimitry Andric
8820b57cec5SDimitry Andric // Return false only if the new file is different from the existing one.
8830b57cec5SDimitry Andric if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) {
8840b57cec5SDimitry Andric return Link->getResolvedFile().getBuffer()->getBuffer() ==
8850b57cec5SDimitry Andric Buffer->getBuffer();
8860b57cec5SDimitry Andric }
8870b57cec5SDimitry Andric return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
8880b57cec5SDimitry Andric Buffer->getBuffer();
8890b57cec5SDimitry Andric }
8900b57cec5SDimitry Andric }
8910b57cec5SDimitry Andric }
8920b57cec5SDimitry 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)8930b57cec5SDimitry Andric bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
8940b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer,
895bdd1243dSDimitry Andric std::optional<uint32_t> User,
896bdd1243dSDimitry Andric std::optional<uint32_t> Group,
897bdd1243dSDimitry Andric std::optional<llvm::sys::fs::file_type> Type,
898bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms) {
8990b57cec5SDimitry Andric return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type,
90004eeddc0SDimitry Andric Perms,
90104eeddc0SDimitry Andric [](detail::NewInMemoryNodeInfo NNI)
90204eeddc0SDimitry Andric -> std::unique_ptr<detail::InMemoryNode> {
90304eeddc0SDimitry Andric Status Stat = NNI.makeStatus();
90404eeddc0SDimitry Andric if (Stat.getType() == sys::fs::file_type::directory_file)
90504eeddc0SDimitry Andric return std::make_unique<detail::InMemoryDirectory>(Stat);
90604eeddc0SDimitry Andric return std::make_unique<detail::InMemoryFile>(
90704eeddc0SDimitry Andric Stat, std::move(NNI.Buffer));
90804eeddc0SDimitry Andric });
9090b57cec5SDimitry Andric }
9100b57cec5SDimitry 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)911bdd1243dSDimitry Andric bool InMemoryFileSystem::addFileNoOwn(
912bdd1243dSDimitry Andric const Twine &P, time_t ModificationTime,
913bdd1243dSDimitry Andric const llvm::MemoryBufferRef &Buffer, std::optional<uint32_t> User,
914bdd1243dSDimitry Andric std::optional<uint32_t> Group, std::optional<llvm::sys::fs::file_type> Type,
915bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms) {
916e8d8bef9SDimitry Andric return addFile(P, ModificationTime, llvm::MemoryBuffer::getMemBuffer(Buffer),
9170b57cec5SDimitry Andric std::move(User), std::move(Group), std::move(Type),
91804eeddc0SDimitry Andric std::move(Perms),
91904eeddc0SDimitry Andric [](detail::NewInMemoryNodeInfo NNI)
92004eeddc0SDimitry Andric -> std::unique_ptr<detail::InMemoryNode> {
92104eeddc0SDimitry Andric Status Stat = NNI.makeStatus();
92204eeddc0SDimitry Andric if (Stat.getType() == sys::fs::file_type::directory_file)
92304eeddc0SDimitry Andric return std::make_unique<detail::InMemoryDirectory>(Stat);
92404eeddc0SDimitry Andric return std::make_unique<detail::InMemoryFile>(
92504eeddc0SDimitry Andric Stat, std::move(NNI.Buffer));
92604eeddc0SDimitry Andric });
9270b57cec5SDimitry Andric }
9280b57cec5SDimitry Andric
92981ad6265SDimitry Andric detail::NamedNodeOrError
lookupNode(const Twine & P,bool FollowFinalSymlink,size_t SymlinkDepth) const93081ad6265SDimitry Andric InMemoryFileSystem::lookupNode(const Twine &P, bool FollowFinalSymlink,
93181ad6265SDimitry Andric size_t SymlinkDepth) const {
9320b57cec5SDimitry Andric SmallString<128> Path;
9330b57cec5SDimitry Andric P.toVector(Path);
9340b57cec5SDimitry Andric
9350b57cec5SDimitry Andric // Fix up relative paths. This just prepends the current working directory.
93681ad6265SDimitry Andric std::error_code EC = makeAbsolute(Path);
9370b57cec5SDimitry Andric assert(!EC);
9380b57cec5SDimitry Andric (void)EC;
9390b57cec5SDimitry Andric
94081ad6265SDimitry Andric if (useNormalizedPaths())
9410b57cec5SDimitry Andric llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
9420b57cec5SDimitry Andric
94381ad6265SDimitry Andric const detail::InMemoryDirectory *Dir = Root.get();
9440b57cec5SDimitry Andric if (Path.empty())
94581ad6265SDimitry Andric return detail::NamedNodeOrError(Path, Dir);
9460b57cec5SDimitry Andric
9470b57cec5SDimitry Andric auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
9480b57cec5SDimitry Andric while (true) {
9490b57cec5SDimitry Andric detail::InMemoryNode *Node = Dir->getChild(*I);
9500b57cec5SDimitry Andric ++I;
9510b57cec5SDimitry Andric if (!Node)
9520b57cec5SDimitry Andric return errc::no_such_file_or_directory;
9530b57cec5SDimitry Andric
95481ad6265SDimitry Andric if (auto Symlink = dyn_cast<detail::InMemorySymbolicLink>(Node)) {
95581ad6265SDimitry Andric // If we're at the end of the path, and we're not following through
95681ad6265SDimitry Andric // terminal symlinks, then we're done.
95781ad6265SDimitry Andric if (I == E && !FollowFinalSymlink)
95881ad6265SDimitry Andric return detail::NamedNodeOrError(Path, Symlink);
95981ad6265SDimitry Andric
96081ad6265SDimitry Andric if (SymlinkDepth > InMemoryFileSystem::MaxSymlinkDepth)
96181ad6265SDimitry Andric return errc::no_such_file_or_directory;
96281ad6265SDimitry Andric
96381ad6265SDimitry Andric SmallString<128> TargetPath = Symlink->getTargetPath();
96481ad6265SDimitry Andric if (std::error_code EC = makeAbsolute(TargetPath))
96581ad6265SDimitry Andric return EC;
96681ad6265SDimitry Andric
96781ad6265SDimitry Andric // Keep going with the target. We always want to follow symlinks here
96881ad6265SDimitry Andric // because we're either at the end of a path that we want to follow, or
96981ad6265SDimitry Andric // not at the end of a path, in which case we need to follow the symlink
97081ad6265SDimitry Andric // regardless.
97181ad6265SDimitry Andric auto Target =
97281ad6265SDimitry Andric lookupNode(TargetPath, /*FollowFinalSymlink=*/true, SymlinkDepth + 1);
97381ad6265SDimitry Andric if (!Target || I == E)
97481ad6265SDimitry Andric return Target;
97581ad6265SDimitry Andric
97681ad6265SDimitry Andric if (!isa<detail::InMemoryDirectory>(*Target))
97781ad6265SDimitry Andric return errc::no_such_file_or_directory;
97881ad6265SDimitry Andric
97981ad6265SDimitry Andric // Otherwise, continue on the search in the symlinked directory.
98081ad6265SDimitry Andric Dir = cast<detail::InMemoryDirectory>(*Target);
98181ad6265SDimitry Andric continue;
98281ad6265SDimitry Andric }
98381ad6265SDimitry Andric
9840b57cec5SDimitry Andric // Return the file if it's at the end of the path.
9850b57cec5SDimitry Andric if (auto File = dyn_cast<detail::InMemoryFile>(Node)) {
9860b57cec5SDimitry Andric if (I == E)
98781ad6265SDimitry Andric return detail::NamedNodeOrError(Path, File);
9880b57cec5SDimitry Andric return errc::no_such_file_or_directory;
9890b57cec5SDimitry Andric }
9900b57cec5SDimitry Andric
9910b57cec5SDimitry Andric // If Node is HardLink then return the resolved file.
9920b57cec5SDimitry Andric if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) {
9930b57cec5SDimitry Andric if (I == E)
99481ad6265SDimitry Andric return detail::NamedNodeOrError(Path, &File->getResolvedFile());
9950b57cec5SDimitry Andric return errc::no_such_file_or_directory;
9960b57cec5SDimitry Andric }
9970b57cec5SDimitry Andric // Traverse directories.
9980b57cec5SDimitry Andric Dir = cast<detail::InMemoryDirectory>(Node);
9990b57cec5SDimitry Andric if (I == E)
100081ad6265SDimitry Andric return detail::NamedNodeOrError(Path, Dir);
10010b57cec5SDimitry Andric }
10020b57cec5SDimitry Andric }
10030b57cec5SDimitry Andric
addHardLink(const Twine & NewLink,const Twine & Target)100481ad6265SDimitry Andric bool InMemoryFileSystem::addHardLink(const Twine &NewLink,
100581ad6265SDimitry Andric const Twine &Target) {
100681ad6265SDimitry Andric auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
100781ad6265SDimitry Andric // Whether symlinks in the hardlink target are followed is
100881ad6265SDimitry Andric // implementation-defined in POSIX.
100981ad6265SDimitry Andric // We're following symlinks here to be consistent with macOS.
101081ad6265SDimitry Andric auto TargetNode = lookupNode(Target, /*FollowFinalSymlink=*/true);
10110b57cec5SDimitry Andric // FromPath must not have been added before. ToPath must have been added
10120b57cec5SDimitry Andric // before. Resolved ToPath must be a File.
101381ad6265SDimitry Andric if (!TargetNode || NewLinkNode || !isa<detail::InMemoryFile>(*TargetNode))
10140b57cec5SDimitry Andric return false;
1015bdd1243dSDimitry Andric return addFile(NewLink, 0, nullptr, std::nullopt, std::nullopt, std::nullopt,
1016bdd1243dSDimitry Andric std::nullopt, [&](detail::NewInMemoryNodeInfo NNI) {
101704eeddc0SDimitry Andric return std::make_unique<detail::InMemoryHardLink>(
101881ad6265SDimitry Andric NNI.Path.str(),
101981ad6265SDimitry Andric *cast<detail::InMemoryFile>(*TargetNode));
102081ad6265SDimitry Andric });
102181ad6265SDimitry Andric }
102281ad6265SDimitry 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)1023bdd1243dSDimitry Andric bool InMemoryFileSystem::addSymbolicLink(
1024bdd1243dSDimitry Andric const Twine &NewLink, const Twine &Target, time_t ModificationTime,
1025bdd1243dSDimitry Andric std::optional<uint32_t> User, std::optional<uint32_t> Group,
1026bdd1243dSDimitry Andric std::optional<llvm::sys::fs::perms> Perms) {
102781ad6265SDimitry Andric auto NewLinkNode = lookupNode(NewLink, /*FollowFinalSymlink=*/false);
102881ad6265SDimitry Andric if (NewLinkNode)
102981ad6265SDimitry Andric return false;
103081ad6265SDimitry Andric
103181ad6265SDimitry Andric SmallString<128> NewLinkStr, TargetStr;
103281ad6265SDimitry Andric NewLink.toVector(NewLinkStr);
103381ad6265SDimitry Andric Target.toVector(TargetStr);
103481ad6265SDimitry Andric
103581ad6265SDimitry Andric return addFile(NewLinkStr, ModificationTime, nullptr, User, Group,
103681ad6265SDimitry Andric sys::fs::file_type::symlink_file, Perms,
103781ad6265SDimitry Andric [&](detail::NewInMemoryNodeInfo NNI) {
103881ad6265SDimitry Andric return std::make_unique<detail::InMemorySymbolicLink>(
103981ad6265SDimitry Andric NewLinkStr, TargetStr, NNI.makeStatus());
104004eeddc0SDimitry Andric });
10410b57cec5SDimitry Andric }
10420b57cec5SDimitry Andric
status(const Twine & Path)10430b57cec5SDimitry Andric llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
104481ad6265SDimitry Andric auto Node = lookupNode(Path, /*FollowFinalSymlink=*/true);
10450b57cec5SDimitry Andric if (Node)
104604eeddc0SDimitry Andric return (*Node)->getStatus(Path);
10470b57cec5SDimitry Andric return Node.getError();
10480b57cec5SDimitry Andric }
10490b57cec5SDimitry Andric
10500b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Path)10510b57cec5SDimitry Andric InMemoryFileSystem::openFileForRead(const Twine &Path) {
105281ad6265SDimitry Andric auto Node = lookupNode(Path,/*FollowFinalSymlink=*/true);
10530b57cec5SDimitry Andric if (!Node)
10540b57cec5SDimitry Andric return Node.getError();
10550b57cec5SDimitry Andric
10560b57cec5SDimitry Andric // When we have a file provide a heap-allocated wrapper for the memory buffer
10570b57cec5SDimitry Andric // to match the ownership semantics for File.
10580b57cec5SDimitry Andric if (auto *F = dyn_cast<detail::InMemoryFile>(*Node))
10590b57cec5SDimitry Andric return std::unique_ptr<File>(
10600b57cec5SDimitry Andric new detail::InMemoryFileAdaptor(*F, Path.str()));
10610b57cec5SDimitry Andric
10620b57cec5SDimitry Andric // FIXME: errc::not_a_file?
10630b57cec5SDimitry Andric return make_error_code(llvm::errc::invalid_argument);
10640b57cec5SDimitry Andric }
10650b57cec5SDimitry Andric
10660b57cec5SDimitry Andric /// Adaptor from InMemoryDir::iterator to directory_iterator.
106781ad6265SDimitry Andric class InMemoryFileSystem::DirIterator : public llvm::vfs::detail::DirIterImpl {
106881ad6265SDimitry Andric const InMemoryFileSystem *FS;
10690b57cec5SDimitry Andric detail::InMemoryDirectory::const_iterator I;
10700b57cec5SDimitry Andric detail::InMemoryDirectory::const_iterator E;
10710b57cec5SDimitry Andric std::string RequestedDirName;
10720b57cec5SDimitry Andric
setCurrentEntry()10730b57cec5SDimitry Andric void setCurrentEntry() {
10740b57cec5SDimitry Andric if (I != E) {
10750b57cec5SDimitry Andric SmallString<256> Path(RequestedDirName);
10760b57cec5SDimitry Andric llvm::sys::path::append(Path, I->second->getFileName());
1077480093f4SDimitry Andric sys::fs::file_type Type = sys::fs::file_type::type_unknown;
10780b57cec5SDimitry Andric switch (I->second->getKind()) {
10790b57cec5SDimitry Andric case detail::IME_File:
10800b57cec5SDimitry Andric case detail::IME_HardLink:
10810b57cec5SDimitry Andric Type = sys::fs::file_type::regular_file;
10820b57cec5SDimitry Andric break;
10830b57cec5SDimitry Andric case detail::IME_Directory:
10840b57cec5SDimitry Andric Type = sys::fs::file_type::directory_file;
10850b57cec5SDimitry Andric break;
108681ad6265SDimitry Andric case detail::IME_SymbolicLink:
108781ad6265SDimitry Andric if (auto SymlinkTarget =
108881ad6265SDimitry Andric FS->lookupNode(Path, /*FollowFinalSymlink=*/true)) {
108981ad6265SDimitry Andric Path = SymlinkTarget.getName();
109081ad6265SDimitry Andric Type = (*SymlinkTarget)->getStatus(Path).getType();
109181ad6265SDimitry Andric }
109281ad6265SDimitry Andric break;
10930b57cec5SDimitry Andric }
1094*a58f00eaSDimitry Andric CurrentEntry = directory_entry(std::string(Path), Type);
10950b57cec5SDimitry Andric } else {
10960b57cec5SDimitry Andric // When we're at the end, make CurrentEntry invalid and DirIterImpl will
10970b57cec5SDimitry Andric // do the rest.
10980b57cec5SDimitry Andric CurrentEntry = directory_entry();
10990b57cec5SDimitry Andric }
11000b57cec5SDimitry Andric }
11010b57cec5SDimitry Andric
11020b57cec5SDimitry Andric public:
110381ad6265SDimitry Andric DirIterator() = default;
11040b57cec5SDimitry Andric
DirIterator(const InMemoryFileSystem * FS,const detail::InMemoryDirectory & Dir,std::string RequestedDirName)110581ad6265SDimitry Andric DirIterator(const InMemoryFileSystem *FS,
110681ad6265SDimitry Andric const detail::InMemoryDirectory &Dir,
11070b57cec5SDimitry Andric std::string RequestedDirName)
110881ad6265SDimitry Andric : FS(FS), I(Dir.begin()), E(Dir.end()),
11090b57cec5SDimitry Andric RequestedDirName(std::move(RequestedDirName)) {
11100b57cec5SDimitry Andric setCurrentEntry();
11110b57cec5SDimitry Andric }
11120b57cec5SDimitry Andric
increment()11130b57cec5SDimitry Andric std::error_code increment() override {
11140b57cec5SDimitry Andric ++I;
11150b57cec5SDimitry Andric setCurrentEntry();
11160b57cec5SDimitry Andric return {};
11170b57cec5SDimitry Andric }
11180b57cec5SDimitry Andric };
11190b57cec5SDimitry Andric
dir_begin(const Twine & Dir,std::error_code & EC)11200b57cec5SDimitry Andric directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
11210b57cec5SDimitry Andric std::error_code &EC) {
112281ad6265SDimitry Andric auto Node = lookupNode(Dir, /*FollowFinalSymlink=*/true);
11230b57cec5SDimitry Andric if (!Node) {
11240b57cec5SDimitry Andric EC = Node.getError();
112581ad6265SDimitry Andric return directory_iterator(std::make_shared<DirIterator>());
11260b57cec5SDimitry Andric }
11270b57cec5SDimitry Andric
11280b57cec5SDimitry Andric if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node))
11290b57cec5SDimitry Andric return directory_iterator(
113081ad6265SDimitry Andric std::make_shared<DirIterator>(this, *DirNode, Dir.str()));
11310b57cec5SDimitry Andric
11320b57cec5SDimitry Andric EC = make_error_code(llvm::errc::not_a_directory);
113381ad6265SDimitry Andric return directory_iterator(std::make_shared<DirIterator>());
11340b57cec5SDimitry Andric }
11350b57cec5SDimitry Andric
setCurrentWorkingDirectory(const Twine & P)11360b57cec5SDimitry Andric std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) {
11370b57cec5SDimitry Andric SmallString<128> Path;
11380b57cec5SDimitry Andric P.toVector(Path);
11390b57cec5SDimitry Andric
11400b57cec5SDimitry Andric // Fix up relative paths. This just prepends the current working directory.
11410b57cec5SDimitry Andric std::error_code EC = makeAbsolute(Path);
11420b57cec5SDimitry Andric assert(!EC);
11430b57cec5SDimitry Andric (void)EC;
11440b57cec5SDimitry Andric
11450b57cec5SDimitry Andric if (useNormalizedPaths())
11460b57cec5SDimitry Andric llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
11470b57cec5SDimitry Andric
11480b57cec5SDimitry Andric if (!Path.empty())
1149*a58f00eaSDimitry Andric WorkingDirectory = std::string(Path);
11500b57cec5SDimitry Andric return {};
11510b57cec5SDimitry Andric }
11520b57cec5SDimitry Andric
11530b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const11540b57cec5SDimitry Andric InMemoryFileSystem::getRealPath(const Twine &Path,
11550b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const {
11560b57cec5SDimitry Andric auto CWD = getCurrentWorkingDirectory();
11570b57cec5SDimitry Andric if (!CWD || CWD->empty())
11580b57cec5SDimitry Andric return errc::operation_not_permitted;
11590b57cec5SDimitry Andric Path.toVector(Output);
11600b57cec5SDimitry Andric if (auto EC = makeAbsolute(Output))
11610b57cec5SDimitry Andric return EC;
11620b57cec5SDimitry Andric llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true);
11630b57cec5SDimitry Andric return {};
11640b57cec5SDimitry Andric }
11650b57cec5SDimitry Andric
isLocal(const Twine & Path,bool & Result)11660b57cec5SDimitry Andric std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) {
11670b57cec5SDimitry Andric Result = false;
11680b57cec5SDimitry Andric return {};
11690b57cec5SDimitry Andric }
11700b57cec5SDimitry Andric
printImpl(raw_ostream & OS,PrintType PrintContents,unsigned IndentLevel) const117181ad6265SDimitry Andric void InMemoryFileSystem::printImpl(raw_ostream &OS, PrintType PrintContents,
117281ad6265SDimitry Andric unsigned IndentLevel) const {
117381ad6265SDimitry Andric printIndent(OS, IndentLevel);
117481ad6265SDimitry Andric OS << "InMemoryFileSystem\n";
117581ad6265SDimitry Andric }
117681ad6265SDimitry Andric
11770b57cec5SDimitry Andric } // namespace vfs
11780b57cec5SDimitry Andric } // namespace llvm
11790b57cec5SDimitry Andric
11800b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
11810b57cec5SDimitry Andric // RedirectingFileSystem implementation
11820b57cec5SDimitry Andric //===-----------------------------------------------------------------------===/
11830b57cec5SDimitry Andric
11845ffd83dbSDimitry Andric namespace {
11855ffd83dbSDimitry Andric
getExistingStyle(llvm::StringRef Path)1186fe6060f1SDimitry Andric static llvm::sys::path::Style getExistingStyle(llvm::StringRef Path) {
1187fe6060f1SDimitry Andric // Detect the path style in use by checking the first separator.
11885ffd83dbSDimitry Andric llvm::sys::path::Style style = llvm::sys::path::Style::native;
11895ffd83dbSDimitry Andric const size_t n = Path.find_first_of("/\\");
1190349cc55cSDimitry Andric // Can't distinguish between posix and windows_slash here.
11915ffd83dbSDimitry Andric if (n != static_cast<size_t>(-1))
11925ffd83dbSDimitry Andric style = (Path[n] == '/') ? llvm::sys::path::Style::posix
1193349cc55cSDimitry Andric : llvm::sys::path::Style::windows_backslash;
1194fe6060f1SDimitry Andric return style;
1195fe6060f1SDimitry Andric }
1196fe6060f1SDimitry Andric
1197fe6060f1SDimitry Andric /// Removes leading "./" as well as path components like ".." and ".".
canonicalize(llvm::StringRef Path)1198fe6060f1SDimitry Andric static llvm::SmallString<256> canonicalize(llvm::StringRef Path) {
1199fe6060f1SDimitry Andric // First detect the path style in use by checking the first separator.
1200fe6060f1SDimitry Andric llvm::sys::path::Style style = getExistingStyle(Path);
12015ffd83dbSDimitry Andric
12025ffd83dbSDimitry Andric // Now remove the dots. Explicitly specifying the path style prevents the
12035ffd83dbSDimitry Andric // direction of the slashes from changing.
12045ffd83dbSDimitry Andric llvm::SmallString<256> result =
12055ffd83dbSDimitry Andric llvm::sys::path::remove_leading_dotslash(Path, style);
12065ffd83dbSDimitry Andric llvm::sys::path::remove_dots(result, /*remove_dot_dot=*/true, style);
12075ffd83dbSDimitry Andric return result;
12085ffd83dbSDimitry Andric }
12095ffd83dbSDimitry Andric
121081ad6265SDimitry Andric /// Whether the error and entry specify a file/directory that was not found.
isFileNotFound(std::error_code EC,RedirectingFileSystem::Entry * E=nullptr)121181ad6265SDimitry Andric static bool isFileNotFound(std::error_code EC,
121281ad6265SDimitry Andric RedirectingFileSystem::Entry *E = nullptr) {
121381ad6265SDimitry Andric if (E && !isa<RedirectingFileSystem::DirectoryRemapEntry>(E))
121481ad6265SDimitry Andric return false;
121581ad6265SDimitry Andric return EC == llvm::errc::no_such_file_or_directory;
121681ad6265SDimitry Andric }
121781ad6265SDimitry Andric
12185ffd83dbSDimitry Andric } // anonymous namespace
12195ffd83dbSDimitry Andric
12205ffd83dbSDimitry Andric
RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)12218bcb0991SDimitry Andric RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
12228bcb0991SDimitry Andric : ExternalFS(std::move(FS)) {
12238bcb0991SDimitry Andric if (ExternalFS)
12248bcb0991SDimitry Andric if (auto ExternalWorkingDirectory =
12258bcb0991SDimitry Andric ExternalFS->getCurrentWorkingDirectory()) {
12268bcb0991SDimitry Andric WorkingDirectory = *ExternalWorkingDirectory;
12278bcb0991SDimitry Andric }
12288bcb0991SDimitry Andric }
12298bcb0991SDimitry Andric
1230fe6060f1SDimitry Andric /// Directory iterator implementation for \c RedirectingFileSystem's
1231fe6060f1SDimitry Andric /// directory entries.
1232fe6060f1SDimitry Andric class llvm::vfs::RedirectingFSDirIterImpl
12330b57cec5SDimitry Andric : public llvm::vfs::detail::DirIterImpl {
12340b57cec5SDimitry Andric std::string Dir;
1235fe6060f1SDimitry Andric RedirectingFileSystem::DirectoryEntry::iterator Current, End;
12360b57cec5SDimitry Andric
incrementImpl(bool IsFirstTime)1237fe6060f1SDimitry Andric std::error_code incrementImpl(bool IsFirstTime) {
1238fe6060f1SDimitry Andric assert((IsFirstTime || Current != End) && "cannot iterate past end");
1239fe6060f1SDimitry Andric if (!IsFirstTime)
1240fe6060f1SDimitry Andric ++Current;
1241fe6060f1SDimitry Andric if (Current != End) {
1242fe6060f1SDimitry Andric SmallString<128> PathStr(Dir);
1243fe6060f1SDimitry Andric llvm::sys::path::append(PathStr, (*Current)->getName());
1244fe6060f1SDimitry Andric sys::fs::file_type Type = sys::fs::file_type::type_unknown;
1245fe6060f1SDimitry Andric switch ((*Current)->getKind()) {
1246fe6060f1SDimitry Andric case RedirectingFileSystem::EK_Directory:
1247bdd1243dSDimitry Andric [[fallthrough]];
1248fe6060f1SDimitry Andric case RedirectingFileSystem::EK_DirectoryRemap:
1249fe6060f1SDimitry Andric Type = sys::fs::file_type::directory_file;
1250fe6060f1SDimitry Andric break;
1251fe6060f1SDimitry Andric case RedirectingFileSystem::EK_File:
1252fe6060f1SDimitry Andric Type = sys::fs::file_type::regular_file;
1253fe6060f1SDimitry Andric break;
1254fe6060f1SDimitry Andric }
1255*a58f00eaSDimitry Andric CurrentEntry = directory_entry(std::string(PathStr), Type);
1256fe6060f1SDimitry Andric } else {
1257fe6060f1SDimitry Andric CurrentEntry = directory_entry();
1258fe6060f1SDimitry Andric }
1259fe6060f1SDimitry Andric return {};
1260fe6060f1SDimitry Andric };
12610b57cec5SDimitry Andric
12620b57cec5SDimitry Andric public:
RedirectingFSDirIterImpl(const Twine & Path,RedirectingFileSystem::DirectoryEntry::iterator Begin,RedirectingFileSystem::DirectoryEntry::iterator End,std::error_code & EC)1263fe6060f1SDimitry Andric RedirectingFSDirIterImpl(
1264fe6060f1SDimitry Andric const Twine &Path, RedirectingFileSystem::DirectoryEntry::iterator Begin,
1265fe6060f1SDimitry Andric RedirectingFileSystem::DirectoryEntry::iterator End, std::error_code &EC)
1266fe6060f1SDimitry Andric : Dir(Path.str()), Current(Begin), End(End) {
1267fe6060f1SDimitry Andric EC = incrementImpl(/*IsFirstTime=*/true);
1268fe6060f1SDimitry Andric }
12690b57cec5SDimitry Andric
increment()1270fe6060f1SDimitry Andric std::error_code increment() override {
1271fe6060f1SDimitry Andric return incrementImpl(/*IsFirstTime=*/false);
1272fe6060f1SDimitry Andric }
1273fe6060f1SDimitry Andric };
1274fe6060f1SDimitry Andric
1275349cc55cSDimitry Andric namespace {
1276fe6060f1SDimitry Andric /// Directory iterator implementation for \c RedirectingFileSystem's
1277fe6060f1SDimitry Andric /// directory remap entries that maps the paths reported by the external
1278fe6060f1SDimitry Andric /// file system's directory iterator back to the virtual directory's path.
1279fe6060f1SDimitry Andric class RedirectingFSDirRemapIterImpl : public llvm::vfs::detail::DirIterImpl {
1280fe6060f1SDimitry Andric std::string Dir;
1281fe6060f1SDimitry Andric llvm::sys::path::Style DirStyle;
1282fe6060f1SDimitry Andric llvm::vfs::directory_iterator ExternalIter;
1283fe6060f1SDimitry Andric
1284fe6060f1SDimitry Andric public:
RedirectingFSDirRemapIterImpl(std::string DirPath,llvm::vfs::directory_iterator ExtIter)1285fe6060f1SDimitry Andric RedirectingFSDirRemapIterImpl(std::string DirPath,
1286fe6060f1SDimitry Andric llvm::vfs::directory_iterator ExtIter)
1287fe6060f1SDimitry Andric : Dir(std::move(DirPath)), DirStyle(getExistingStyle(Dir)),
1288fe6060f1SDimitry Andric ExternalIter(ExtIter) {
1289fe6060f1SDimitry Andric if (ExternalIter != llvm::vfs::directory_iterator())
1290fe6060f1SDimitry Andric setCurrentEntry();
1291fe6060f1SDimitry Andric }
1292fe6060f1SDimitry Andric
setCurrentEntry()1293fe6060f1SDimitry Andric void setCurrentEntry() {
1294fe6060f1SDimitry Andric StringRef ExternalPath = ExternalIter->path();
1295fe6060f1SDimitry Andric llvm::sys::path::Style ExternalStyle = getExistingStyle(ExternalPath);
1296fe6060f1SDimitry Andric StringRef File = llvm::sys::path::filename(ExternalPath, ExternalStyle);
1297fe6060f1SDimitry Andric
1298fe6060f1SDimitry Andric SmallString<128> NewPath(Dir);
1299fe6060f1SDimitry Andric llvm::sys::path::append(NewPath, DirStyle, File);
1300fe6060f1SDimitry Andric
1301fe6060f1SDimitry Andric CurrentEntry = directory_entry(std::string(NewPath), ExternalIter->type());
1302fe6060f1SDimitry Andric }
1303fe6060f1SDimitry Andric
increment()1304fe6060f1SDimitry Andric std::error_code increment() override {
1305fe6060f1SDimitry Andric std::error_code EC;
1306fe6060f1SDimitry Andric ExternalIter.increment(EC);
1307fe6060f1SDimitry Andric if (!EC && ExternalIter != llvm::vfs::directory_iterator())
1308fe6060f1SDimitry Andric setCurrentEntry();
1309fe6060f1SDimitry Andric else
1310fe6060f1SDimitry Andric CurrentEntry = directory_entry();
1311fe6060f1SDimitry Andric return EC;
1312fe6060f1SDimitry Andric }
13130b57cec5SDimitry Andric };
1314349cc55cSDimitry Andric } // namespace
13150b57cec5SDimitry Andric
13160b57cec5SDimitry Andric llvm::ErrorOr<std::string>
getCurrentWorkingDirectory() const13170b57cec5SDimitry Andric RedirectingFileSystem::getCurrentWorkingDirectory() const {
13188bcb0991SDimitry Andric return WorkingDirectory;
13190b57cec5SDimitry Andric }
13200b57cec5SDimitry Andric
13210b57cec5SDimitry Andric std::error_code
setCurrentWorkingDirectory(const Twine & Path)13220b57cec5SDimitry Andric RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
13238bcb0991SDimitry Andric // Don't change the working directory if the path doesn't exist.
13248bcb0991SDimitry Andric if (!exists(Path))
13258bcb0991SDimitry Andric return errc::no_such_file_or_directory;
13268bcb0991SDimitry Andric
13278bcb0991SDimitry Andric SmallString<128> AbsolutePath;
13288bcb0991SDimitry Andric Path.toVector(AbsolutePath);
13298bcb0991SDimitry Andric if (std::error_code EC = makeAbsolute(AbsolutePath))
13308bcb0991SDimitry Andric return EC;
1331*a58f00eaSDimitry Andric WorkingDirectory = std::string(AbsolutePath);
13328bcb0991SDimitry Andric return {};
13330b57cec5SDimitry Andric }
13340b57cec5SDimitry Andric
isLocal(const Twine & Path_,bool & Result)1335e8d8bef9SDimitry Andric std::error_code RedirectingFileSystem::isLocal(const Twine &Path_,
13360b57cec5SDimitry Andric bool &Result) {
1337e8d8bef9SDimitry Andric SmallString<256> Path;
1338e8d8bef9SDimitry Andric Path_.toVector(Path);
1339e8d8bef9SDimitry Andric
1340c9157d92SDimitry Andric if (makeCanonical(Path))
1341e8d8bef9SDimitry Andric return {};
1342e8d8bef9SDimitry Andric
13430b57cec5SDimitry Andric return ExternalFS->isLocal(Path, Result);
13440b57cec5SDimitry Andric }
13450b57cec5SDimitry Andric
makeAbsolute(SmallVectorImpl<char> & Path) const1346480093f4SDimitry Andric std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
1347349cc55cSDimitry Andric // is_absolute(..., Style::windows_*) accepts paths with both slash types.
1348480093f4SDimitry Andric if (llvm::sys::path::is_absolute(Path, llvm::sys::path::Style::posix) ||
1349349cc55cSDimitry Andric llvm::sys::path::is_absolute(Path,
1350349cc55cSDimitry Andric llvm::sys::path::Style::windows_backslash))
1351bdd1243dSDimitry Andric // This covers windows absolute path with forward slash as well, as the
1352bdd1243dSDimitry Andric // forward slashes are treated as path seperation in llvm::path
1353bdd1243dSDimitry Andric // regardless of what path::Style is used.
1354480093f4SDimitry Andric return {};
1355480093f4SDimitry Andric
1356480093f4SDimitry Andric auto WorkingDir = getCurrentWorkingDirectory();
1357480093f4SDimitry Andric if (!WorkingDir)
1358480093f4SDimitry Andric return WorkingDir.getError();
1359480093f4SDimitry Andric
1360bdd1243dSDimitry Andric return makeAbsolute(WorkingDir.get(), Path);
1361bdd1243dSDimitry Andric }
1362bdd1243dSDimitry Andric
1363bdd1243dSDimitry Andric std::error_code
makeAbsolute(StringRef WorkingDir,SmallVectorImpl<char> & Path) const1364bdd1243dSDimitry Andric RedirectingFileSystem::makeAbsolute(StringRef WorkingDir,
1365bdd1243dSDimitry Andric SmallVectorImpl<char> &Path) const {
13665ffd83dbSDimitry Andric // We can't use sys::fs::make_absolute because that assumes the path style
13675ffd83dbSDimitry Andric // is native and there is no way to override that. Since we know WorkingDir
13685ffd83dbSDimitry Andric // is absolute, we can use it to determine which style we actually have and
13695ffd83dbSDimitry Andric // append Path ourselves.
1370bdd1243dSDimitry Andric if (!WorkingDir.empty() &&
1371bdd1243dSDimitry Andric !sys::path::is_absolute(WorkingDir, sys::path::Style::posix) &&
1372bdd1243dSDimitry Andric !sys::path::is_absolute(WorkingDir,
1373bdd1243dSDimitry Andric sys::path::Style::windows_backslash)) {
1374bdd1243dSDimitry Andric return std::error_code();
1375bdd1243dSDimitry Andric }
1376349cc55cSDimitry Andric sys::path::Style style = sys::path::Style::windows_backslash;
1377bdd1243dSDimitry Andric if (sys::path::is_absolute(WorkingDir, sys::path::Style::posix)) {
13785ffd83dbSDimitry Andric style = sys::path::Style::posix;
1379349cc55cSDimitry Andric } else {
1380349cc55cSDimitry Andric // Distinguish between windows_backslash and windows_slash; getExistingStyle
1381349cc55cSDimitry Andric // returns posix for a path with windows_slash.
1382bdd1243dSDimitry Andric if (getExistingStyle(WorkingDir) != sys::path::Style::windows_backslash)
1383349cc55cSDimitry Andric style = sys::path::Style::windows_slash;
13845ffd83dbSDimitry Andric }
13855ffd83dbSDimitry Andric
1386bdd1243dSDimitry Andric std::string Result = std::string(WorkingDir);
13875ffd83dbSDimitry Andric StringRef Dir(Result);
1388c9157d92SDimitry Andric if (!Dir.ends_with(sys::path::get_separator(style))) {
13895ffd83dbSDimitry Andric Result += sys::path::get_separator(style);
13905ffd83dbSDimitry Andric }
1391bdd1243dSDimitry Andric // backslashes '\' are legit path charactors under POSIX. Windows APIs
1392bdd1243dSDimitry Andric // like CreateFile accepts forward slashes '/' as path
1393bdd1243dSDimitry Andric // separator (even when mixed with backslashes). Therefore,
1394bdd1243dSDimitry Andric // `Path` should be directly appended to `WorkingDir` without converting
1395bdd1243dSDimitry Andric // path separator.
13965ffd83dbSDimitry Andric Result.append(Path.data(), Path.size());
13975ffd83dbSDimitry Andric Path.assign(Result.begin(), Result.end());
13985ffd83dbSDimitry Andric
1399480093f4SDimitry Andric return {};
1400480093f4SDimitry Andric }
1401480093f4SDimitry Andric
dir_begin(const Twine & Dir,std::error_code & EC)14020b57cec5SDimitry Andric directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
14030b57cec5SDimitry Andric std::error_code &EC) {
1404e8d8bef9SDimitry Andric SmallString<256> Path;
1405e8d8bef9SDimitry Andric Dir.toVector(Path);
1406e8d8bef9SDimitry Andric
1407e8d8bef9SDimitry Andric EC = makeCanonical(Path);
1408e8d8bef9SDimitry Andric if (EC)
1409e8d8bef9SDimitry Andric return {};
1410e8d8bef9SDimitry Andric
1411fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
1412fe6060f1SDimitry Andric if (!Result) {
141381ad6265SDimitry Andric if (Redirection != RedirectKind::RedirectOnly &&
141481ad6265SDimitry Andric isFileNotFound(Result.getError()))
1415e8d8bef9SDimitry Andric return ExternalFS->dir_begin(Path, EC);
141681ad6265SDimitry Andric
141781ad6265SDimitry Andric EC = Result.getError();
14180b57cec5SDimitry Andric return {};
14190b57cec5SDimitry Andric }
1420fe6060f1SDimitry Andric
1421fe6060f1SDimitry Andric // Use status to make sure the path exists and refers to a directory.
1422349cc55cSDimitry Andric ErrorOr<Status> S = status(Path, Dir, *Result);
14230b57cec5SDimitry Andric if (!S) {
142481ad6265SDimitry Andric if (Redirection != RedirectKind::RedirectOnly &&
142581ad6265SDimitry Andric isFileNotFound(S.getError(), Result->E))
1426fe6060f1SDimitry Andric return ExternalFS->dir_begin(Dir, EC);
142781ad6265SDimitry Andric
14280b57cec5SDimitry Andric EC = S.getError();
14290b57cec5SDimitry Andric return {};
14300b57cec5SDimitry Andric }
143181ad6265SDimitry Andric
14320b57cec5SDimitry Andric if (!S->isDirectory()) {
143381ad6265SDimitry Andric EC = errc::not_a_directory;
14340b57cec5SDimitry Andric return {};
14350b57cec5SDimitry Andric }
14360b57cec5SDimitry Andric
1437fe6060f1SDimitry Andric // Create the appropriate directory iterator based on whether we found a
1438fe6060f1SDimitry Andric // DirectoryRemapEntry or DirectoryEntry.
143981ad6265SDimitry Andric directory_iterator RedirectIter;
144081ad6265SDimitry Andric std::error_code RedirectEC;
1441fe6060f1SDimitry Andric if (auto ExtRedirect = Result->getExternalRedirect()) {
1442fe6060f1SDimitry Andric auto RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
144381ad6265SDimitry Andric RedirectIter = ExternalFS->dir_begin(*ExtRedirect, RedirectEC);
1444fe6060f1SDimitry Andric
1445fe6060f1SDimitry Andric if (!RE->useExternalName(UseExternalNames)) {
1446fe6060f1SDimitry Andric // Update the paths in the results to use the virtual directory's path.
144781ad6265SDimitry Andric RedirectIter =
1448fe6060f1SDimitry Andric directory_iterator(std::make_shared<RedirectingFSDirRemapIterImpl>(
144981ad6265SDimitry Andric std::string(Path), RedirectIter));
1450fe6060f1SDimitry Andric }
1451fe6060f1SDimitry Andric } else {
1452fe6060f1SDimitry Andric auto DE = cast<DirectoryEntry>(Result->E);
145381ad6265SDimitry Andric RedirectIter =
145481ad6265SDimitry Andric directory_iterator(std::make_shared<RedirectingFSDirIterImpl>(
145581ad6265SDimitry Andric Path, DE->contents_begin(), DE->contents_end(), RedirectEC));
1456fe6060f1SDimitry Andric }
1457fe6060f1SDimitry Andric
145881ad6265SDimitry Andric if (RedirectEC) {
145981ad6265SDimitry Andric if (RedirectEC != errc::no_such_file_or_directory) {
146081ad6265SDimitry Andric EC = RedirectEC;
146181ad6265SDimitry Andric return {};
146281ad6265SDimitry Andric }
146381ad6265SDimitry Andric RedirectIter = {};
146481ad6265SDimitry Andric }
146581ad6265SDimitry Andric
146681ad6265SDimitry Andric if (Redirection == RedirectKind::RedirectOnly) {
146781ad6265SDimitry Andric EC = RedirectEC;
146881ad6265SDimitry Andric return RedirectIter;
146981ad6265SDimitry Andric }
147081ad6265SDimitry Andric
147181ad6265SDimitry Andric std::error_code ExternalEC;
147281ad6265SDimitry Andric directory_iterator ExternalIter = ExternalFS->dir_begin(Path, ExternalEC);
147381ad6265SDimitry Andric if (ExternalEC) {
147481ad6265SDimitry Andric if (ExternalEC != errc::no_such_file_or_directory) {
147581ad6265SDimitry Andric EC = ExternalEC;
147681ad6265SDimitry Andric return {};
147781ad6265SDimitry Andric }
147881ad6265SDimitry Andric ExternalIter = {};
147981ad6265SDimitry Andric }
148081ad6265SDimitry Andric
148181ad6265SDimitry Andric SmallVector<directory_iterator, 2> Iters;
148281ad6265SDimitry Andric switch (Redirection) {
148381ad6265SDimitry Andric case RedirectKind::Fallthrough:
148481ad6265SDimitry Andric Iters.push_back(ExternalIter);
148581ad6265SDimitry Andric Iters.push_back(RedirectIter);
148681ad6265SDimitry Andric break;
148781ad6265SDimitry Andric case RedirectKind::Fallback:
148881ad6265SDimitry Andric Iters.push_back(RedirectIter);
148981ad6265SDimitry Andric Iters.push_back(ExternalIter);
149081ad6265SDimitry Andric break;
149181ad6265SDimitry Andric default:
149281ad6265SDimitry Andric llvm_unreachable("unhandled RedirectKind");
149381ad6265SDimitry Andric }
149481ad6265SDimitry Andric
149581ad6265SDimitry Andric directory_iterator Combined{
149681ad6265SDimitry Andric std::make_shared<CombiningDirIterImpl>(Iters, EC)};
149781ad6265SDimitry Andric if (EC)
149881ad6265SDimitry Andric return {};
149981ad6265SDimitry Andric return Combined;
15000b57cec5SDimitry Andric }
15010b57cec5SDimitry Andric
setOverlayFileDir(StringRef Dir)1502bdd1243dSDimitry Andric void RedirectingFileSystem::setOverlayFileDir(StringRef Dir) {
1503bdd1243dSDimitry Andric OverlayFileDir = Dir.str();
15040b57cec5SDimitry Andric }
15050b57cec5SDimitry Andric
getOverlayFileDir() const1506bdd1243dSDimitry Andric StringRef RedirectingFileSystem::getOverlayFileDir() const {
1507bdd1243dSDimitry Andric return OverlayFileDir;
15080b57cec5SDimitry Andric }
15090b57cec5SDimitry Andric
setFallthrough(bool Fallthrough)1510e8d8bef9SDimitry Andric void RedirectingFileSystem::setFallthrough(bool Fallthrough) {
151181ad6265SDimitry Andric if (Fallthrough) {
151281ad6265SDimitry Andric Redirection = RedirectingFileSystem::RedirectKind::Fallthrough;
151381ad6265SDimitry Andric } else {
151481ad6265SDimitry Andric Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly;
151581ad6265SDimitry Andric }
151681ad6265SDimitry Andric }
151781ad6265SDimitry Andric
setRedirection(RedirectingFileSystem::RedirectKind Kind)151881ad6265SDimitry Andric void RedirectingFileSystem::setRedirection(
151981ad6265SDimitry Andric RedirectingFileSystem::RedirectKind Kind) {
152081ad6265SDimitry Andric Redirection = Kind;
1521e8d8bef9SDimitry Andric }
1522e8d8bef9SDimitry Andric
getRoots() const1523e8d8bef9SDimitry Andric std::vector<StringRef> RedirectingFileSystem::getRoots() const {
1524e8d8bef9SDimitry Andric std::vector<StringRef> R;
1525bdd1243dSDimitry Andric R.reserve(Roots.size());
1526e8d8bef9SDimitry Andric for (const auto &Root : Roots)
1527e8d8bef9SDimitry Andric R.push_back(Root->getName());
1528e8d8bef9SDimitry Andric return R;
1529e8d8bef9SDimitry Andric }
1530e8d8bef9SDimitry Andric
printImpl(raw_ostream & OS,PrintType Type,unsigned IndentLevel) const153181ad6265SDimitry Andric void RedirectingFileSystem::printImpl(raw_ostream &OS, PrintType Type,
153281ad6265SDimitry Andric unsigned IndentLevel) const {
153381ad6265SDimitry Andric printIndent(OS, IndentLevel);
153481ad6265SDimitry Andric OS << "RedirectingFileSystem (UseExternalNames: "
153581ad6265SDimitry Andric << (UseExternalNames ? "true" : "false") << ")\n";
153681ad6265SDimitry Andric if (Type == PrintType::Summary)
153781ad6265SDimitry Andric return;
153881ad6265SDimitry Andric
15390b57cec5SDimitry Andric for (const auto &Root : Roots)
154081ad6265SDimitry Andric printEntry(OS, Root.get(), IndentLevel);
154181ad6265SDimitry Andric
154281ad6265SDimitry Andric printIndent(OS, IndentLevel);
154381ad6265SDimitry Andric OS << "ExternalFS:\n";
154481ad6265SDimitry Andric ExternalFS->print(OS, Type == PrintType::Contents ? PrintType::Summary : Type,
154581ad6265SDimitry Andric IndentLevel + 1);
15460b57cec5SDimitry Andric }
15470b57cec5SDimitry Andric
printEntry(raw_ostream & OS,RedirectingFileSystem::Entry * E,unsigned IndentLevel) const154881ad6265SDimitry Andric void RedirectingFileSystem::printEntry(raw_ostream &OS,
15498bcb0991SDimitry Andric RedirectingFileSystem::Entry *E,
155081ad6265SDimitry Andric unsigned IndentLevel) const {
155181ad6265SDimitry Andric printIndent(OS, IndentLevel);
155281ad6265SDimitry Andric OS << "'" << E->getName() << "'";
15530b57cec5SDimitry Andric
155481ad6265SDimitry Andric switch (E->getKind()) {
155581ad6265SDimitry Andric case EK_Directory: {
155681ad6265SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(E);
15570b57cec5SDimitry Andric
155881ad6265SDimitry Andric OS << "\n";
15590b57cec5SDimitry Andric for (std::unique_ptr<Entry> &SubEntry :
15600b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end()))
156181ad6265SDimitry Andric printEntry(OS, SubEntry.get(), IndentLevel + 1);
156281ad6265SDimitry Andric break;
156381ad6265SDimitry Andric }
156481ad6265SDimitry Andric case EK_DirectoryRemap:
156581ad6265SDimitry Andric case EK_File: {
156681ad6265SDimitry Andric auto *RE = cast<RedirectingFileSystem::RemapEntry>(E);
156781ad6265SDimitry Andric OS << " -> '" << RE->getExternalContentsPath() << "'";
156881ad6265SDimitry Andric switch (RE->getUseName()) {
156981ad6265SDimitry Andric case NK_NotSet:
157081ad6265SDimitry Andric break;
157181ad6265SDimitry Andric case NK_External:
157281ad6265SDimitry Andric OS << " (UseExternalName: true)";
157381ad6265SDimitry Andric break;
157481ad6265SDimitry Andric case NK_Virtual:
157581ad6265SDimitry Andric OS << " (UseExternalName: false)";
157681ad6265SDimitry Andric break;
157781ad6265SDimitry Andric }
157881ad6265SDimitry Andric OS << "\n";
157981ad6265SDimitry Andric break;
15800b57cec5SDimitry Andric }
15810b57cec5SDimitry Andric }
158281ad6265SDimitry Andric }
15830b57cec5SDimitry Andric
15840b57cec5SDimitry Andric /// A helper class to hold the common YAML parsing state.
15850b57cec5SDimitry Andric class llvm::vfs::RedirectingFileSystemParser {
15860b57cec5SDimitry Andric yaml::Stream &Stream;
15870b57cec5SDimitry Andric
error(yaml::Node * N,const Twine & Msg)15880b57cec5SDimitry Andric void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); }
15890b57cec5SDimitry Andric
15900b57cec5SDimitry Andric // false on error
parseScalarString(yaml::Node * N,StringRef & Result,SmallVectorImpl<char> & Storage)15910b57cec5SDimitry Andric bool parseScalarString(yaml::Node *N, StringRef &Result,
15920b57cec5SDimitry Andric SmallVectorImpl<char> &Storage) {
15930b57cec5SDimitry Andric const auto *S = dyn_cast<yaml::ScalarNode>(N);
15940b57cec5SDimitry Andric
15950b57cec5SDimitry Andric if (!S) {
15960b57cec5SDimitry Andric error(N, "expected string");
15970b57cec5SDimitry Andric return false;
15980b57cec5SDimitry Andric }
15990b57cec5SDimitry Andric Result = S->getValue(Storage);
16000b57cec5SDimitry Andric return true;
16010b57cec5SDimitry Andric }
16020b57cec5SDimitry Andric
16030b57cec5SDimitry Andric // false on error
parseScalarBool(yaml::Node * N,bool & Result)16040b57cec5SDimitry Andric bool parseScalarBool(yaml::Node *N, bool &Result) {
16050b57cec5SDimitry Andric SmallString<5> Storage;
16060b57cec5SDimitry Andric StringRef Value;
16070b57cec5SDimitry Andric if (!parseScalarString(N, Value, Storage))
16080b57cec5SDimitry Andric return false;
16090b57cec5SDimitry Andric
1610fe6060f1SDimitry Andric if (Value.equals_insensitive("true") || Value.equals_insensitive("on") ||
1611fe6060f1SDimitry Andric Value.equals_insensitive("yes") || Value == "1") {
16120b57cec5SDimitry Andric Result = true;
16130b57cec5SDimitry Andric return true;
1614fe6060f1SDimitry Andric } else if (Value.equals_insensitive("false") ||
1615fe6060f1SDimitry Andric Value.equals_insensitive("off") ||
1616fe6060f1SDimitry Andric Value.equals_insensitive("no") || Value == "0") {
16170b57cec5SDimitry Andric Result = false;
16180b57cec5SDimitry Andric return true;
16190b57cec5SDimitry Andric }
16200b57cec5SDimitry Andric
16210b57cec5SDimitry Andric error(N, "expected boolean value");
16220b57cec5SDimitry Andric return false;
16230b57cec5SDimitry Andric }
16240b57cec5SDimitry Andric
1625bdd1243dSDimitry Andric std::optional<RedirectingFileSystem::RedirectKind>
parseRedirectKind(yaml::Node * N)162681ad6265SDimitry Andric parseRedirectKind(yaml::Node *N) {
162781ad6265SDimitry Andric SmallString<12> Storage;
162881ad6265SDimitry Andric StringRef Value;
162981ad6265SDimitry Andric if (!parseScalarString(N, Value, Storage))
1630bdd1243dSDimitry Andric return std::nullopt;
163181ad6265SDimitry Andric
163281ad6265SDimitry Andric if (Value.equals_insensitive("fallthrough")) {
163381ad6265SDimitry Andric return RedirectingFileSystem::RedirectKind::Fallthrough;
163481ad6265SDimitry Andric } else if (Value.equals_insensitive("fallback")) {
163581ad6265SDimitry Andric return RedirectingFileSystem::RedirectKind::Fallback;
163681ad6265SDimitry Andric } else if (Value.equals_insensitive("redirect-only")) {
163781ad6265SDimitry Andric return RedirectingFileSystem::RedirectKind::RedirectOnly;
163881ad6265SDimitry Andric }
1639bdd1243dSDimitry Andric return std::nullopt;
1640bdd1243dSDimitry Andric }
1641bdd1243dSDimitry Andric
1642bdd1243dSDimitry Andric std::optional<RedirectingFileSystem::RootRelativeKind>
parseRootRelativeKind(yaml::Node * N)1643bdd1243dSDimitry Andric parseRootRelativeKind(yaml::Node *N) {
1644bdd1243dSDimitry Andric SmallString<12> Storage;
1645bdd1243dSDimitry Andric StringRef Value;
1646bdd1243dSDimitry Andric if (!parseScalarString(N, Value, Storage))
1647bdd1243dSDimitry Andric return std::nullopt;
1648bdd1243dSDimitry Andric if (Value.equals_insensitive("cwd")) {
1649bdd1243dSDimitry Andric return RedirectingFileSystem::RootRelativeKind::CWD;
1650bdd1243dSDimitry Andric } else if (Value.equals_insensitive("overlay-dir")) {
1651bdd1243dSDimitry Andric return RedirectingFileSystem::RootRelativeKind::OverlayDir;
1652bdd1243dSDimitry Andric }
1653bdd1243dSDimitry Andric return std::nullopt;
165481ad6265SDimitry Andric }
165581ad6265SDimitry Andric
16560b57cec5SDimitry Andric struct KeyStatus {
16570b57cec5SDimitry Andric bool Required;
16580b57cec5SDimitry Andric bool Seen = false;
16590b57cec5SDimitry Andric
KeyStatusllvm::vfs::RedirectingFileSystemParser::KeyStatus16600b57cec5SDimitry Andric KeyStatus(bool Required = false) : Required(Required) {}
16610b57cec5SDimitry Andric };
16620b57cec5SDimitry Andric
16630b57cec5SDimitry Andric using KeyStatusPair = std::pair<StringRef, KeyStatus>;
16640b57cec5SDimitry Andric
16650b57cec5SDimitry Andric // false on error
checkDuplicateOrUnknownKey(yaml::Node * KeyNode,StringRef Key,DenseMap<StringRef,KeyStatus> & Keys)16660b57cec5SDimitry Andric bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
16670b57cec5SDimitry Andric DenseMap<StringRef, KeyStatus> &Keys) {
16680b57cec5SDimitry Andric if (!Keys.count(Key)) {
16690b57cec5SDimitry Andric error(KeyNode, "unknown key");
16700b57cec5SDimitry Andric return false;
16710b57cec5SDimitry Andric }
16720b57cec5SDimitry Andric KeyStatus &S = Keys[Key];
16730b57cec5SDimitry Andric if (S.Seen) {
16740b57cec5SDimitry Andric error(KeyNode, Twine("duplicate key '") + Key + "'");
16750b57cec5SDimitry Andric return false;
16760b57cec5SDimitry Andric }
16770b57cec5SDimitry Andric S.Seen = true;
16780b57cec5SDimitry Andric return true;
16790b57cec5SDimitry Andric }
16800b57cec5SDimitry Andric
16810b57cec5SDimitry Andric // false on error
checkMissingKeys(yaml::Node * Obj,DenseMap<StringRef,KeyStatus> & Keys)16820b57cec5SDimitry Andric bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
16830b57cec5SDimitry Andric for (const auto &I : Keys) {
16840b57cec5SDimitry Andric if (I.second.Required && !I.second.Seen) {
16850b57cec5SDimitry Andric error(Obj, Twine("missing key '") + I.first + "'");
16860b57cec5SDimitry Andric return false;
16870b57cec5SDimitry Andric }
16880b57cec5SDimitry Andric }
16890b57cec5SDimitry Andric return true;
16900b57cec5SDimitry Andric }
16910b57cec5SDimitry Andric
1692e8d8bef9SDimitry Andric public:
1693e8d8bef9SDimitry Andric static RedirectingFileSystem::Entry *
lookupOrCreateEntry(RedirectingFileSystem * FS,StringRef Name,RedirectingFileSystem::Entry * ParentEntry=nullptr)16940b57cec5SDimitry Andric lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,
16950b57cec5SDimitry Andric RedirectingFileSystem::Entry *ParentEntry = nullptr) {
16960b57cec5SDimitry Andric if (!ParentEntry) { // Look for a existent root
16970b57cec5SDimitry Andric for (const auto &Root : FS->Roots) {
16980b57cec5SDimitry Andric if (Name.equals(Root->getName())) {
16990b57cec5SDimitry Andric ParentEntry = Root.get();
17000b57cec5SDimitry Andric return ParentEntry;
17010b57cec5SDimitry Andric }
17020b57cec5SDimitry Andric }
17030b57cec5SDimitry Andric } else { // Advance to the next component
1704fe6060f1SDimitry Andric auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
17050b57cec5SDimitry Andric for (std::unique_ptr<RedirectingFileSystem::Entry> &Content :
17060b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) {
17070b57cec5SDimitry Andric auto *DirContent =
1708fe6060f1SDimitry Andric dyn_cast<RedirectingFileSystem::DirectoryEntry>(Content.get());
17090b57cec5SDimitry Andric if (DirContent && Name.equals(Content->getName()))
17100b57cec5SDimitry Andric return DirContent;
17110b57cec5SDimitry Andric }
17120b57cec5SDimitry Andric }
17130b57cec5SDimitry Andric
17140b57cec5SDimitry Andric // ... or create a new one
17150b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem::Entry> E =
1716fe6060f1SDimitry Andric std::make_unique<RedirectingFileSystem::DirectoryEntry>(
17170b57cec5SDimitry Andric Name, Status("", getNextVirtualUniqueID(),
17180b57cec5SDimitry Andric std::chrono::system_clock::now(), 0, 0, 0,
17190b57cec5SDimitry Andric file_type::directory_file, sys::fs::all_all));
17200b57cec5SDimitry Andric
17210b57cec5SDimitry Andric if (!ParentEntry) { // Add a new root to the overlay
17220b57cec5SDimitry Andric FS->Roots.push_back(std::move(E));
17230b57cec5SDimitry Andric ParentEntry = FS->Roots.back().get();
17240b57cec5SDimitry Andric return ParentEntry;
17250b57cec5SDimitry Andric }
17260b57cec5SDimitry Andric
1727fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(ParentEntry);
17280b57cec5SDimitry Andric DE->addContent(std::move(E));
17290b57cec5SDimitry Andric return DE->getLastContent();
17300b57cec5SDimitry Andric }
17310b57cec5SDimitry Andric
1732e8d8bef9SDimitry Andric private:
uniqueOverlayTree(RedirectingFileSystem * FS,RedirectingFileSystem::Entry * SrcE,RedirectingFileSystem::Entry * NewParentE=nullptr)17330b57cec5SDimitry Andric void uniqueOverlayTree(RedirectingFileSystem *FS,
17340b57cec5SDimitry Andric RedirectingFileSystem::Entry *SrcE,
17350b57cec5SDimitry Andric RedirectingFileSystem::Entry *NewParentE = nullptr) {
17360b57cec5SDimitry Andric StringRef Name = SrcE->getName();
17370b57cec5SDimitry Andric switch (SrcE->getKind()) {
17380b57cec5SDimitry Andric case RedirectingFileSystem::EK_Directory: {
1739fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
17400b57cec5SDimitry Andric // Empty directories could be present in the YAML as a way to
17410b57cec5SDimitry Andric // describe a file for a current directory after some of its subdir
17420b57cec5SDimitry Andric // is parsed. This only leads to redundant walks, ignore it.
17430b57cec5SDimitry Andric if (!Name.empty())
17440b57cec5SDimitry Andric NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
17450b57cec5SDimitry Andric for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
17460b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end()))
17470b57cec5SDimitry Andric uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
17480b57cec5SDimitry Andric break;
17490b57cec5SDimitry Andric }
1750fe6060f1SDimitry Andric case RedirectingFileSystem::EK_DirectoryRemap: {
1751fe6060f1SDimitry Andric assert(NewParentE && "Parent entry must exist");
1752fe6060f1SDimitry Andric auto *DR = cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
1753fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1754fe6060f1SDimitry Andric DE->addContent(
1755fe6060f1SDimitry Andric std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
1756fe6060f1SDimitry Andric Name, DR->getExternalContentsPath(), DR->getUseName()));
1757fe6060f1SDimitry Andric break;
1758fe6060f1SDimitry Andric }
17590b57cec5SDimitry Andric case RedirectingFileSystem::EK_File: {
17600b57cec5SDimitry Andric assert(NewParentE && "Parent entry must exist");
1761fe6060f1SDimitry Andric auto *FE = cast<RedirectingFileSystem::FileEntry>(SrcE);
1762fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(NewParentE);
1763fe6060f1SDimitry Andric DE->addContent(std::make_unique<RedirectingFileSystem::FileEntry>(
17640b57cec5SDimitry Andric Name, FE->getExternalContentsPath(), FE->getUseName()));
17650b57cec5SDimitry Andric break;
17660b57cec5SDimitry Andric }
17670b57cec5SDimitry Andric }
17680b57cec5SDimitry Andric }
17690b57cec5SDimitry Andric
17700b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem::Entry>
parseEntry(yaml::Node * N,RedirectingFileSystem * FS,bool IsRootEntry)17710b57cec5SDimitry Andric parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) {
17720b57cec5SDimitry Andric auto *M = dyn_cast<yaml::MappingNode>(N);
17730b57cec5SDimitry Andric if (!M) {
17740b57cec5SDimitry Andric error(N, "expected mapping node for file or directory entry");
17750b57cec5SDimitry Andric return nullptr;
17760b57cec5SDimitry Andric }
17770b57cec5SDimitry Andric
17780b57cec5SDimitry Andric KeyStatusPair Fields[] = {
17790b57cec5SDimitry Andric KeyStatusPair("name", true),
17800b57cec5SDimitry Andric KeyStatusPair("type", true),
17810b57cec5SDimitry Andric KeyStatusPair("contents", false),
17820b57cec5SDimitry Andric KeyStatusPair("external-contents", false),
17830b57cec5SDimitry Andric KeyStatusPair("use-external-name", false),
17840b57cec5SDimitry Andric };
17850b57cec5SDimitry Andric
17860b57cec5SDimitry Andric DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
17870b57cec5SDimitry Andric
1788fe6060f1SDimitry Andric enum { CF_NotSet, CF_List, CF_External } ContentsField = CF_NotSet;
17890b57cec5SDimitry Andric std::vector<std::unique_ptr<RedirectingFileSystem::Entry>>
17900b57cec5SDimitry Andric EntryArrayContents;
17915ffd83dbSDimitry Andric SmallString<256> ExternalContentsPath;
17925ffd83dbSDimitry Andric SmallString<256> Name;
17930b57cec5SDimitry Andric yaml::Node *NameValueNode = nullptr;
1794fe6060f1SDimitry Andric auto UseExternalName = RedirectingFileSystem::NK_NotSet;
17950b57cec5SDimitry Andric RedirectingFileSystem::EntryKind Kind;
17960b57cec5SDimitry Andric
17970b57cec5SDimitry Andric for (auto &I : *M) {
17980b57cec5SDimitry Andric StringRef Key;
17990b57cec5SDimitry Andric // Reuse the buffer for key and value, since we don't look at key after
18000b57cec5SDimitry Andric // parsing value.
18010b57cec5SDimitry Andric SmallString<256> Buffer;
18020b57cec5SDimitry Andric if (!parseScalarString(I.getKey(), Key, Buffer))
18030b57cec5SDimitry Andric return nullptr;
18040b57cec5SDimitry Andric
18050b57cec5SDimitry Andric if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
18060b57cec5SDimitry Andric return nullptr;
18070b57cec5SDimitry Andric
18080b57cec5SDimitry Andric StringRef Value;
18090b57cec5SDimitry Andric if (Key == "name") {
18100b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), Value, Buffer))
18110b57cec5SDimitry Andric return nullptr;
18120b57cec5SDimitry Andric
18130b57cec5SDimitry Andric NameValueNode = I.getValue();
18140b57cec5SDimitry Andric // Guarantee that old YAML files containing paths with ".." and "."
18150b57cec5SDimitry Andric // are properly canonicalized before read into the VFS.
18165ffd83dbSDimitry Andric Name = canonicalize(Value).str();
18170b57cec5SDimitry Andric } else if (Key == "type") {
18180b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), Value, Buffer))
18190b57cec5SDimitry Andric return nullptr;
18200b57cec5SDimitry Andric if (Value == "file")
18210b57cec5SDimitry Andric Kind = RedirectingFileSystem::EK_File;
18220b57cec5SDimitry Andric else if (Value == "directory")
18230b57cec5SDimitry Andric Kind = RedirectingFileSystem::EK_Directory;
1824fe6060f1SDimitry Andric else if (Value == "directory-remap")
1825fe6060f1SDimitry Andric Kind = RedirectingFileSystem::EK_DirectoryRemap;
18260b57cec5SDimitry Andric else {
18270b57cec5SDimitry Andric error(I.getValue(), "unknown value for 'type'");
18280b57cec5SDimitry Andric return nullptr;
18290b57cec5SDimitry Andric }
18300b57cec5SDimitry Andric } else if (Key == "contents") {
1831fe6060f1SDimitry Andric if (ContentsField != CF_NotSet) {
18320b57cec5SDimitry Andric error(I.getKey(),
18330b57cec5SDimitry Andric "entry already has 'contents' or 'external-contents'");
18340b57cec5SDimitry Andric return nullptr;
18350b57cec5SDimitry Andric }
1836fe6060f1SDimitry Andric ContentsField = CF_List;
18370b57cec5SDimitry Andric auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue());
18380b57cec5SDimitry Andric if (!Contents) {
18390b57cec5SDimitry Andric // FIXME: this is only for directories, what about files?
18400b57cec5SDimitry Andric error(I.getValue(), "expected array");
18410b57cec5SDimitry Andric return nullptr;
18420b57cec5SDimitry Andric }
18430b57cec5SDimitry Andric
18440b57cec5SDimitry Andric for (auto &I : *Contents) {
18450b57cec5SDimitry Andric if (std::unique_ptr<RedirectingFileSystem::Entry> E =
18460b57cec5SDimitry Andric parseEntry(&I, FS, /*IsRootEntry*/ false))
18470b57cec5SDimitry Andric EntryArrayContents.push_back(std::move(E));
18480b57cec5SDimitry Andric else
18490b57cec5SDimitry Andric return nullptr;
18500b57cec5SDimitry Andric }
18510b57cec5SDimitry Andric } else if (Key == "external-contents") {
1852fe6060f1SDimitry Andric if (ContentsField != CF_NotSet) {
18530b57cec5SDimitry Andric error(I.getKey(),
18540b57cec5SDimitry Andric "entry already has 'contents' or 'external-contents'");
18550b57cec5SDimitry Andric return nullptr;
18560b57cec5SDimitry Andric }
1857fe6060f1SDimitry Andric ContentsField = CF_External;
18580b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), Value, Buffer))
18590b57cec5SDimitry Andric return nullptr;
18600b57cec5SDimitry Andric
18610b57cec5SDimitry Andric SmallString<256> FullPath;
18620b57cec5SDimitry Andric if (FS->IsRelativeOverlay) {
1863bdd1243dSDimitry Andric FullPath = FS->getOverlayFileDir();
18640b57cec5SDimitry Andric assert(!FullPath.empty() &&
18650b57cec5SDimitry Andric "External contents prefix directory must exist");
18660b57cec5SDimitry Andric llvm::sys::path::append(FullPath, Value);
18670b57cec5SDimitry Andric } else {
18680b57cec5SDimitry Andric FullPath = Value;
18690b57cec5SDimitry Andric }
18700b57cec5SDimitry Andric
18710b57cec5SDimitry Andric // Guarantee that old YAML files containing paths with ".." and "."
18720b57cec5SDimitry Andric // are properly canonicalized before read into the VFS.
18735ffd83dbSDimitry Andric FullPath = canonicalize(FullPath);
18740b57cec5SDimitry Andric ExternalContentsPath = FullPath.str();
18750b57cec5SDimitry Andric } else if (Key == "use-external-name") {
18760b57cec5SDimitry Andric bool Val;
18770b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), Val))
18780b57cec5SDimitry Andric return nullptr;
1879fe6060f1SDimitry Andric UseExternalName = Val ? RedirectingFileSystem::NK_External
1880fe6060f1SDimitry Andric : RedirectingFileSystem::NK_Virtual;
18810b57cec5SDimitry Andric } else {
18820b57cec5SDimitry Andric llvm_unreachable("key missing from Keys");
18830b57cec5SDimitry Andric }
18840b57cec5SDimitry Andric }
18850b57cec5SDimitry Andric
18860b57cec5SDimitry Andric if (Stream.failed())
18870b57cec5SDimitry Andric return nullptr;
18880b57cec5SDimitry Andric
18890b57cec5SDimitry Andric // check for missing keys
1890fe6060f1SDimitry Andric if (ContentsField == CF_NotSet) {
18910b57cec5SDimitry Andric error(N, "missing key 'contents' or 'external-contents'");
18920b57cec5SDimitry Andric return nullptr;
18930b57cec5SDimitry Andric }
18940b57cec5SDimitry Andric if (!checkMissingKeys(N, Keys))
18950b57cec5SDimitry Andric return nullptr;
18960b57cec5SDimitry Andric
18970b57cec5SDimitry Andric // check invalid configuration
18980b57cec5SDimitry Andric if (Kind == RedirectingFileSystem::EK_Directory &&
1899fe6060f1SDimitry Andric UseExternalName != RedirectingFileSystem::NK_NotSet) {
1900fe6060f1SDimitry Andric error(N, "'use-external-name' is not supported for 'directory' entries");
1901fe6060f1SDimitry Andric return nullptr;
1902fe6060f1SDimitry Andric }
1903fe6060f1SDimitry Andric
1904fe6060f1SDimitry Andric if (Kind == RedirectingFileSystem::EK_DirectoryRemap &&
1905fe6060f1SDimitry Andric ContentsField == CF_List) {
1906fe6060f1SDimitry Andric error(N, "'contents' is not supported for 'directory-remap' entries");
19070b57cec5SDimitry Andric return nullptr;
19080b57cec5SDimitry Andric }
19090b57cec5SDimitry Andric
1910480093f4SDimitry Andric sys::path::Style path_style = sys::path::Style::native;
1911480093f4SDimitry Andric if (IsRootEntry) {
1912480093f4SDimitry Andric // VFS root entries may be in either Posix or Windows style. Figure out
1913480093f4SDimitry Andric // which style we have, and use it consistently.
1914480093f4SDimitry Andric if (sys::path::is_absolute(Name, sys::path::Style::posix)) {
1915480093f4SDimitry Andric path_style = sys::path::Style::posix;
1916349cc55cSDimitry Andric } else if (sys::path::is_absolute(Name,
1917349cc55cSDimitry Andric sys::path::Style::windows_backslash)) {
1918349cc55cSDimitry Andric path_style = sys::path::Style::windows_backslash;
1919480093f4SDimitry Andric } else {
1920bdd1243dSDimitry Andric // Relative VFS root entries are made absolute to either the overlay
1921bdd1243dSDimitry Andric // directory, or the current working directory, then we can determine
1922bdd1243dSDimitry Andric // the path style from that.
1923bdd1243dSDimitry Andric std::error_code EC;
1924bdd1243dSDimitry Andric if (FS->RootRelative ==
1925bdd1243dSDimitry Andric RedirectingFileSystem::RootRelativeKind::OverlayDir) {
1926bdd1243dSDimitry Andric StringRef FullPath = FS->getOverlayFileDir();
1927bdd1243dSDimitry Andric assert(!FullPath.empty() && "Overlay file directory must exist");
1928bdd1243dSDimitry Andric EC = FS->makeAbsolute(FullPath, Name);
1929bdd1243dSDimitry Andric Name = canonicalize(Name);
1930bdd1243dSDimitry Andric } else {
1931bdd1243dSDimitry Andric EC = sys::fs::make_absolute(Name);
1932bdd1243dSDimitry Andric }
193304eeddc0SDimitry Andric if (EC) {
19340b57cec5SDimitry Andric assert(NameValueNode && "Name presence should be checked earlier");
193504eeddc0SDimitry Andric error(
193604eeddc0SDimitry Andric NameValueNode,
19370b57cec5SDimitry Andric "entry with relative path at the root level is not discoverable");
19380b57cec5SDimitry Andric return nullptr;
19390b57cec5SDimitry Andric }
194004eeddc0SDimitry Andric path_style = sys::path::is_absolute(Name, sys::path::Style::posix)
194104eeddc0SDimitry Andric ? sys::path::Style::posix
194204eeddc0SDimitry Andric : sys::path::Style::windows_backslash;
194304eeddc0SDimitry Andric }
1944bdd1243dSDimitry Andric // is::path::is_absolute(Name, sys::path::Style::windows_backslash) will
1945bdd1243dSDimitry Andric // return true even if `Name` is using forward slashes. Distinguish
1946bdd1243dSDimitry Andric // between windows_backslash and windows_slash.
1947bdd1243dSDimitry Andric if (path_style == sys::path::Style::windows_backslash &&
1948bdd1243dSDimitry Andric getExistingStyle(Name) != sys::path::Style::windows_backslash)
1949bdd1243dSDimitry Andric path_style = sys::path::Style::windows_slash;
1950480093f4SDimitry Andric }
19510b57cec5SDimitry Andric
19520b57cec5SDimitry Andric // Remove trailing slash(es), being careful not to remove the root path
1953fe6060f1SDimitry Andric StringRef Trimmed = Name;
1954480093f4SDimitry Andric size_t RootPathLen = sys::path::root_path(Trimmed, path_style).size();
19550b57cec5SDimitry Andric while (Trimmed.size() > RootPathLen &&
1956480093f4SDimitry Andric sys::path::is_separator(Trimmed.back(), path_style))
19570b57cec5SDimitry Andric Trimmed = Trimmed.slice(0, Trimmed.size() - 1);
1958480093f4SDimitry Andric
19590b57cec5SDimitry Andric // Get the last component
1960480093f4SDimitry Andric StringRef LastComponent = sys::path::filename(Trimmed, path_style);
19610b57cec5SDimitry Andric
19620b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem::Entry> Result;
19630b57cec5SDimitry Andric switch (Kind) {
19640b57cec5SDimitry Andric case RedirectingFileSystem::EK_File:
1965fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::FileEntry>(
1966fe6060f1SDimitry Andric LastComponent, std::move(ExternalContentsPath), UseExternalName);
1967fe6060f1SDimitry Andric break;
1968fe6060f1SDimitry Andric case RedirectingFileSystem::EK_DirectoryRemap:
1969fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::DirectoryRemapEntry>(
19700b57cec5SDimitry Andric LastComponent, std::move(ExternalContentsPath), UseExternalName);
19710b57cec5SDimitry Andric break;
19720b57cec5SDimitry Andric case RedirectingFileSystem::EK_Directory:
1973fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
19740b57cec5SDimitry Andric LastComponent, std::move(EntryArrayContents),
1975fe6060f1SDimitry Andric Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1976fe6060f1SDimitry Andric 0, 0, 0, file_type::directory_file, sys::fs::all_all));
19770b57cec5SDimitry Andric break;
19780b57cec5SDimitry Andric }
19790b57cec5SDimitry Andric
1980480093f4SDimitry Andric StringRef Parent = sys::path::parent_path(Trimmed, path_style);
19810b57cec5SDimitry Andric if (Parent.empty())
19820b57cec5SDimitry Andric return Result;
19830b57cec5SDimitry Andric
19840b57cec5SDimitry Andric // if 'name' contains multiple components, create implicit directory entries
1985480093f4SDimitry Andric for (sys::path::reverse_iterator I = sys::path::rbegin(Parent, path_style),
19860b57cec5SDimitry Andric E = sys::path::rend(Parent);
19870b57cec5SDimitry Andric I != E; ++I) {
19880b57cec5SDimitry Andric std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries;
19890b57cec5SDimitry Andric Entries.push_back(std::move(Result));
1990fe6060f1SDimitry Andric Result = std::make_unique<RedirectingFileSystem::DirectoryEntry>(
19910b57cec5SDimitry Andric *I, std::move(Entries),
1992fe6060f1SDimitry Andric Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1993fe6060f1SDimitry Andric 0, 0, 0, file_type::directory_file, sys::fs::all_all));
19940b57cec5SDimitry Andric }
19950b57cec5SDimitry Andric return Result;
19960b57cec5SDimitry Andric }
19970b57cec5SDimitry Andric
19980b57cec5SDimitry Andric public:
RedirectingFileSystemParser(yaml::Stream & S)19990b57cec5SDimitry Andric RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
20000b57cec5SDimitry Andric
20010b57cec5SDimitry Andric // false on error
parse(yaml::Node * Root,RedirectingFileSystem * FS)20020b57cec5SDimitry Andric bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
20030b57cec5SDimitry Andric auto *Top = dyn_cast<yaml::MappingNode>(Root);
20040b57cec5SDimitry Andric if (!Top) {
20050b57cec5SDimitry Andric error(Root, "expected mapping node");
20060b57cec5SDimitry Andric return false;
20070b57cec5SDimitry Andric }
20080b57cec5SDimitry Andric
20090b57cec5SDimitry Andric KeyStatusPair Fields[] = {
20100b57cec5SDimitry Andric KeyStatusPair("version", true),
20110b57cec5SDimitry Andric KeyStatusPair("case-sensitive", false),
20120b57cec5SDimitry Andric KeyStatusPair("use-external-names", false),
2013bdd1243dSDimitry Andric KeyStatusPair("root-relative", false),
20140b57cec5SDimitry Andric KeyStatusPair("overlay-relative", false),
20150b57cec5SDimitry Andric KeyStatusPair("fallthrough", false),
201681ad6265SDimitry Andric KeyStatusPair("redirecting-with", false),
20170b57cec5SDimitry Andric KeyStatusPair("roots", true),
20180b57cec5SDimitry Andric };
20190b57cec5SDimitry Andric
20200b57cec5SDimitry Andric DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
20210b57cec5SDimitry Andric std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries;
20220b57cec5SDimitry Andric
20230b57cec5SDimitry Andric // Parse configuration and 'roots'
20240b57cec5SDimitry Andric for (auto &I : *Top) {
20250b57cec5SDimitry Andric SmallString<10> KeyBuffer;
20260b57cec5SDimitry Andric StringRef Key;
20270b57cec5SDimitry Andric if (!parseScalarString(I.getKey(), Key, KeyBuffer))
20280b57cec5SDimitry Andric return false;
20290b57cec5SDimitry Andric
20300b57cec5SDimitry Andric if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
20310b57cec5SDimitry Andric return false;
20320b57cec5SDimitry Andric
20330b57cec5SDimitry Andric if (Key == "roots") {
20340b57cec5SDimitry Andric auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue());
20350b57cec5SDimitry Andric if (!Roots) {
20360b57cec5SDimitry Andric error(I.getValue(), "expected array");
20370b57cec5SDimitry Andric return false;
20380b57cec5SDimitry Andric }
20390b57cec5SDimitry Andric
20400b57cec5SDimitry Andric for (auto &I : *Roots) {
20410b57cec5SDimitry Andric if (std::unique_ptr<RedirectingFileSystem::Entry> E =
20420b57cec5SDimitry Andric parseEntry(&I, FS, /*IsRootEntry*/ true))
20430b57cec5SDimitry Andric RootEntries.push_back(std::move(E));
20440b57cec5SDimitry Andric else
20450b57cec5SDimitry Andric return false;
20460b57cec5SDimitry Andric }
20470b57cec5SDimitry Andric } else if (Key == "version") {
20480b57cec5SDimitry Andric StringRef VersionString;
20490b57cec5SDimitry Andric SmallString<4> Storage;
20500b57cec5SDimitry Andric if (!parseScalarString(I.getValue(), VersionString, Storage))
20510b57cec5SDimitry Andric return false;
20520b57cec5SDimitry Andric int Version;
20530b57cec5SDimitry Andric if (VersionString.getAsInteger<int>(10, Version)) {
20540b57cec5SDimitry Andric error(I.getValue(), "expected integer");
20550b57cec5SDimitry Andric return false;
20560b57cec5SDimitry Andric }
20570b57cec5SDimitry Andric if (Version < 0) {
20580b57cec5SDimitry Andric error(I.getValue(), "invalid version number");
20590b57cec5SDimitry Andric return false;
20600b57cec5SDimitry Andric }
20610b57cec5SDimitry Andric if (Version != 0) {
20620b57cec5SDimitry Andric error(I.getValue(), "version mismatch, expected 0");
20630b57cec5SDimitry Andric return false;
20640b57cec5SDimitry Andric }
20650b57cec5SDimitry Andric } else if (Key == "case-sensitive") {
20660b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), FS->CaseSensitive))
20670b57cec5SDimitry Andric return false;
20680b57cec5SDimitry Andric } else if (Key == "overlay-relative") {
20690b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay))
20700b57cec5SDimitry Andric return false;
20710b57cec5SDimitry Andric } else if (Key == "use-external-names") {
20720b57cec5SDimitry Andric if (!parseScalarBool(I.getValue(), FS->UseExternalNames))
20730b57cec5SDimitry Andric return false;
20740b57cec5SDimitry Andric } else if (Key == "fallthrough") {
207581ad6265SDimitry Andric if (Keys["redirecting-with"].Seen) {
207681ad6265SDimitry Andric error(I.getValue(),
207781ad6265SDimitry Andric "'fallthrough' and 'redirecting-with' are mutually exclusive");
20780b57cec5SDimitry Andric return false;
207981ad6265SDimitry Andric }
208081ad6265SDimitry Andric
208181ad6265SDimitry Andric bool ShouldFallthrough = false;
208281ad6265SDimitry Andric if (!parseScalarBool(I.getValue(), ShouldFallthrough))
208381ad6265SDimitry Andric return false;
208481ad6265SDimitry Andric
208581ad6265SDimitry Andric if (ShouldFallthrough) {
208681ad6265SDimitry Andric FS->Redirection = RedirectingFileSystem::RedirectKind::Fallthrough;
208781ad6265SDimitry Andric } else {
208881ad6265SDimitry Andric FS->Redirection = RedirectingFileSystem::RedirectKind::RedirectOnly;
208981ad6265SDimitry Andric }
209081ad6265SDimitry Andric } else if (Key == "redirecting-with") {
209181ad6265SDimitry Andric if (Keys["fallthrough"].Seen) {
209281ad6265SDimitry Andric error(I.getValue(),
209381ad6265SDimitry Andric "'fallthrough' and 'redirecting-with' are mutually exclusive");
209481ad6265SDimitry Andric return false;
209581ad6265SDimitry Andric }
209681ad6265SDimitry Andric
209781ad6265SDimitry Andric if (auto Kind = parseRedirectKind(I.getValue())) {
209881ad6265SDimitry Andric FS->Redirection = *Kind;
209981ad6265SDimitry Andric } else {
210081ad6265SDimitry Andric error(I.getValue(), "expected valid redirect kind");
210181ad6265SDimitry Andric return false;
210281ad6265SDimitry Andric }
2103bdd1243dSDimitry Andric } else if (Key == "root-relative") {
2104bdd1243dSDimitry Andric if (auto Kind = parseRootRelativeKind(I.getValue())) {
2105bdd1243dSDimitry Andric FS->RootRelative = *Kind;
2106bdd1243dSDimitry Andric } else {
2107bdd1243dSDimitry Andric error(I.getValue(), "expected valid root-relative kind");
2108bdd1243dSDimitry Andric return false;
2109bdd1243dSDimitry Andric }
21100b57cec5SDimitry Andric } else {
21110b57cec5SDimitry Andric llvm_unreachable("key missing from Keys");
21120b57cec5SDimitry Andric }
21130b57cec5SDimitry Andric }
21140b57cec5SDimitry Andric
21150b57cec5SDimitry Andric if (Stream.failed())
21160b57cec5SDimitry Andric return false;
21170b57cec5SDimitry Andric
21180b57cec5SDimitry Andric if (!checkMissingKeys(Top, Keys))
21190b57cec5SDimitry Andric return false;
21200b57cec5SDimitry Andric
21210b57cec5SDimitry Andric // Now that we sucessefully parsed the YAML file, canonicalize the internal
21220b57cec5SDimitry Andric // representation to a proper directory tree so that we can search faster
21230b57cec5SDimitry Andric // inside the VFS.
21240b57cec5SDimitry Andric for (auto &E : RootEntries)
21250b57cec5SDimitry Andric uniqueOverlayTree(FS, E.get());
21260b57cec5SDimitry Andric
21270b57cec5SDimitry Andric return true;
21280b57cec5SDimitry Andric }
21290b57cec5SDimitry Andric };
21300b57cec5SDimitry Andric
2131e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem>
create(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)21320b57cec5SDimitry Andric RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
21330b57cec5SDimitry Andric SourceMgr::DiagHandlerTy DiagHandler,
21340b57cec5SDimitry Andric StringRef YAMLFilePath, void *DiagContext,
21350b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> ExternalFS) {
21360b57cec5SDimitry Andric SourceMgr SM;
21370b57cec5SDimitry Andric yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
21380b57cec5SDimitry Andric
21390b57cec5SDimitry Andric SM.setDiagHandler(DiagHandler, DiagContext);
21400b57cec5SDimitry Andric yaml::document_iterator DI = Stream.begin();
21410b57cec5SDimitry Andric yaml::Node *Root = DI->getRoot();
21420b57cec5SDimitry Andric if (DI == Stream.end() || !Root) {
21430b57cec5SDimitry Andric SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
21440b57cec5SDimitry Andric return nullptr;
21450b57cec5SDimitry Andric }
21460b57cec5SDimitry Andric
21470b57cec5SDimitry Andric RedirectingFileSystemParser P(Stream);
21480b57cec5SDimitry Andric
21490b57cec5SDimitry Andric std::unique_ptr<RedirectingFileSystem> FS(
21508bcb0991SDimitry Andric new RedirectingFileSystem(ExternalFS));
21510b57cec5SDimitry Andric
21520b57cec5SDimitry Andric if (!YAMLFilePath.empty()) {
21530b57cec5SDimitry Andric // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
21540b57cec5SDimitry Andric // to each 'external-contents' path.
21550b57cec5SDimitry Andric //
21560b57cec5SDimitry Andric // Example:
21570b57cec5SDimitry Andric // -ivfsoverlay dummy.cache/vfs/vfs.yaml
21580b57cec5SDimitry Andric // yields:
2159bdd1243dSDimitry Andric // FS->OverlayFileDir => /<absolute_path_to>/dummy.cache/vfs
21600b57cec5SDimitry Andric //
21610b57cec5SDimitry Andric SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
21620b57cec5SDimitry Andric std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir);
21630b57cec5SDimitry Andric assert(!EC && "Overlay dir final path must be absolute");
21640b57cec5SDimitry Andric (void)EC;
2165bdd1243dSDimitry Andric FS->setOverlayFileDir(OverlayAbsDir);
21660b57cec5SDimitry Andric }
21670b57cec5SDimitry Andric
21680b57cec5SDimitry Andric if (!P.parse(Root, FS.get()))
21690b57cec5SDimitry Andric return nullptr;
21700b57cec5SDimitry Andric
2171e8d8bef9SDimitry Andric return FS;
21720b57cec5SDimitry Andric }
21730b57cec5SDimitry Andric
create(ArrayRef<std::pair<std::string,std::string>> RemappedFiles,bool UseExternalNames,FileSystem & ExternalFS)2174e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> RedirectingFileSystem::create(
2175e8d8bef9SDimitry Andric ArrayRef<std::pair<std::string, std::string>> RemappedFiles,
2176e8d8bef9SDimitry Andric bool UseExternalNames, FileSystem &ExternalFS) {
2177e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> FS(
2178e8d8bef9SDimitry Andric new RedirectingFileSystem(&ExternalFS));
2179e8d8bef9SDimitry Andric FS->UseExternalNames = UseExternalNames;
21800b57cec5SDimitry Andric
2181e8d8bef9SDimitry Andric StringMap<RedirectingFileSystem::Entry *> Entries;
2182e8d8bef9SDimitry Andric
2183e8d8bef9SDimitry Andric for (auto &Mapping : llvm::reverse(RemappedFiles)) {
2184e8d8bef9SDimitry Andric SmallString<128> From = StringRef(Mapping.first);
2185e8d8bef9SDimitry Andric SmallString<128> To = StringRef(Mapping.second);
2186e8d8bef9SDimitry Andric {
2187e8d8bef9SDimitry Andric auto EC = ExternalFS.makeAbsolute(From);
2188e8d8bef9SDimitry Andric (void)EC;
2189e8d8bef9SDimitry Andric assert(!EC && "Could not make absolute path");
2190e8d8bef9SDimitry Andric }
2191e8d8bef9SDimitry Andric
2192e8d8bef9SDimitry Andric // Check if we've already mapped this file. The first one we see (in the
2193e8d8bef9SDimitry Andric // reverse iteration) wins.
2194e8d8bef9SDimitry Andric RedirectingFileSystem::Entry *&ToEntry = Entries[From];
2195e8d8bef9SDimitry Andric if (ToEntry)
2196e8d8bef9SDimitry Andric continue;
2197e8d8bef9SDimitry Andric
2198e8d8bef9SDimitry Andric // Add parent directories.
2199e8d8bef9SDimitry Andric RedirectingFileSystem::Entry *Parent = nullptr;
2200e8d8bef9SDimitry Andric StringRef FromDirectory = llvm::sys::path::parent_path(From);
2201e8d8bef9SDimitry Andric for (auto I = llvm::sys::path::begin(FromDirectory),
2202e8d8bef9SDimitry Andric E = llvm::sys::path::end(FromDirectory);
2203e8d8bef9SDimitry Andric I != E; ++I) {
2204e8d8bef9SDimitry Andric Parent = RedirectingFileSystemParser::lookupOrCreateEntry(FS.get(), *I,
2205e8d8bef9SDimitry Andric Parent);
2206e8d8bef9SDimitry Andric }
2207e8d8bef9SDimitry Andric assert(Parent && "File without a directory?");
2208e8d8bef9SDimitry Andric {
2209e8d8bef9SDimitry Andric auto EC = ExternalFS.makeAbsolute(To);
2210e8d8bef9SDimitry Andric (void)EC;
2211e8d8bef9SDimitry Andric assert(!EC && "Could not make absolute path");
2212e8d8bef9SDimitry Andric }
2213e8d8bef9SDimitry Andric
2214e8d8bef9SDimitry Andric // Add the file.
2215fe6060f1SDimitry Andric auto NewFile = std::make_unique<RedirectingFileSystem::FileEntry>(
2216e8d8bef9SDimitry Andric llvm::sys::path::filename(From), To,
2217fe6060f1SDimitry Andric UseExternalNames ? RedirectingFileSystem::NK_External
2218fe6060f1SDimitry Andric : RedirectingFileSystem::NK_Virtual);
2219e8d8bef9SDimitry Andric ToEntry = NewFile.get();
2220fe6060f1SDimitry Andric cast<RedirectingFileSystem::DirectoryEntry>(Parent)->addContent(
2221e8d8bef9SDimitry Andric std::move(NewFile));
2222e8d8bef9SDimitry Andric }
2223e8d8bef9SDimitry Andric
2224e8d8bef9SDimitry Andric return FS;
2225e8d8bef9SDimitry Andric }
2226e8d8bef9SDimitry Andric
LookupResult(Entry * E,sys::path::const_iterator Start,sys::path::const_iterator End)2227fe6060f1SDimitry Andric RedirectingFileSystem::LookupResult::LookupResult(
2228fe6060f1SDimitry Andric Entry *E, sys::path::const_iterator Start, sys::path::const_iterator End)
2229fe6060f1SDimitry Andric : E(E) {
2230fe6060f1SDimitry Andric assert(E != nullptr);
2231fe6060f1SDimitry Andric // If the matched entry is a DirectoryRemapEntry, set ExternalRedirect to the
2232fe6060f1SDimitry Andric // path of the directory it maps to in the external file system plus any
2233fe6060f1SDimitry Andric // remaining path components in the provided iterator.
2234fe6060f1SDimitry Andric if (auto *DRE = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(E)) {
2235fe6060f1SDimitry Andric SmallString<256> Redirect(DRE->getExternalContentsPath());
2236fe6060f1SDimitry Andric sys::path::append(Redirect, Start, End,
2237fe6060f1SDimitry Andric getExistingStyle(DRE->getExternalContentsPath()));
2238fe6060f1SDimitry Andric ExternalRedirect = std::string(Redirect);
2239fe6060f1SDimitry Andric }
2240fe6060f1SDimitry Andric }
2241fe6060f1SDimitry Andric
getPath(llvm::SmallVectorImpl<char> & Result) const2242fe013be4SDimitry Andric void RedirectingFileSystem::LookupResult::getPath(
2243fe013be4SDimitry Andric llvm::SmallVectorImpl<char> &Result) const {
2244fe013be4SDimitry Andric Result.clear();
2245fe013be4SDimitry Andric for (Entry *Parent : Parents)
2246fe013be4SDimitry Andric llvm::sys::path::append(Result, Parent->getName());
2247fe013be4SDimitry Andric llvm::sys::path::append(Result, E->getName());
2248fe013be4SDimitry Andric }
2249fe013be4SDimitry Andric
2250e8d8bef9SDimitry Andric std::error_code
makeCanonical(SmallVectorImpl<char> & Path) const2251e8d8bef9SDimitry Andric RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const {
22520b57cec5SDimitry Andric if (std::error_code EC = makeAbsolute(Path))
22530b57cec5SDimitry Andric return EC;
22540b57cec5SDimitry Andric
2255e8d8bef9SDimitry Andric llvm::SmallString<256> CanonicalPath =
2256e8d8bef9SDimitry Andric canonicalize(StringRef(Path.data(), Path.size()));
2257e8d8bef9SDimitry Andric if (CanonicalPath.empty())
22580b57cec5SDimitry Andric return make_error_code(llvm::errc::invalid_argument);
22590b57cec5SDimitry Andric
2260e8d8bef9SDimitry Andric Path.assign(CanonicalPath.begin(), CanonicalPath.end());
2261e8d8bef9SDimitry Andric return {};
2262e8d8bef9SDimitry Andric }
2263e8d8bef9SDimitry Andric
2264fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult>
lookupPath(StringRef Path) const2265e8d8bef9SDimitry Andric RedirectingFileSystem::lookupPath(StringRef Path) const {
22660b57cec5SDimitry Andric sys::path::const_iterator Start = sys::path::begin(Path);
22670b57cec5SDimitry Andric sys::path::const_iterator End = sys::path::end(Path);
2268fe013be4SDimitry Andric llvm::SmallVector<Entry *, 32> Entries;
22690b57cec5SDimitry Andric for (const auto &Root : Roots) {
2270fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result =
2271fe013be4SDimitry Andric lookupPathImpl(Start, End, Root.get(), Entries);
2272fe013be4SDimitry Andric if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) {
2273fe013be4SDimitry Andric Result->Parents = std::move(Entries);
22740b57cec5SDimitry Andric return Result;
22750b57cec5SDimitry Andric }
2276fe013be4SDimitry Andric }
22770b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory);
22780b57cec5SDimitry Andric }
22790b57cec5SDimitry Andric
2280fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult>
lookupPathImpl(sys::path::const_iterator Start,sys::path::const_iterator End,RedirectingFileSystem::Entry * From,llvm::SmallVectorImpl<Entry * > & Entries) const2281fe6060f1SDimitry Andric RedirectingFileSystem::lookupPathImpl(
2282fe6060f1SDimitry Andric sys::path::const_iterator Start, sys::path::const_iterator End,
2283fe013be4SDimitry Andric RedirectingFileSystem::Entry *From,
2284fe013be4SDimitry Andric llvm::SmallVectorImpl<Entry *> &Entries) const {
22850b57cec5SDimitry Andric assert(!isTraversalComponent(*Start) &&
22860b57cec5SDimitry Andric !isTraversalComponent(From->getName()) &&
22870b57cec5SDimitry Andric "Paths should not contain traversal components");
22880b57cec5SDimitry Andric
22890b57cec5SDimitry Andric StringRef FromName = From->getName();
22900b57cec5SDimitry Andric
22910b57cec5SDimitry Andric // Forward the search to the next component in case this is an empty one.
22920b57cec5SDimitry Andric if (!FromName.empty()) {
2293480093f4SDimitry Andric if (!pathComponentMatches(*Start, FromName))
22940b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory);
22950b57cec5SDimitry Andric
22960b57cec5SDimitry Andric ++Start;
22970b57cec5SDimitry Andric
22980b57cec5SDimitry Andric if (Start == End) {
22990b57cec5SDimitry Andric // Match!
2300fe6060f1SDimitry Andric return LookupResult(From, Start, End);
23010b57cec5SDimitry Andric }
23020b57cec5SDimitry Andric }
23030b57cec5SDimitry Andric
2304fe6060f1SDimitry Andric if (isa<RedirectingFileSystem::FileEntry>(From))
23050b57cec5SDimitry Andric return make_error_code(llvm::errc::not_a_directory);
23060b57cec5SDimitry Andric
2307fe6060f1SDimitry Andric if (isa<RedirectingFileSystem::DirectoryRemapEntry>(From))
2308fe6060f1SDimitry Andric return LookupResult(From, Start, End);
2309fe6060f1SDimitry Andric
2310fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(From);
23110b57cec5SDimitry Andric for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry :
23120b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) {
2313fe013be4SDimitry Andric Entries.push_back(From);
2314fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result =
2315fe013be4SDimitry Andric lookupPathImpl(Start, End, DirEntry.get(), Entries);
23160b57cec5SDimitry Andric if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
23170b57cec5SDimitry Andric return Result;
2318fe013be4SDimitry Andric Entries.pop_back();
23190b57cec5SDimitry Andric }
2320480093f4SDimitry Andric
23210b57cec5SDimitry Andric return make_error_code(llvm::errc::no_such_file_or_directory);
23220b57cec5SDimitry Andric }
23230b57cec5SDimitry Andric
getRedirectedFileStatus(const Twine & OriginalPath,bool UseExternalNames,Status ExternalStatus)2324349cc55cSDimitry Andric static Status getRedirectedFileStatus(const Twine &OriginalPath,
2325349cc55cSDimitry Andric bool UseExternalNames,
23260b57cec5SDimitry Andric Status ExternalStatus) {
232781ad6265SDimitry Andric // The path has been mapped by some nested VFS and exposes an external path,
232881ad6265SDimitry Andric // don't override it with the original path.
232981ad6265SDimitry Andric if (ExternalStatus.ExposesExternalVFSPath)
233081ad6265SDimitry Andric return ExternalStatus;
233181ad6265SDimitry Andric
23320b57cec5SDimitry Andric Status S = ExternalStatus;
23330b57cec5SDimitry Andric if (!UseExternalNames)
2334349cc55cSDimitry Andric S = Status::copyWithNewName(S, OriginalPath);
233581ad6265SDimitry Andric else
233681ad6265SDimitry Andric S.ExposesExternalVFSPath = true;
23370b57cec5SDimitry Andric S.IsVFSMapped = true;
23380b57cec5SDimitry Andric return S;
23390b57cec5SDimitry Andric }
23400b57cec5SDimitry Andric
status(const Twine & CanonicalPath,const Twine & OriginalPath,const RedirectingFileSystem::LookupResult & Result)2341fe6060f1SDimitry Andric ErrorOr<Status> RedirectingFileSystem::status(
2342349cc55cSDimitry Andric const Twine &CanonicalPath, const Twine &OriginalPath,
2343349cc55cSDimitry Andric const RedirectingFileSystem::LookupResult &Result) {
2344bdd1243dSDimitry Andric if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
2345349cc55cSDimitry Andric SmallString<256> CanonicalRemappedPath((*ExtRedirect).str());
2346349cc55cSDimitry Andric if (std::error_code EC = makeCanonical(CanonicalRemappedPath))
2347349cc55cSDimitry Andric return EC;
2348349cc55cSDimitry Andric
2349349cc55cSDimitry Andric ErrorOr<Status> S = ExternalFS->status(CanonicalRemappedPath);
2350fe6060f1SDimitry Andric if (!S)
23510b57cec5SDimitry Andric return S;
2352349cc55cSDimitry Andric S = Status::copyWithNewName(*S, *ExtRedirect);
2353fe6060f1SDimitry Andric auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result.E);
2354349cc55cSDimitry Andric return getRedirectedFileStatus(OriginalPath,
2355349cc55cSDimitry Andric RE->useExternalName(UseExternalNames), *S);
23560b57cec5SDimitry Andric }
2357fe6060f1SDimitry Andric
2358fe6060f1SDimitry Andric auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
2359349cc55cSDimitry Andric return Status::copyWithNewName(DE->getStatus(), CanonicalPath);
23600b57cec5SDimitry Andric }
23610b57cec5SDimitry Andric
2362349cc55cSDimitry Andric ErrorOr<Status>
getExternalStatus(const Twine & CanonicalPath,const Twine & OriginalPath) const2363349cc55cSDimitry Andric RedirectingFileSystem::getExternalStatus(const Twine &CanonicalPath,
2364349cc55cSDimitry Andric const Twine &OriginalPath) const {
236581ad6265SDimitry Andric auto Result = ExternalFS->status(CanonicalPath);
236681ad6265SDimitry Andric
236781ad6265SDimitry Andric // The path has been mapped by some nested VFS, don't override it with the
236881ad6265SDimitry Andric // original path.
236981ad6265SDimitry Andric if (!Result || Result->ExposesExternalVFSPath)
237081ad6265SDimitry Andric return Result;
237181ad6265SDimitry Andric return Status::copyWithNewName(Result.get(), OriginalPath);
2372349cc55cSDimitry Andric }
2373e8d8bef9SDimitry Andric
status(const Twine & OriginalPath)2374349cc55cSDimitry Andric ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {
2375349cc55cSDimitry Andric SmallString<256> CanonicalPath;
2376349cc55cSDimitry Andric OriginalPath.toVector(CanonicalPath);
2377349cc55cSDimitry Andric
2378349cc55cSDimitry Andric if (std::error_code EC = makeCanonical(CanonicalPath))
2379e8d8bef9SDimitry Andric return EC;
2380e8d8bef9SDimitry Andric
238181ad6265SDimitry Andric if (Redirection == RedirectKind::Fallback) {
238281ad6265SDimitry Andric // Attempt to find the original file first, only falling back to the
238381ad6265SDimitry Andric // mapped file if that fails.
238481ad6265SDimitry Andric ErrorOr<Status> S = getExternalStatus(CanonicalPath, OriginalPath);
238581ad6265SDimitry Andric if (S)
238681ad6265SDimitry Andric return S;
238781ad6265SDimitry Andric }
238881ad6265SDimitry Andric
2389349cc55cSDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result =
2390349cc55cSDimitry Andric lookupPath(CanonicalPath);
23910b57cec5SDimitry Andric if (!Result) {
239281ad6265SDimitry Andric // Was not able to map file, fallthrough to using the original path if
239381ad6265SDimitry Andric // that was the specified redirection type.
239481ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough &&
239581ad6265SDimitry Andric isFileNotFound(Result.getError()))
2396349cc55cSDimitry Andric return getExternalStatus(CanonicalPath, OriginalPath);
23970b57cec5SDimitry Andric return Result.getError();
23980b57cec5SDimitry Andric }
2399fe6060f1SDimitry Andric
2400349cc55cSDimitry Andric ErrorOr<Status> S = status(CanonicalPath, OriginalPath, *Result);
240181ad6265SDimitry Andric if (!S && Redirection == RedirectKind::Fallthrough &&
240281ad6265SDimitry Andric isFileNotFound(S.getError(), Result->E)) {
240381ad6265SDimitry Andric // Mapped the file but it wasn't found in the underlying filesystem,
240481ad6265SDimitry Andric // fallthrough to using the original path if that was the specified
240581ad6265SDimitry Andric // redirection type.
2406349cc55cSDimitry Andric return getExternalStatus(CanonicalPath, OriginalPath);
2407349cc55cSDimitry Andric }
2408349cc55cSDimitry Andric
2409fe6060f1SDimitry Andric return S;
24100b57cec5SDimitry Andric }
24110b57cec5SDimitry Andric
24120b57cec5SDimitry Andric namespace {
24130b57cec5SDimitry Andric
24140b57cec5SDimitry Andric /// Provide a file wrapper with an overriden status.
24150b57cec5SDimitry Andric class FileWithFixedStatus : public File {
24160b57cec5SDimitry Andric std::unique_ptr<File> InnerFile;
24170b57cec5SDimitry Andric Status S;
24180b57cec5SDimitry Andric
24190b57cec5SDimitry Andric public:
FileWithFixedStatus(std::unique_ptr<File> InnerFile,Status S)24200b57cec5SDimitry Andric FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
24210b57cec5SDimitry Andric : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
24220b57cec5SDimitry Andric
status()24230b57cec5SDimitry Andric ErrorOr<Status> status() override { return S; }
24240b57cec5SDimitry Andric ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
24250b57cec5SDimitry Andric
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)24260b57cec5SDimitry Andric getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
24270b57cec5SDimitry Andric bool IsVolatile) override {
24280b57cec5SDimitry Andric return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
24290b57cec5SDimitry Andric IsVolatile);
24300b57cec5SDimitry Andric }
24310b57cec5SDimitry Andric
close()24320b57cec5SDimitry Andric std::error_code close() override { return InnerFile->close(); }
2433349cc55cSDimitry Andric
setPath(const Twine & Path)2434349cc55cSDimitry Andric void setPath(const Twine &Path) override { S = S.copyWithNewName(S, Path); }
24350b57cec5SDimitry Andric };
24360b57cec5SDimitry Andric
24370b57cec5SDimitry Andric } // namespace
24380b57cec5SDimitry Andric
24390b57cec5SDimitry Andric ErrorOr<std::unique_ptr<File>>
getWithPath(ErrorOr<std::unique_ptr<File>> Result,const Twine & P)2440349cc55cSDimitry Andric File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
244181ad6265SDimitry Andric // See \c getRedirectedFileStatus - don't update path if it's exposing an
244281ad6265SDimitry Andric // external path.
244381ad6265SDimitry Andric if (!Result || (*Result)->status()->ExposesExternalVFSPath)
2444349cc55cSDimitry Andric return Result;
2445e8d8bef9SDimitry Andric
2446349cc55cSDimitry Andric ErrorOr<std::unique_ptr<File>> F = std::move(*Result);
2447349cc55cSDimitry Andric auto Name = F->get()->getName();
2448349cc55cSDimitry Andric if (Name && Name.get() != P.str())
2449349cc55cSDimitry Andric F->get()->setPath(P);
2450349cc55cSDimitry Andric return F;
2451349cc55cSDimitry Andric }
2452349cc55cSDimitry Andric
2453349cc55cSDimitry Andric ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & OriginalPath)2454349cc55cSDimitry Andric RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) {
2455349cc55cSDimitry Andric SmallString<256> CanonicalPath;
2456349cc55cSDimitry Andric OriginalPath.toVector(CanonicalPath);
2457349cc55cSDimitry Andric
2458349cc55cSDimitry Andric if (std::error_code EC = makeCanonical(CanonicalPath))
2459e8d8bef9SDimitry Andric return EC;
2460e8d8bef9SDimitry Andric
246181ad6265SDimitry Andric if (Redirection == RedirectKind::Fallback) {
246281ad6265SDimitry Andric // Attempt to find the original file first, only falling back to the
246381ad6265SDimitry Andric // mapped file if that fails.
246481ad6265SDimitry Andric auto F = File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
246581ad6265SDimitry Andric OriginalPath);
246681ad6265SDimitry Andric if (F)
246781ad6265SDimitry Andric return F;
246881ad6265SDimitry Andric }
246981ad6265SDimitry Andric
2470349cc55cSDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result =
2471349cc55cSDimitry Andric lookupPath(CanonicalPath);
2472fe6060f1SDimitry Andric if (!Result) {
247381ad6265SDimitry Andric // Was not able to map file, fallthrough to using the original path if
247481ad6265SDimitry Andric // that was the specified redirection type.
247581ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough &&
247681ad6265SDimitry Andric isFileNotFound(Result.getError()))
2477349cc55cSDimitry Andric return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2478349cc55cSDimitry Andric OriginalPath);
2479fe6060f1SDimitry Andric return Result.getError();
24800b57cec5SDimitry Andric }
24810b57cec5SDimitry Andric
2482fe6060f1SDimitry Andric if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
24830b57cec5SDimitry Andric return make_error_code(llvm::errc::invalid_argument);
24840b57cec5SDimitry Andric
2485fe6060f1SDimitry Andric StringRef ExtRedirect = *Result->getExternalRedirect();
2486349cc55cSDimitry Andric SmallString<256> CanonicalRemappedPath(ExtRedirect.str());
2487349cc55cSDimitry Andric if (std::error_code EC = makeCanonical(CanonicalRemappedPath))
2488349cc55cSDimitry Andric return EC;
2489349cc55cSDimitry Andric
2490fe6060f1SDimitry Andric auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
24910b57cec5SDimitry Andric
2492349cc55cSDimitry Andric auto ExternalFile = File::getWithPath(
2493349cc55cSDimitry Andric ExternalFS->openFileForRead(CanonicalRemappedPath), ExtRedirect);
2494fe6060f1SDimitry Andric if (!ExternalFile) {
249581ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough &&
249681ad6265SDimitry Andric isFileNotFound(ExternalFile.getError(), Result->E)) {
249781ad6265SDimitry Andric // Mapped the file but it wasn't found in the underlying filesystem,
249881ad6265SDimitry Andric // fallthrough to using the original path if that was the specified
249981ad6265SDimitry Andric // redirection type.
2500349cc55cSDimitry Andric return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2501349cc55cSDimitry Andric OriginalPath);
250281ad6265SDimitry Andric }
2503fe6060f1SDimitry Andric return ExternalFile;
2504fe6060f1SDimitry Andric }
2505fe6060f1SDimitry Andric
2506fe6060f1SDimitry Andric auto ExternalStatus = (*ExternalFile)->status();
25070b57cec5SDimitry Andric if (!ExternalStatus)
25080b57cec5SDimitry Andric return ExternalStatus.getError();
25090b57cec5SDimitry Andric
251081ad6265SDimitry Andric // Otherwise, the file was successfully remapped. Mark it as such. Also
251181ad6265SDimitry Andric // replace the underlying path if the external name is being used.
2512fe6060f1SDimitry Andric Status S = getRedirectedFileStatus(
2513349cc55cSDimitry Andric OriginalPath, RE->useExternalName(UseExternalNames), *ExternalStatus);
25140b57cec5SDimitry Andric return std::unique_ptr<File>(
2515fe6060f1SDimitry Andric std::make_unique<FileWithFixedStatus>(std::move(*ExternalFile), S));
25160b57cec5SDimitry Andric }
25170b57cec5SDimitry Andric
25180b57cec5SDimitry Andric std::error_code
getRealPath(const Twine & OriginalPath,SmallVectorImpl<char> & Output) const251981ad6265SDimitry Andric RedirectingFileSystem::getRealPath(const Twine &OriginalPath,
25200b57cec5SDimitry Andric SmallVectorImpl<char> &Output) const {
252181ad6265SDimitry Andric SmallString<256> CanonicalPath;
252281ad6265SDimitry Andric OriginalPath.toVector(CanonicalPath);
2523e8d8bef9SDimitry Andric
252481ad6265SDimitry Andric if (std::error_code EC = makeCanonical(CanonicalPath))
2525e8d8bef9SDimitry Andric return EC;
2526e8d8bef9SDimitry Andric
252781ad6265SDimitry Andric if (Redirection == RedirectKind::Fallback) {
252881ad6265SDimitry Andric // Attempt to find the original file first, only falling back to the
252981ad6265SDimitry Andric // mapped file if that fails.
253081ad6265SDimitry Andric std::error_code EC = ExternalFS->getRealPath(CanonicalPath, Output);
253181ad6265SDimitry Andric if (!EC)
253281ad6265SDimitry Andric return EC;
253381ad6265SDimitry Andric }
253481ad6265SDimitry Andric
253581ad6265SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> Result =
253681ad6265SDimitry Andric lookupPath(CanonicalPath);
25370b57cec5SDimitry Andric if (!Result) {
253881ad6265SDimitry Andric // Was not able to map file, fallthrough to using the original path if
253981ad6265SDimitry Andric // that was the specified redirection type.
254081ad6265SDimitry Andric if (Redirection == RedirectKind::Fallthrough &&
254181ad6265SDimitry Andric isFileNotFound(Result.getError()))
254281ad6265SDimitry Andric return ExternalFS->getRealPath(CanonicalPath, Output);
25430b57cec5SDimitry Andric return Result.getError();
25440b57cec5SDimitry Andric }
25450b57cec5SDimitry Andric
2546fe6060f1SDimitry Andric // If we found FileEntry or DirectoryRemapEntry, look up the mapped
2547fe6060f1SDimitry Andric // path in the external file system.
2548fe6060f1SDimitry Andric if (auto ExtRedirect = Result->getExternalRedirect()) {
2549fe6060f1SDimitry Andric auto P = ExternalFS->getRealPath(*ExtRedirect, Output);
255081ad6265SDimitry Andric if (P && Redirection == RedirectKind::Fallthrough &&
255181ad6265SDimitry Andric isFileNotFound(P, Result->E)) {
255281ad6265SDimitry Andric // Mapped the file but it wasn't found in the underlying filesystem,
255381ad6265SDimitry Andric // fallthrough to using the original path if that was the specified
255481ad6265SDimitry Andric // redirection type.
255581ad6265SDimitry Andric return ExternalFS->getRealPath(CanonicalPath, Output);
25560b57cec5SDimitry Andric }
2557fe6060f1SDimitry Andric return P;
2558fe6060f1SDimitry Andric }
2559fe6060f1SDimitry Andric
2560fe013be4SDimitry Andric // We found a DirectoryEntry, which does not have a single external contents
2561fe013be4SDimitry Andric // path. Use the canonical virtual path.
2562fe013be4SDimitry Andric if (Redirection == RedirectKind::Fallthrough) {
2563fe013be4SDimitry Andric Result->getPath(Output);
2564fe013be4SDimitry Andric return {};
2565fe013be4SDimitry Andric }
256681ad6265SDimitry Andric return llvm::errc::invalid_argument;
25670b57cec5SDimitry Andric }
25680b57cec5SDimitry Andric
2569e8d8bef9SDimitry Andric std::unique_ptr<FileSystem>
getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)25700b57cec5SDimitry Andric vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
25710b57cec5SDimitry Andric SourceMgr::DiagHandlerTy DiagHandler,
25720b57cec5SDimitry Andric StringRef YAMLFilePath, void *DiagContext,
25730b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> ExternalFS) {
25740b57cec5SDimitry Andric return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
25750b57cec5SDimitry Andric YAMLFilePath, DiagContext,
25760b57cec5SDimitry Andric std::move(ExternalFS));
25770b57cec5SDimitry Andric }
25780b57cec5SDimitry Andric
getVFSEntries(RedirectingFileSystem::Entry * SrcE,SmallVectorImpl<StringRef> & Path,SmallVectorImpl<YAMLVFSEntry> & Entries)25790b57cec5SDimitry Andric static void getVFSEntries(RedirectingFileSystem::Entry *SrcE,
25800b57cec5SDimitry Andric SmallVectorImpl<StringRef> &Path,
25810b57cec5SDimitry Andric SmallVectorImpl<YAMLVFSEntry> &Entries) {
25820b57cec5SDimitry Andric auto Kind = SrcE->getKind();
25830b57cec5SDimitry Andric if (Kind == RedirectingFileSystem::EK_Directory) {
2584fe6060f1SDimitry Andric auto *DE = dyn_cast<RedirectingFileSystem::DirectoryEntry>(SrcE);
25850b57cec5SDimitry Andric assert(DE && "Must be a directory");
25860b57cec5SDimitry Andric for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry :
25870b57cec5SDimitry Andric llvm::make_range(DE->contents_begin(), DE->contents_end())) {
25880b57cec5SDimitry Andric Path.push_back(SubEntry->getName());
25890b57cec5SDimitry Andric getVFSEntries(SubEntry.get(), Path, Entries);
25900b57cec5SDimitry Andric Path.pop_back();
25910b57cec5SDimitry Andric }
25920b57cec5SDimitry Andric return;
25930b57cec5SDimitry Andric }
25940b57cec5SDimitry Andric
2595fe6060f1SDimitry Andric if (Kind == RedirectingFileSystem::EK_DirectoryRemap) {
2596fe6060f1SDimitry Andric auto *DR = dyn_cast<RedirectingFileSystem::DirectoryRemapEntry>(SrcE);
2597fe6060f1SDimitry Andric assert(DR && "Must be a directory remap");
2598fe6060f1SDimitry Andric SmallString<128> VPath;
2599fe6060f1SDimitry Andric for (auto &Comp : Path)
2600fe6060f1SDimitry Andric llvm::sys::path::append(VPath, Comp);
2601fe6060f1SDimitry Andric Entries.push_back(
2602fe6060f1SDimitry Andric YAMLVFSEntry(VPath.c_str(), DR->getExternalContentsPath()));
2603fe6060f1SDimitry Andric return;
2604fe6060f1SDimitry Andric }
2605fe6060f1SDimitry Andric
26060b57cec5SDimitry Andric assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File");
2607fe6060f1SDimitry Andric auto *FE = dyn_cast<RedirectingFileSystem::FileEntry>(SrcE);
26080b57cec5SDimitry Andric assert(FE && "Must be a file");
26090b57cec5SDimitry Andric SmallString<128> VPath;
26100b57cec5SDimitry Andric for (auto &Comp : Path)
26110b57cec5SDimitry Andric llvm::sys::path::append(VPath, Comp);
26120b57cec5SDimitry Andric Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
26130b57cec5SDimitry Andric }
26140b57cec5SDimitry Andric
collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,StringRef YAMLFilePath,SmallVectorImpl<YAMLVFSEntry> & CollectedEntries,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)26150b57cec5SDimitry Andric void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
26160b57cec5SDimitry Andric SourceMgr::DiagHandlerTy DiagHandler,
26170b57cec5SDimitry Andric StringRef YAMLFilePath,
26180b57cec5SDimitry Andric SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
26190b57cec5SDimitry Andric void *DiagContext,
26200b57cec5SDimitry Andric IntrusiveRefCntPtr<FileSystem> ExternalFS) {
2621e8d8bef9SDimitry Andric std::unique_ptr<RedirectingFileSystem> VFS = RedirectingFileSystem::create(
26220b57cec5SDimitry Andric std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
26230b57cec5SDimitry Andric std::move(ExternalFS));
2624fe6060f1SDimitry Andric if (!VFS)
2625fe6060f1SDimitry Andric return;
2626fe6060f1SDimitry Andric ErrorOr<RedirectingFileSystem::LookupResult> RootResult =
2627fe6060f1SDimitry Andric VFS->lookupPath("/");
2628fe6060f1SDimitry Andric if (!RootResult)
26290b57cec5SDimitry Andric return;
26300b57cec5SDimitry Andric SmallVector<StringRef, 8> Components;
26310b57cec5SDimitry Andric Components.push_back("/");
2632fe6060f1SDimitry Andric getVFSEntries(RootResult->E, Components, CollectedEntries);
26330b57cec5SDimitry Andric }
26340b57cec5SDimitry Andric
getNextVirtualUniqueID()26350b57cec5SDimitry Andric UniqueID vfs::getNextVirtualUniqueID() {
26360b57cec5SDimitry Andric static std::atomic<unsigned> UID;
26370b57cec5SDimitry Andric unsigned ID = ++UID;
26380b57cec5SDimitry Andric // The following assumes that uint64_t max will never collide with a real
26390b57cec5SDimitry Andric // dev_t value from the OS.
26400b57cec5SDimitry Andric return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
26410b57cec5SDimitry Andric }
26420b57cec5SDimitry Andric
addEntry(StringRef VirtualPath,StringRef RealPath,bool IsDirectory)26435ffd83dbSDimitry Andric void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath,
26445ffd83dbSDimitry Andric bool IsDirectory) {
26450b57cec5SDimitry Andric assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
26460b57cec5SDimitry Andric assert(sys::path::is_absolute(RealPath) && "real path not absolute");
26470b57cec5SDimitry Andric assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
26485ffd83dbSDimitry Andric Mappings.emplace_back(VirtualPath, RealPath, IsDirectory);
26495ffd83dbSDimitry Andric }
26505ffd83dbSDimitry Andric
addFileMapping(StringRef VirtualPath,StringRef RealPath)26515ffd83dbSDimitry Andric void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
26525ffd83dbSDimitry Andric addEntry(VirtualPath, RealPath, /*IsDirectory=*/false);
26535ffd83dbSDimitry Andric }
26545ffd83dbSDimitry Andric
addDirectoryMapping(StringRef VirtualPath,StringRef RealPath)26555ffd83dbSDimitry Andric void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath,
26565ffd83dbSDimitry Andric StringRef RealPath) {
26575ffd83dbSDimitry Andric addEntry(VirtualPath, RealPath, /*IsDirectory=*/true);
26580b57cec5SDimitry Andric }
26590b57cec5SDimitry Andric
26600b57cec5SDimitry Andric namespace {
26610b57cec5SDimitry Andric
26620b57cec5SDimitry Andric class JSONWriter {
26630b57cec5SDimitry Andric llvm::raw_ostream &OS;
26640b57cec5SDimitry Andric SmallVector<StringRef, 16> DirStack;
26650b57cec5SDimitry Andric
getDirIndent()26660b57cec5SDimitry Andric unsigned getDirIndent() { return 4 * DirStack.size(); }
getFileIndent()26670b57cec5SDimitry Andric unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
26680b57cec5SDimitry Andric bool containedIn(StringRef Parent, StringRef Path);
26690b57cec5SDimitry Andric StringRef containedPart(StringRef Parent, StringRef Path);
26700b57cec5SDimitry Andric void startDirectory(StringRef Path);
26710b57cec5SDimitry Andric void endDirectory();
26720b57cec5SDimitry Andric void writeEntry(StringRef VPath, StringRef RPath);
26730b57cec5SDimitry Andric
26740b57cec5SDimitry Andric public:
JSONWriter(llvm::raw_ostream & OS)26750b57cec5SDimitry Andric JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
26760b57cec5SDimitry Andric
2677bdd1243dSDimitry Andric void write(ArrayRef<YAMLVFSEntry> Entries,
2678bdd1243dSDimitry Andric std::optional<bool> UseExternalNames,
2679bdd1243dSDimitry Andric std::optional<bool> IsCaseSensitive,
2680bdd1243dSDimitry Andric std::optional<bool> IsOverlayRelative, StringRef OverlayDir);
26810b57cec5SDimitry Andric };
26820b57cec5SDimitry Andric
26830b57cec5SDimitry Andric } // namespace
26840b57cec5SDimitry Andric
containedIn(StringRef Parent,StringRef Path)26850b57cec5SDimitry Andric bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
26860b57cec5SDimitry Andric using namespace llvm::sys;
26870b57cec5SDimitry Andric
26880b57cec5SDimitry Andric // Compare each path component.
26890b57cec5SDimitry Andric auto IParent = path::begin(Parent), EParent = path::end(Parent);
26900b57cec5SDimitry Andric for (auto IChild = path::begin(Path), EChild = path::end(Path);
26910b57cec5SDimitry Andric IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
26920b57cec5SDimitry Andric if (*IParent != *IChild)
26930b57cec5SDimitry Andric return false;
26940b57cec5SDimitry Andric }
26950b57cec5SDimitry Andric // Have we exhausted the parent path?
26960b57cec5SDimitry Andric return IParent == EParent;
26970b57cec5SDimitry Andric }
26980b57cec5SDimitry Andric
containedPart(StringRef Parent,StringRef Path)26990b57cec5SDimitry Andric StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
27000b57cec5SDimitry Andric assert(!Parent.empty());
27010b57cec5SDimitry Andric assert(containedIn(Parent, Path));
27020b57cec5SDimitry Andric return Path.slice(Parent.size() + 1, StringRef::npos);
27030b57cec5SDimitry Andric }
27040b57cec5SDimitry Andric
startDirectory(StringRef Path)27050b57cec5SDimitry Andric void JSONWriter::startDirectory(StringRef Path) {
27060b57cec5SDimitry Andric StringRef Name =
27070b57cec5SDimitry Andric DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
27080b57cec5SDimitry Andric DirStack.push_back(Path);
27090b57cec5SDimitry Andric unsigned Indent = getDirIndent();
27100b57cec5SDimitry Andric OS.indent(Indent) << "{\n";
27110b57cec5SDimitry Andric OS.indent(Indent + 2) << "'type': 'directory',\n";
27120b57cec5SDimitry Andric OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
27130b57cec5SDimitry Andric OS.indent(Indent + 2) << "'contents': [\n";
27140b57cec5SDimitry Andric }
27150b57cec5SDimitry Andric
endDirectory()27160b57cec5SDimitry Andric void JSONWriter::endDirectory() {
27170b57cec5SDimitry Andric unsigned Indent = getDirIndent();
27180b57cec5SDimitry Andric OS.indent(Indent + 2) << "]\n";
27190b57cec5SDimitry Andric OS.indent(Indent) << "}";
27200b57cec5SDimitry Andric
27210b57cec5SDimitry Andric DirStack.pop_back();
27220b57cec5SDimitry Andric }
27230b57cec5SDimitry Andric
writeEntry(StringRef VPath,StringRef RPath)27240b57cec5SDimitry Andric void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
27250b57cec5SDimitry Andric unsigned Indent = getFileIndent();
27260b57cec5SDimitry Andric OS.indent(Indent) << "{\n";
27270b57cec5SDimitry Andric OS.indent(Indent + 2) << "'type': 'file',\n";
27280b57cec5SDimitry Andric OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
27290b57cec5SDimitry Andric OS.indent(Indent + 2) << "'external-contents': \""
27300b57cec5SDimitry Andric << llvm::yaml::escape(RPath) << "\"\n";
27310b57cec5SDimitry Andric OS.indent(Indent) << "}";
27320b57cec5SDimitry Andric }
27330b57cec5SDimitry Andric
write(ArrayRef<YAMLVFSEntry> Entries,std::optional<bool> UseExternalNames,std::optional<bool> IsCaseSensitive,std::optional<bool> IsOverlayRelative,StringRef OverlayDir)27340b57cec5SDimitry Andric void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
2735bdd1243dSDimitry Andric std::optional<bool> UseExternalNames,
2736bdd1243dSDimitry Andric std::optional<bool> IsCaseSensitive,
2737bdd1243dSDimitry Andric std::optional<bool> IsOverlayRelative,
27380b57cec5SDimitry Andric StringRef OverlayDir) {
27390b57cec5SDimitry Andric using namespace llvm::sys;
27400b57cec5SDimitry Andric
27410b57cec5SDimitry Andric OS << "{\n"
27420b57cec5SDimitry Andric " 'version': 0,\n";
274381ad6265SDimitry Andric if (IsCaseSensitive)
2744bdd1243dSDimitry Andric OS << " 'case-sensitive': '" << (*IsCaseSensitive ? "true" : "false")
2745bdd1243dSDimitry Andric << "',\n";
274681ad6265SDimitry Andric if (UseExternalNames)
2747bdd1243dSDimitry Andric OS << " 'use-external-names': '" << (*UseExternalNames ? "true" : "false")
2748bdd1243dSDimitry Andric << "',\n";
27490b57cec5SDimitry Andric bool UseOverlayRelative = false;
275081ad6265SDimitry Andric if (IsOverlayRelative) {
2751bdd1243dSDimitry Andric UseOverlayRelative = *IsOverlayRelative;
27520b57cec5SDimitry Andric OS << " 'overlay-relative': '" << (UseOverlayRelative ? "true" : "false")
27530b57cec5SDimitry Andric << "',\n";
27540b57cec5SDimitry Andric }
27550b57cec5SDimitry Andric OS << " 'roots': [\n";
27560b57cec5SDimitry Andric
27570b57cec5SDimitry Andric if (!Entries.empty()) {
27580b57cec5SDimitry Andric const YAMLVFSEntry &Entry = Entries.front();
27595ffd83dbSDimitry Andric
27605ffd83dbSDimitry Andric startDirectory(
27615ffd83dbSDimitry Andric Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath)
27625ffd83dbSDimitry Andric );
27630b57cec5SDimitry Andric
27640b57cec5SDimitry Andric StringRef RPath = Entry.RPath;
27650b57cec5SDimitry Andric if (UseOverlayRelative) {
27660b57cec5SDimitry Andric unsigned OverlayDirLen = OverlayDir.size();
27670b57cec5SDimitry Andric assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
27680b57cec5SDimitry Andric "Overlay dir must be contained in RPath");
27690b57cec5SDimitry Andric RPath = RPath.slice(OverlayDirLen, RPath.size());
27700b57cec5SDimitry Andric }
27710b57cec5SDimitry Andric
27725ffd83dbSDimitry Andric bool IsCurrentDirEmpty = true;
27735ffd83dbSDimitry Andric if (!Entry.IsDirectory) {
27740b57cec5SDimitry Andric writeEntry(path::filename(Entry.VPath), RPath);
27755ffd83dbSDimitry Andric IsCurrentDirEmpty = false;
27765ffd83dbSDimitry Andric }
27770b57cec5SDimitry Andric
27780b57cec5SDimitry Andric for (const auto &Entry : Entries.slice(1)) {
27795ffd83dbSDimitry Andric StringRef Dir =
27805ffd83dbSDimitry Andric Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath);
27815ffd83dbSDimitry Andric if (Dir == DirStack.back()) {
27825ffd83dbSDimitry Andric if (!IsCurrentDirEmpty) {
27830b57cec5SDimitry Andric OS << ",\n";
27845ffd83dbSDimitry Andric }
27855ffd83dbSDimitry Andric } else {
27865ffd83dbSDimitry Andric bool IsDirPoppedFromStack = false;
27870b57cec5SDimitry Andric while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
27880b57cec5SDimitry Andric OS << "\n";
27890b57cec5SDimitry Andric endDirectory();
27905ffd83dbSDimitry Andric IsDirPoppedFromStack = true;
27910b57cec5SDimitry Andric }
27925ffd83dbSDimitry Andric if (IsDirPoppedFromStack || !IsCurrentDirEmpty) {
27930b57cec5SDimitry Andric OS << ",\n";
27945ffd83dbSDimitry Andric }
27950b57cec5SDimitry Andric startDirectory(Dir);
27965ffd83dbSDimitry Andric IsCurrentDirEmpty = true;
27970b57cec5SDimitry Andric }
27980b57cec5SDimitry Andric StringRef RPath = Entry.RPath;
27990b57cec5SDimitry Andric if (UseOverlayRelative) {
28000b57cec5SDimitry Andric unsigned OverlayDirLen = OverlayDir.size();
28010b57cec5SDimitry Andric assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
28020b57cec5SDimitry Andric "Overlay dir must be contained in RPath");
28030b57cec5SDimitry Andric RPath = RPath.slice(OverlayDirLen, RPath.size());
28040b57cec5SDimitry Andric }
28055ffd83dbSDimitry Andric if (!Entry.IsDirectory) {
28060b57cec5SDimitry Andric writeEntry(path::filename(Entry.VPath), RPath);
28075ffd83dbSDimitry Andric IsCurrentDirEmpty = false;
28085ffd83dbSDimitry Andric }
28090b57cec5SDimitry Andric }
28100b57cec5SDimitry Andric
28110b57cec5SDimitry Andric while (!DirStack.empty()) {
28120b57cec5SDimitry Andric OS << "\n";
28130b57cec5SDimitry Andric endDirectory();
28140b57cec5SDimitry Andric }
28150b57cec5SDimitry Andric OS << "\n";
28160b57cec5SDimitry Andric }
28170b57cec5SDimitry Andric
28180b57cec5SDimitry Andric OS << " ]\n"
28190b57cec5SDimitry Andric << "}\n";
28200b57cec5SDimitry Andric }
28210b57cec5SDimitry Andric
write(llvm::raw_ostream & OS)28220b57cec5SDimitry Andric void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
28230b57cec5SDimitry Andric llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
28240b57cec5SDimitry Andric return LHS.VPath < RHS.VPath;
28250b57cec5SDimitry Andric });
28260b57cec5SDimitry Andric
28270b57cec5SDimitry Andric JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
28280b57cec5SDimitry Andric IsOverlayRelative, OverlayDir);
28290b57cec5SDimitry Andric }
28300b57cec5SDimitry Andric
recursive_directory_iterator(FileSystem & FS_,const Twine & Path,std::error_code & EC)28310b57cec5SDimitry Andric vfs::recursive_directory_iterator::recursive_directory_iterator(
28320b57cec5SDimitry Andric FileSystem &FS_, const Twine &Path, std::error_code &EC)
28330b57cec5SDimitry Andric : FS(&FS_) {
28340b57cec5SDimitry Andric directory_iterator I = FS->dir_begin(Path, EC);
28350b57cec5SDimitry Andric if (I != directory_iterator()) {
28360b57cec5SDimitry Andric State = std::make_shared<detail::RecDirIterState>();
28370b57cec5SDimitry Andric State->Stack.push(I);
28380b57cec5SDimitry Andric }
28390b57cec5SDimitry Andric }
28400b57cec5SDimitry Andric
28410b57cec5SDimitry Andric vfs::recursive_directory_iterator &
increment(std::error_code & EC)28420b57cec5SDimitry Andric recursive_directory_iterator::increment(std::error_code &EC) {
28430b57cec5SDimitry Andric assert(FS && State && !State->Stack.empty() && "incrementing past end");
28440b57cec5SDimitry Andric assert(!State->Stack.top()->path().empty() && "non-canonical end iterator");
28450b57cec5SDimitry Andric vfs::directory_iterator End;
28460b57cec5SDimitry Andric
28470b57cec5SDimitry Andric if (State->HasNoPushRequest)
28480b57cec5SDimitry Andric State->HasNoPushRequest = false;
28490b57cec5SDimitry Andric else {
28500b57cec5SDimitry Andric if (State->Stack.top()->type() == sys::fs::file_type::directory_file) {
28510b57cec5SDimitry Andric vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC);
28520b57cec5SDimitry Andric if (I != End) {
28530b57cec5SDimitry Andric State->Stack.push(I);
28540b57cec5SDimitry Andric return *this;
28550b57cec5SDimitry Andric }
28560b57cec5SDimitry Andric }
28570b57cec5SDimitry Andric }
28580b57cec5SDimitry Andric
28590b57cec5SDimitry Andric while (!State->Stack.empty() && State->Stack.top().increment(EC) == End)
28600b57cec5SDimitry Andric State->Stack.pop();
28610b57cec5SDimitry Andric
28620b57cec5SDimitry Andric if (State->Stack.empty())
28630b57cec5SDimitry Andric State.reset(); // end iterator
28640b57cec5SDimitry Andric
28650b57cec5SDimitry Andric return *this;
28660b57cec5SDimitry Andric }
2867