Bug Summary

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