Bug Summary

File:lib/Support/VirtualFileSystem.cpp
Warning:line 1961, column 28
Potential leak of memory pointed to by 'VFS'

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) {
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;
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))
1432 return nullptr;
1433
1434 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
1435 return nullptr;
1436
1437 StringRef Value;
1438 if (Key == "name") {
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") {
1454 if (!parseScalarString(I.getValue(), Value, Buffer))
1455 return nullptr;
1456 if (Value == "file")
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") {
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") {
1486 if (HasContents) {
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))
1493 return nullptr;
1494
1495 SmallString<256> FullPath;
1496 if (FS->IsRelativeOverlay) {
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) {
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())
1524 return nullptr;
1525
1526 // check for missing keys
1527 if (!HasContents) {
1528 error(N, "missing key 'contents' or 'external-contents'");
1529 return nullptr;
1530 }
1531 if (!checkMissingKeys(N, Keys))
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)) {
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__))
;
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) {
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))
1617 return false;
1618
1619 if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys))
1620 return false;
1621
1622 if (Key == "roots") {
1623 auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue());
1624 if (!Roots) {
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))
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)));
4
Memory is allocated
1709
1710 if (!YAMLFilePath.empty()) {
5
Assuming the condition is false
6
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()))
7
Taking false branch
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'
8
Returned allocated memory
1959 std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
1960 std::move(ExternalFS));
1961 ErrorOr<Entry *> RootE = VFS->lookupPath("/");
9
Potential leak of memory pointed to by 'VFS'
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}