Bug Summary

File:lib/Support/VirtualFileSystem.cpp
Warning:line 1542, column 7
Branch condition evaluates to a garbage value

Annotated Source Code

Press '?' to see keyboard shortcuts

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