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