Coverage Report

Created: 2017-10-03 07:32

/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/lib/Basic/VirtualFileSystem.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- VirtualFileSystem.cpp - Virtual File System Layer --------*- C++ -*-===//
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
// This file implements the VirtualFileSystem interface.
10
//===----------------------------------------------------------------------===//
11
12
#include "clang/Basic/VirtualFileSystem.h"
13
#include "clang/Basic/FileManager.h"
14
#include "llvm/ADT/DenseMap.h"
15
#include "llvm/ADT/STLExtras.h"
16
#include "llvm/ADT/StringExtras.h"
17
#include "llvm/ADT/StringSet.h"
18
#include "llvm/ADT/iterator_range.h"
19
#include "llvm/Config/llvm-config.h"
20
#include "llvm/Support/Debug.h"
21
#include "llvm/Support/Errc.h"
22
#include "llvm/Support/MemoryBuffer.h"
23
#include "llvm/Support/Path.h"
24
#include "llvm/Support/Process.h"
25
#include "llvm/Support/YAMLParser.h"
26
#include <atomic>
27
#include <memory>
28
#include <utility>
29
30
using namespace clang;
31
using namespace clang::vfs;
32
using namespace llvm;
33
using llvm::sys::fs::file_status;
34
using llvm::sys::fs::file_type;
35
using llvm::sys::fs::perms;
36
using llvm::sys::fs::UniqueID;
37
38
Status::Status(const file_status &Status)
39
    : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
40
      User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
41
0
      Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false)  {}
42
43
Status::Status(StringRef Name, UniqueID UID, sys::TimePoint<> MTime,
44
               uint32_t User, uint32_t Group, uint64_t Size, file_type Type,
45
               perms Perms)
46
    : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
47
1.46M
      Type(Type), Perms(Perms), IsVFSMapped(false) {}
48
49
390
Status Status::copyWithNewName(const Status &In, StringRef NewName) {
50
390
  return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
51
390
                In.getUser(), In.getGroup(), In.getSize(), In.getType(),
52
390
                In.getPermissions());
53
390
}
54
55
842k
Status Status::copyWithNewName(const file_status &In, StringRef NewName) {
56
842k
  return Status(NewName, In.getUniqueID(), In.getLastModificationTime(),
57
842k
                In.getUser(), In.getGroup(), In.getSize(), In.type(),
58
842k
                In.permissions());
59
842k
}
60
61
16
bool Status::equivalent(const Status &Other) const {
62
16
  assert(isStatusKnown() && Other.isStatusKnown());
63
16
  return getUniqueID() == Other.getUniqueID();
64
16
}
65
838k
bool Status::isDirectory() const {
66
838k
  return Type == file_type::directory_file;
67
838k
}
68
6
bool Status::isRegularFile() const {
69
6
  return Type == file_type::regular_file;
70
6
}
71
3
bool Status::isOther() const {
72
3
  return exists() && 
!isRegularFile()3
&&
!isDirectory()2
&&
!isSymlink()1
;
73
3
}
74
4
bool Status::isSymlink() const {
75
4
  return Type == file_type::symlink_file;
76
4
}
77
800k
bool Status::isStatusKnown() const {
78
800k
  return Type != file_type::status_error;
79
800k
}
80
55.1k
bool Status::exists() const {
81
55.1k
  return isStatusKnown() && Type != file_type::file_not_found;
82
55.1k
}
83
84
557k
File::~File() {}
85
86
75.7k
FileSystem::~FileSystem() {}
87
88
ErrorOr<std::unique_ptr<MemoryBuffer>>
89
FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
90
88.1k
                             bool RequiresNullTerminator, bool IsVolatile) {
91
88.1k
  auto F = openFileForRead(Name);
92
88.1k
  if (!F)
93
45.5k
    return F.getError();
94
42.5k
95
42.5k
  return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
96
42.5k
}
97
98
149k
std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
99
149k
  if (llvm::sys::path::is_absolute(Path))
100
27.9k
    return std::error_code();
101
121k
102
121k
  auto WorkingDir = getCurrentWorkingDirectory();
103
121k
  if (!WorkingDir)
104
0
    return WorkingDir.getError();
105
121k
106
121k
  return llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
107
121k
}
108
109
267k
bool FileSystem::exists(const Twine &Path) {
110
267k
  auto Status = status(Path);
111
55.1k
  return Status && Status->exists();
112
267k
}
113
114
#ifndef NDEBUG
115
static bool isTraversalComponent(StringRef Component) {
116
  return Component.equals("..") || Component.equals(".");
117
}
118
119
static bool pathHasTraversal(StringRef Path) {
120
  using namespace llvm::sys;
121
  for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
122
    if (isTraversalComponent(Comp))
123
      return true;
124
  return false;
125
}
126
#endif
127
128
//===-----------------------------------------------------------------------===/
129
// RealFileSystem implementation
130
//===-----------------------------------------------------------------------===/
131
132
namespace {
133
/// \brief Wrapper around a raw file descriptor.
134
class RealFile : public File {
135
  int FD;
136
  Status S;
137
  std::string RealName;
138
  friend class RealFileSystem;
139
  RealFile(int FD, StringRef NewName, StringRef NewRealPathName)
140
      : FD(FD), S(NewName, {}, {}, {}, {}, {},
141
                  llvm::sys::fs::file_type::status_error, {}),
142
533k
        RealName(NewRealPathName.str()) {
143
533k
    assert(FD >= 0 && "Invalid or inactive file descriptor");
144
533k
  }
145
146
public:
147
  ~RealFile() override;
148
  ErrorOr<Status> status() override;
149
  ErrorOr<std::string> getName() override;
150
  ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name,
151
                                                   int64_t FileSize,
152
                                                   bool RequiresNullTerminator,
153
                                                   bool IsVolatile) override;
154
  std::error_code close() override;
155
};
156
} // end anonymous namespace
157
529k
RealFile::~RealFile() { close(); }
158
159
516k
ErrorOr<Status> RealFile::status() {
160
516k
  assert(FD != -1 && "cannot stat closed file");
161
516k
  if (
!S.isStatusKnown()516k
) {
162
516k
    file_status RealStatus;
163
516k
    if (std::error_code EC = sys::fs::status(FD, RealStatus))
164
0
      return EC;
165
516k
    S = Status::copyWithNewName(RealStatus, S.getName());
166
516k
  }
167
516k
  return S;
168
516k
}
169
170
514k
ErrorOr<std::string> RealFile::getName() {
171
514k
  return RealName.empty() ? 
S.getName().str()1
:
RealName514k
;
172
514k
}
173
174
ErrorOr<std::unique_ptr<MemoryBuffer>>
175
RealFile::getBuffer(const Twine &Name, int64_t FileSize,
176
527k
                    bool RequiresNullTerminator, bool IsVolatile) {
177
527k
  assert(FD != -1 && "cannot get buffer for closed file");
178
527k
  return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
179
527k
                                   IsVolatile);
180
527k
}
181
182
529k
std::error_code RealFile::close() {
183
529k
  std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD);
184
529k
  FD = -1;
185
529k
  return EC;
186
529k
}
187
188
namespace {
189
/// \brief The file system according to your operating system.
190
class RealFileSystem : public FileSystem {
191
public:
192
  ErrorOr<Status> status(const Twine &Path) override;
193
  ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
194
  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
195
196
  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
197
  std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
198
};
199
} // end anonymous namespace
200
201
749k
ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
202
749k
  sys::fs::file_status RealStatus;
203
749k
  if (std::error_code EC = sys::fs::status(Path, RealStatus))
204
429k
    return EC;
205
319k
  return Status::copyWithNewName(RealStatus, Path.str());
206
319k
}
207
208
ErrorOr<std::unique_ptr<File>>
209
792k
RealFileSystem::openFileForRead(const Twine &Name) {
210
792k
  int FD;
211
792k
  SmallString<256> RealName;
212
792k
  if (std::error_code EC = sys::fs::openFileForRead(Name, FD, &RealName))
213
258k
    return EC;
214
533k
  return std::unique_ptr<File>(new RealFile(FD, Name.str(), RealName.str()));
215
533k
}
216
217
7.19k
llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
218
7.19k
  SmallString<256> Dir;
219
7.19k
  if (std::error_code EC = llvm::sys::fs::current_path(Dir))
220
0
    return EC;
221
7.19k
  return Dir.str().str();
222
7.19k
}
223
224
371
std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
225
371
  // FIXME: chdir is thread hostile; on the other hand, creating the same
226
371
  // behavior as chdir is complex: chdir resolves the path once, thus
227
371
  // guaranteeing that all subsequent relative path operations work
228
371
  // on the same path the original chdir resulted in. This makes a
229
371
  // difference for example on network filesystems, where symlinks might be
230
371
  // switched during runtime of the tool. Fixing this depends on having a
231
371
  // file system abstraction that allows openat() style interactions.
232
371
  return llvm::sys::fs::set_current_path(Path);
233
371
}
234
235
105k
IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
236
105k
  static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
237
105k
  return FS;
238
105k
}
239
240
namespace {
241
class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
242
  llvm::sys::fs::directory_iterator Iter;
243
public:
244
221k
  RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) {
245
221k
    if (
!EC && 221k
Iter != llvm::sys::fs::directory_iterator()1.34k
) {
246
1.33k
      llvm::sys::fs::file_status S;
247
1.33k
      EC = Iter->status(S);
248
1.33k
      CurrentEntry = Status::copyWithNewName(S, Iter->path());
249
1.33k
    }
250
221k
  }
251
252
6.21k
  std::error_code increment() override {
253
6.21k
    std::error_code EC;
254
6.21k
    Iter.increment(EC);
255
6.21k
    if (
EC6.21k
) {
256
0
      return EC;
257
6.21k
    } else 
if (6.21k
Iter == llvm::sys::fs::directory_iterator()6.21k
) {
258
1.33k
      CurrentEntry = Status();
259
6.21k
    } else {
260
4.88k
      llvm::sys::fs::file_status S;
261
4.88k
      EC = Iter->status(S);
262
4.88k
      CurrentEntry = Status::copyWithNewName(S, Iter->path());
263
4.88k
    }
264
6.21k
    return EC;
265
6.21k
  }
266
};
267
}
268
269
directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
270
221k
                                             std::error_code &EC) {
271
221k
  return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
272
221k
}
273
274
//===-----------------------------------------------------------------------===/
275
// OverlayFileSystem implementation
276
//===-----------------------------------------------------------------------===/
277
2.73k
OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
278
2.73k
  FSList.push_back(std::move(BaseFS));
279
2.73k
}
280
281
2.74k
void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
282
2.74k
  FSList.push_back(FS);
283
2.74k
  // Synchronize added file systems by duplicating the working directory from
284
2.74k
  // the first one in the list.
285
2.74k
  FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
286
2.74k
}
287
288
33.1k
ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
289
33.1k
  // FIXME: handle symlinks that cross file systems
290
84.5k
  for (iterator I = overlays_begin(), E = overlays_end(); 
I != E84.5k
;
++I51.4k
) {
291
63.2k
    ErrorOr<Status> Status = (*I)->status(Path);
292
63.2k
    if (
Status || 63.2k
Status.getError() != llvm::errc::no_such_file_or_directory51.4k
)
293
11.8k
      return Status;
294
63.2k
  }
295
21.2k
  return make_error_code(llvm::errc::no_such_file_or_directory);
296
33.1k
}
297
298
ErrorOr<std::unique_ptr<File>>
299
3.27k
OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
300
3.27k
  // FIXME: handle symlinks that cross file systems
301
3.77k
  for (iterator I = overlays_begin(), E = overlays_end(); 
I != E3.77k
;
++I499
) {
302
3.75k
    auto Result = (*I)->openFileForRead(Path);
303
3.75k
    if (
Result || 3.75k
Result.getError() != llvm::errc::no_such_file_or_directory499
)
304
3.25k
      return Result;
305
3.75k
  }
306
22
  return make_error_code(llvm::errc::no_such_file_or_directory);
307
3.27k
}
308
309
llvm::ErrorOr<std::string>
310
2.79k
OverlayFileSystem::getCurrentWorkingDirectory() const {
311
2.79k
  // All file systems are synchronized, just take the first working directory.
312
2.79k
  return FSList.front()->getCurrentWorkingDirectory();
313
2.79k
}
314
std::error_code
315
324
OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
316
324
  for (auto &FS : FSList)
317
648
    
if (std::error_code 648
EC648
= FS->setCurrentWorkingDirectory(Path))
318
0
      return EC;
319
324
  return std::error_code();
320
324
}
321
322
222k
clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
323
324
namespace {
325
class OverlayFSDirIterImpl : public clang::vfs::detail::DirIterImpl {
326
  OverlayFileSystem &Overlays;
327
  std::string Path;
328
  OverlayFileSystem::iterator CurrentFS;
329
  directory_iterator CurrentDirIter;
330
  llvm::StringSet<> SeenNames;
331
332
83
  std::error_code incrementFS() {
333
83
    assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
334
83
    ++CurrentFS;
335
125
    for (auto E = Overlays.overlays_end(); 
CurrentFS != E125
;
++CurrentFS42
) {
336
73
      std::error_code EC;
337
73
      CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
338
73
      if (
EC && 73
EC != errc::no_such_file_or_directory19
)
339
0
        return EC;
340
73
      
if (73
CurrentDirIter != directory_iterator()73
)
341
31
        break; // found
342
73
    }
343
83
    return std::error_code();
344
83
  }
345
346
173
  std::error_code incrementDirIter(bool IsFirstTime) {
347
173
    assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
348
173
           "incrementing past end");
349
173
    std::error_code EC;
350
173
    if (!IsFirstTime)
351
119
      CurrentDirIter.increment(EC);
352
173
    if (
!EC && 173
CurrentDirIter == directory_iterator()173
)
353
83
      EC = incrementFS();
354
173
    return EC;
355
173
  }
356
357
164
  std::error_code incrementImpl(bool IsFirstTime) {
358
173
    while (
true173
) {
359
173
      std::error_code EC = incrementDirIter(IsFirstTime);
360
173
      if (
EC || 173
CurrentDirIter == directory_iterator()173
) {
361
52
        CurrentEntry = Status();
362
52
        return EC;
363
52
      }
364
121
      CurrentEntry = *CurrentDirIter;
365
121
      StringRef Name = llvm::sys::path::filename(CurrentEntry.getName());
366
121
      if (SeenNames.insert(Name).second)
367
112
        return EC; // name not seen before
368
173
    }
369
0
    
llvm_unreachable0
("returned above");
370
0
  }
371
372
public:
373
  OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
374
                       std::error_code &EC)
375
54
      : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
376
54
    CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
377
54
    EC = incrementImpl(true);
378
54
  }
379
380
110
  std::error_code increment() override { return incrementImpl(false); }
381
};
382
} // end anonymous namespace
383
384
directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
385
54
                                                std::error_code &EC) {
386
54
  return directory_iterator(
387
54
      std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
388
54
}
389
390
namespace clang {
391
namespace vfs {
392
namespace detail {
393
394
enum InMemoryNodeKind { IME_File, IME_Directory };
395
396
/// The in memory file system is a tree of Nodes. Every node can either be a
397
/// file or a directory.
398
class InMemoryNode {
399
  Status Stat;
400
  InMemoryNodeKind Kind;
401
402
public:
403
  InMemoryNode(Status Stat, InMemoryNodeKind Kind)
404
87.3k
      : Stat(std::move(Stat)), Kind(Kind) {}
405
87.3k
  virtual ~InMemoryNode() {}
406
59.0k
  const Status &getStatus() const { return Stat; }
407
263k
  InMemoryNodeKind getKind() const { return Kind; }
408
  virtual std::string toString(unsigned Indent) const = 0;
409
};
410
411
namespace {
412
class InMemoryFile : public InMemoryNode {
413
  std::unique_ptr<llvm::MemoryBuffer> Buffer;
414
415
public:
416
  InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer)
417
28.4k
      : InMemoryNode(std::move(Stat), IME_File), Buffer(std::move(Buffer)) {}
418
419
28.3k
  llvm::MemoryBuffer *getBuffer() { return Buffer.get(); }
420
0
  std::string toString(unsigned Indent) const override {
421
0
    return (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
422
0
  }
423
257k
  static bool classof(const InMemoryNode *N) {
424
257k
    return N->getKind() == IME_File;
425
257k
  }
426
};
427
428
/// Adapt a InMemoryFile for VFS' File interface.
429
class InMemoryFileAdaptor : public File {
430
  InMemoryFile &Node;
431
432
public:
433
28.1k
  explicit InMemoryFileAdaptor(InMemoryFile &Node) : Node(Node) {}
434
435
5.14k
  llvm::ErrorOr<Status> status() override { return Node.getStatus(); }
436
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
437
  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
438
28.1k
            bool IsVolatile) override {
439
28.1k
    llvm::MemoryBuffer *Buf = Node.getBuffer();
440
28.1k
    return llvm::MemoryBuffer::getMemBuffer(
441
28.1k
        Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator);
442
28.1k
  }
443
0
  std::error_code close() override { return std::error_code(); }
444
};
445
} // end anonymous namespace
446
447
class InMemoryDirectory : public InMemoryNode {
448
  std::map<std::string, std::unique_ptr<InMemoryNode>> Entries;
449
450
public:
451
  InMemoryDirectory(Status Stat)
452
58.8k
      : InMemoryNode(std::move(Stat), IME_Directory) {}
453
324k
  InMemoryNode *getChild(StringRef Name) {
454
324k
    auto I = Entries.find(Name);
455
324k
    if (I != Entries.end())
456
235k
      return I->second.get();
457
88.7k
    return nullptr;
458
88.7k
  }
459
59.0k
  InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) {
460
59.0k
    return Entries.insert(make_pair(Name, std::move(Child)))
461
59.0k
        .first->second.get();
462
59.0k
  }
463
464
  typedef decltype(Entries)::const_iterator const_iterator;
465
4
  const_iterator begin() const { return Entries.begin(); }
466
4
  const_iterator end() const { return Entries.end(); }
467
468
0
  std::string toString(unsigned Indent) const override {
469
0
    std::string Result =
470
0
        (std::string(Indent, ' ') + getStatus().getName() + "\n").str();
471
0
    for (const auto &Entry : Entries) {
472
0
      Result += Entry.second->toString(Indent + 2);
473
0
    }
474
0
    return Result;
475
0
  }
476
5.64k
  static bool classof(const InMemoryNode *N) {
477
5.64k
    return N->getKind() == IME_Directory;
478
5.64k
  }
479
};
480
}
481
482
InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
483
    : Root(new detail::InMemoryDirectory(
484
          Status("", getNextVirtualUniqueID(), llvm::sys::TimePoint<>(), 0, 0,
485
                 0, llvm::sys::fs::file_type::directory_file,
486
                 llvm::sys::fs::perms::all_all))),
487
28.2k
      UseNormalizedPaths(UseNormalizedPaths) {}
488
489
28.2k
InMemoryFileSystem::~InMemoryFileSystem() {}
490
491
0
std::string InMemoryFileSystem::toString() const {
492
0
  return Root->toString(/*Indent=*/0);
493
0
}
494
495
bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
496
28.6k
                                 std::unique_ptr<llvm::MemoryBuffer> Buffer) {
497
28.6k
  SmallString<128> Path;
498
28.6k
  P.toVector(Path);
499
28.6k
500
28.6k
  // Fix up relative paths. This just prepends the current working directory.
501
28.6k
  std::error_code EC = makeAbsolute(Path);
502
28.6k
  assert(!EC);
503
28.6k
  (void)EC;
504
28.6k
505
28.6k
  if (useNormalizedPaths())
506
28.6k
    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
507
28.6k
508
28.6k
  if (Path.empty())
509
0
    return false;
510
28.6k
511
28.6k
  detail::InMemoryDirectory *Dir = Root.get();
512
28.6k
  auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
513
64.7k
  while (
true64.7k
) {
514
64.7k
    StringRef Name = *I;
515
64.7k
    detail::InMemoryNode *Node = Dir->getChild(Name);
516
64.7k
    ++I;
517
64.7k
    if (
!Node64.7k
) {
518
59.0k
      if (
I == E59.0k
) {
519
28.4k
        // End of the path, create a new file.
520
28.4k
        // FIXME: expose the status details in the interface.
521
28.4k
        Status Stat(P.str(), getNextVirtualUniqueID(),
522
28.4k
                    llvm::sys::toTimePoint(ModificationTime), 0, 0,
523
28.4k
                    Buffer->getBufferSize(),
524
28.4k
                    llvm::sys::fs::file_type::regular_file,
525
28.4k
                    llvm::sys::fs::all_all);
526
28.4k
        Dir->addChild(Name, llvm::make_unique<detail::InMemoryFile>(
527
28.4k
                                std::move(Stat), std::move(Buffer)));
528
28.4k
        return true;
529
28.4k
      }
530
30.6k
531
30.6k
      // Create a new directory. Use the path up to here.
532
30.6k
      // FIXME: expose the status details in the interface.
533
30.6k
      Status Stat(
534
30.6k
          StringRef(Path.str().begin(), Name.end() - Path.str().begin()),
535
30.6k
          getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime), 0,
536
30.6k
          0, Buffer->getBufferSize(), llvm::sys::fs::file_type::directory_file,
537
30.6k
          llvm::sys::fs::all_all);
538
30.6k
      Dir = cast<detail::InMemoryDirectory>(Dir->addChild(
539
30.6k
          Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat))));
540
30.6k
      continue;
541
30.6k
    }
542
5.64k
543
5.64k
    
if (auto *5.64k
NewDir5.64k
= dyn_cast<detail::InMemoryDirectory>(Node)) {
544
5.43k
      Dir = NewDir;
545
5.64k
    } else {
546
207
      assert(isa<detail::InMemoryFile>(Node) &&
547
207
             "Must be either file or directory!");
548
207
549
207
      // Trying to insert a directory in place of a file.
550
207
      if (I != E)
551
1
        return false;
552
206
553
206
      // Return false only if the new file is different from the existing one.
554
206
      return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() ==
555
206
             Buffer->getBuffer();
556
206
    }
557
64.7k
  }
558
28.6k
}
559
560
bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
561
265
                                      llvm::MemoryBuffer *Buffer) {
562
265
  return addFile(P, ModificationTime,
563
265
                 llvm::MemoryBuffer::getMemBuffer(
564
265
                     Buffer->getBuffer(), Buffer->getBufferIdentifier()));
565
265
}
566
567
static ErrorOr<detail::InMemoryNode *>
568
lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir,
569
111k
                   const Twine &P) {
570
111k
  SmallString<128> Path;
571
111k
  P.toVector(Path);
572
111k
573
111k
  // Fix up relative paths. This just prepends the current working directory.
574
111k
  std::error_code EC = FS.makeAbsolute(Path);
575
111k
  assert(!EC);
576
111k
  (void)EC;
577
111k
578
111k
  if (FS.useNormalizedPaths())
579
111k
    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
580
111k
581
111k
  if (Path.empty())
582
25.0k
    return Dir;
583
86.7k
584
86.7k
  auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path);
585
259k
  while (
true259k
) {
586
259k
    detail::InMemoryNode *Node = Dir->getChild(*I);
587
259k
    ++I;
588
259k
    if (!Node)
589
29.6k
      return errc::no_such_file_or_directory;
590
229k
591
229k
    // Return the file if it's at the end of the path.
592
229k
    
if (auto 229k
File229k
= dyn_cast<detail::InMemoryFile>(Node)) {
593
54.0k
      if (I == E)
594
54.0k
        return File;
595
0
      return errc::no_such_file_or_directory;
596
0
    }
597
175k
598
175k
    // Traverse directories.
599
175k
    Dir = cast<detail::InMemoryDirectory>(Node);
600
175k
    if (I == E)
601
3.06k
      return Dir;
602
259k
  }
603
111k
}
604
605
83.1k
llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
606
83.1k
  auto Node = lookupInMemoryNode(*this, Root.get(), Path);
607
83.1k
  if (Node)
608
53.9k
    return (*Node)->getStatus();
609
29.2k
  return Node.getError();
610
29.2k
}
611
612
llvm::ErrorOr<std::unique_ptr<File>>
613
28.4k
InMemoryFileSystem::openFileForRead(const Twine &Path) {
614
28.4k
  auto Node = lookupInMemoryNode(*this, Root.get(), Path);
615
28.4k
  if (!Node)
616
296
    return Node.getError();
617
28.1k
618
28.1k
  // When we have a file provide a heap-allocated wrapper for the memory buffer
619
28.1k
  // to match the ownership semantics for File.
620
28.1k
  
if (auto *28.1k
F28.1k
= dyn_cast<detail::InMemoryFile>(*Node))
621
28.1k
    return std::unique_ptr<File>(new detail::InMemoryFileAdaptor(*F));
622
1
623
1
  // FIXME: errc::not_a_file?
624
1
  return make_error_code(llvm::errc::invalid_argument);
625
1
}
626
627
namespace {
628
/// Adaptor from InMemoryDir::iterator to directory_iterator.
629
class InMemoryDirIterator : public clang::vfs::detail::DirIterImpl {
630
  detail::InMemoryDirectory::const_iterator I;
631
  detail::InMemoryDirectory::const_iterator E;
632
633
public:
634
37
  InMemoryDirIterator() {}
635
  explicit InMemoryDirIterator(detail::InMemoryDirectory &Dir)
636
4
      : I(Dir.begin()), E(Dir.end()) {
637
4
    if (I != E)
638
4
      CurrentEntry = I->second->getStatus();
639
4
  }
640
641
5
  std::error_code increment() override {
642
5
    ++I;
643
5
    // When we're at the end, make CurrentEntry invalid and DirIterImpl will do
644
5
    // the rest.
645
5
    CurrentEntry = I != E ? 
I->second->getStatus()1
:
Status()4
;
646
5
    return std::error_code();
647
5
  }
648
};
649
} // end anonymous namespace
650
651
directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
652
41
                                                 std::error_code &EC) {
653
41
  auto Node = lookupInMemoryNode(*this, Root.get(), Dir);
654
41
  if (
!Node41
) {
655
37
    EC = Node.getError();
656
37
    return directory_iterator(std::make_shared<InMemoryDirIterator>());
657
37
  }
658
4
659
4
  
if (auto *4
DirNode4
= dyn_cast<detail::InMemoryDirectory>(*Node))
660
4
    return directory_iterator(std::make_shared<InMemoryDirIterator>(*DirNode));
661
0
662
0
  EC = make_error_code(llvm::errc::not_a_directory);
663
0
  return directory_iterator(std::make_shared<InMemoryDirIterator>());
664
0
}
665
666
3.01k
std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) {
667
3.01k
  SmallString<128> Path;
668
3.01k
  P.toVector(Path);
669
3.01k
670
3.01k
  // Fix up relative paths. This just prepends the current working directory.
671
3.01k
  std::error_code EC = makeAbsolute(Path);
672
3.01k
  assert(!EC);
673
3.01k
  (void)EC;
674
3.01k
675
3.01k
  if (useNormalizedPaths())
676
3.00k
    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
677
3.01k
678
3.01k
  if (!Path.empty())
679
3.01k
    WorkingDirectory = Path.str();
680
3.01k
  return std::error_code();
681
3.01k
}
682
}
683
}
684
685
//===-----------------------------------------------------------------------===/
686
// RedirectingFileSystem implementation
687
//===-----------------------------------------------------------------------===/
688
689
namespace {
690
691
enum EntryKind {
692
  EK_Directory,
693
  EK_File
694
};
695
696
/// \brief A single file or directory in the VFS.
697
class Entry {
698
  EntryKind Kind;
699
  std::string Name;
700
701
public:
702
  virtual ~Entry();
703
2.56k
  Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
704
19.9k
  StringRef getName() const { return Name; }
705
20.8k
  EntryKind getKind() const { return Kind; }
706
};
707
708
class RedirectingDirectoryEntry : public Entry {
709
  std::vector<std::unique_ptr<Entry>> Contents;
710
  Status S;
711
712
public:
713
  RedirectingDirectoryEntry(StringRef Name,
714
                            std::vector<std::unique_ptr<Entry>> Contents,
715
                            Status S)
716
      : Entry(EK_Directory, Name), Contents(std::move(Contents)),
717
1.02k
        S(std::move(S)) {}
718
  RedirectingDirectoryEntry(StringRef Name, Status S)
719
880
      : Entry(EK_Directory, Name), S(std::move(S)) {}
720
319
  Status getStatus() { return S; }
721
1.15k
  void addContent(std::unique_ptr<Entry> Content) {
722
1.15k
    Contents.push_back(std::move(Content));
723
1.15k
  }
724
819
  Entry *getLastContent() const { return Contents.back().get(); }
725
  typedef decltype(Contents)::iterator iterator;
726
17.0k
  iterator contents_begin() { return Contents.begin(); }
727
17.0k
  iterator contents_end() { return Contents.end(); }
728
18.6k
  static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
729
};
730
731
class RedirectingFileEntry : public Entry {
732
public:
733
  enum NameKind {
734
    NK_NotSet,
735
    NK_External,
736
    NK_Virtual
737
  };
738
private:
739
  std::string ExternalContentsPath;
740
  NameKind UseName;
741
public:
742
  RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath,
743
                       NameKind UseName)
744
      : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
745
662
        UseName(UseName) {}
746
521
  StringRef getExternalContentsPath() const { return ExternalContentsPath; }
747
  /// \brief whether to use the external path as the name for this file.
748
186
  bool useExternalName(bool GlobalUseExternalName) const {
749
182
    return UseName == NK_NotSet ? GlobalUseExternalName
750
4
                                : (UseName == NK_External);
751
186
  }
752
331
  NameKind getUseName() const { return UseName; }
753
840
  static bool classof(const Entry *E) { return E->getKind() == EK_File; }
754
};
755
756
class RedirectingFileSystem;
757
758
class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
759
  std::string Dir;
760
  RedirectingFileSystem &FS;
761
  RedirectingDirectoryEntry::iterator Current, End;
762
763
public:
764
  VFSFromYamlDirIterImpl(const Twine &Path, RedirectingFileSystem &FS,
765
                         RedirectingDirectoryEntry::iterator Begin,
766
                         RedirectingDirectoryEntry::iterator End,
767
                         std::error_code &EC);
768
  std::error_code increment() override;
769
};
770
771
/// \brief A virtual file system parsed from a YAML file.
772
///
773
/// Currently, this class allows creating virtual directories and mapping
774
/// virtual file paths to existing external files, available in \c ExternalFS.
775
///
776
/// The basic structure of the parsed file is:
777
/// \verbatim
778
/// {
779
///   'version': <version number>,
780
///   <optional configuration>
781
///   'roots': [
782
///              <directory entries>
783
///            ]
784
/// }
785
/// \endverbatim
786
///
787
/// All configuration options are optional.
788
///   'case-sensitive': <boolean, default=true>
789
///   'use-external-names': <boolean, default=true>
790
///   'overlay-relative': <boolean, default=false>
791
///   'ignore-non-existent-contents': <boolean, default=true>
792
///
793
/// Virtual directories are represented as
794
/// \verbatim
795
/// {
796
///   'type': 'directory',
797
///   'name': <string>,
798
///   'contents': [ <file or directory entries> ]
799
/// }
800
/// \endverbatim
801
///
802
/// The default attributes for virtual directories are:
803
/// \verbatim
804
/// MTime = now() when created
805
/// Perms = 0777
806
/// User = Group = 0
807
/// Size = 0
808
/// UniqueID = unspecified unique value
809
/// \endverbatim
810
///
811
/// Re-mapped files are represented as
812
/// \verbatim
813
/// {
814
///   'type': 'file',
815
///   'name': <string>,
816
///   'use-external-name': <boolean> # Optional
817
///   'external-contents': <path to external file>)
818
/// }
819
/// \endverbatim
820
///
821
/// and inherit their attributes from the external contents.
822
///
823
/// In both cases, the 'name' field may contain multiple path components (e.g.
824
/// /path/to/file). However, any directory that contains more than one child
825
/// must be uniquely represented by a directory entry.
826
class RedirectingFileSystem : public vfs::FileSystem {
827
  /// The root(s) of the virtual file system.
828
  std::vector<std::unique_ptr<Entry>> Roots;
829
  /// \brief The file system to use for external references.
830
  IntrusiveRefCntPtr<FileSystem> ExternalFS;
831
  /// If IsRelativeOverlay is set, this represents the directory
832
  /// path that should be prefixed to each 'external-contents' entry
833
  /// when reading from YAML files.
834
  std::string ExternalContentsPrefixDir;
835
836
  /// @name Configuration
837
  /// @{
838
839
  /// \brief Whether to perform case-sensitive comparisons.
840
  ///
841
  /// Currently, case-insensitive matching only works correctly with ASCII.
842
  bool CaseSensitive = true;
843
844
  /// IsRelativeOverlay marks whether a IsExternalContentsPrefixDir path must
845
  /// be prefixed in every 'external-contents' when reading from YAML files.
846
  bool IsRelativeOverlay = false;
847
848
  /// \brief Whether to use to use the value of 'external-contents' for the
849
  /// names of files.  This global value is overridable on a per-file basis.
850
  bool UseExternalNames = true;
851
852
  /// \brief Whether an invalid path obtained via 'external-contents' should
853
  /// cause iteration on the VFS to stop. If 'true', the VFS should ignore
854
  /// the entry and continue with the next. Allows YAML files to be shared
855
  /// across multiple compiler invocations regardless of prior existent
856
  /// paths in 'external-contents'. This global value is overridable on a
857
  /// per-file basis.
858
  bool IgnoreNonExistentContents = true;
859
  /// @}
860
861
  /// Virtual file paths and external files could be canonicalized without "..",
862
  /// "." and "./" in their paths. FIXME: some unittests currently fail on
863
  /// win32 when using remove_dots and remove_leading_dotslash on paths.
864
  bool UseCanonicalizedPaths =
865
#ifdef LLVM_ON_WIN32
866
      false;
867
#else
868
      true;
869
#endif
870
871
  friend class RedirectingFileSystemParser;
872
873
private:
874
  RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)
875
92
      : ExternalFS(std::move(ExternalFS)) {}
876
877
  /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
878
  /// recursing into the contents of \p From if it is a directory.
879
  ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
880
                              sys::path::const_iterator End, Entry *From);
881
882
  /// \brief Get the status of a given an \c Entry.
883
  ErrorOr<Status> status(const Twine &Path, Entry *E);
884
885
public:
886
  /// \brief Looks up \p Path in \c Roots.
887
  ErrorOr<Entry *> lookupPath(const Twine &Path);
888
889
  /// \brief Parses \p Buffer, which is expected to be in YAML format and
890
  /// returns a virtual file system representing its contents.
891
  static RedirectingFileSystem *
892
  create(std::unique_ptr<MemoryBuffer> Buffer,
893
         SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath,
894
         void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS);
895
896
  ErrorOr<Status> status(const Twine &Path) override;
897
  ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
898
899
17
  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
900
17
    return ExternalFS->getCurrentWorkingDirectory();
901
17
  }
902
53
  std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
903
53
    return ExternalFS->setCurrentWorkingDirectory(Path);
904
53
  }
905
906
33
  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
907
33
    ErrorOr<Entry *> E = lookupPath(Dir);
908
33
    if (
!E33
) {
909
11
      EC = E.getError();
910
11
      return directory_iterator();
911
11
    }
912
22
    ErrorOr<Status> S = status(Dir, *E);
913
22
    if (
!S22
) {
914
0
      EC = S.getError();
915
0
      return directory_iterator();
916
0
    }
917
22
    
if (22
!S->isDirectory()22
) {
918
0
      EC = std::error_code(static_cast<int>(errc::not_a_directory),
919
0
                           std::system_category());
920
0
      return directory_iterator();
921
0
    }
922
22
923
22
    auto *D = cast<RedirectingDirectoryEntry>(*E);
924
22
    return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
925
22
        *this, D->contents_begin(), D->contents_end(), EC));
926
22
  }
927
928
53
  void setExternalContentsPrefixDir(StringRef PrefixDir) {
929
53
    ExternalContentsPrefixDir = PrefixDir.str();
930
53
  }
931
932
32
  StringRef getExternalContentsPrefixDir() const {
933
32
    return ExternalContentsPrefixDir;
934
32
  }
935
936
1
  bool ignoreNonExistentContents() const {
937
1
    return IgnoreNonExistentContents;
938
1
  }
939
940
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
941
LLVM_DUMP_METHOD void dump() const {
942
    for (const std::unique_ptr<Entry> &Root : Roots)
943
      dumpEntry(Root.get());
944
  }
945
946
LLVM_DUMP_METHOD void dumpEntry(Entry *E, int NumSpaces = 0) const {
947
    StringRef Name = E->getName();
948
    for (int i = 0, e = NumSpaces; i < e; ++i)
949
      dbgs() << " ";
950
    dbgs() << "'" << Name.str().c_str() << "'" << "\n";
951
952
    if (E->getKind() == EK_Directory) {
953
      auto *DE = dyn_cast<RedirectingDirectoryEntry>(E);
954
      assert(DE && "Should be a directory");
955
956
      for (std::unique_ptr<Entry> &SubEntry :
957
           llvm::make_range(DE->contents_begin(), DE->contents_end()))
958
        dumpEntry(SubEntry.get(), NumSpaces+2);
959
    }
960
  }
961
#endif
962
963
};
964
965
/// \brief A helper class to hold the common YAML parsing state.
966
class RedirectingFileSystemParser {
967
  yaml::Stream &Stream;
968
969
27
  void error(yaml::Node *N, const Twine &Msg) {
970
27
    Stream.printError(N, Msg);
971
27
  }
972
973
  // false on error
974
  bool parseScalarString(yaml::Node *N, StringRef &Result,
975
3.20k
                         SmallVectorImpl<char> &Storage) {
976
3.20k
    yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
977
3.20k
    if (
!S3.20k
) {
978
3
      error(N, "expected string");
979
3
      return false;
980
3
    }
981
3.20k
    Result = S->getValue(Storage);
982
3.20k
    return true;
983
3.20k
  }
984
985
  // false on error
986
53
  bool parseScalarBool(yaml::Node *N, bool &Result) {
987
53
    SmallString<5> Storage;
988
53
    StringRef Value;
989
53
    if (!parseScalarString(N, Value, Storage))
990
0
      return false;
991
53
992
53
    
if (53
Value.equals_lower("true") || 53
Value.equals_lower("on")32
||
993
53
        
Value.equals_lower("yes")32
||
Value == "1"32
) {
994
21
      Result = true;
995
21
      return true;
996
32
    } else 
if (32
Value.equals_lower("false") || 32
Value.equals_lower("off")2
||
997
32
               
Value.equals_lower("no")2
||
Value == "0"2
) {
998
30
      Result = false;
999
30
      return true;
1000
30
    }
1001
2
1002
2
    error(N, "expected boolean value");
1003
2
    return false;
1004
2
  }
1005
1006
  struct KeyStatus {
1007
3.06k
    KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
1008
    bool Required;
1009
    bool Seen;
1010
  };
1011
  typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
1012
1013
  // false on error
1014
  bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
1015
1.72k
                                  DenseMap<StringRef, KeyStatus> &Keys) {
1016
1.72k
    if (
!Keys.count(Key)1.72k
) {
1017
4
      error(KeyNode, "unknown key");
1018
4
      return false;
1019
4
    }
1020
1.72k
    KeyStatus &S = Keys[Key];
1021
1.72k
    if (
S.Seen1.72k
) {
1022
3
      error(KeyNode, Twine("duplicate key '") + Key + "'");
1023
3
      return false;
1024
3
    }
1025
1.72k
    S.Seen = true;
1026
1.72k
    return true;
1027
1.72k
  }
1028
1029
  // false on error
1030
555
  bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
1031
555
    for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
1032
555
         E = Keys.end();
1033
3.38k
         
I != E3.38k
;
++I2.82k
) {
1034
2.82k
      if (
I->second.Required && 2.82k
!I->second.Seen1.10k
) {
1035
4
        error(Obj, Twine("missing key '") + I->first + "'");
1036
4
        return false;
1037
4
      }
1038
2.82k
    }
1039
551
    return true;
1040
555
  }
1041
1042
  Entry *lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,
1043
1.01k
                             Entry *ParentEntry = nullptr) {
1044
1.01k
    if (
!ParentEntry1.01k
) { // Look for a existent root
1045
13
      for (const std::unique_ptr<Entry> &Root : FS->Roots) {
1046
13
        if (
Name.equals(Root->getName())13
) {
1047
13
          ParentEntry = Root.get();
1048
13
          return ParentEntry;
1049
13
        }
1050
1.01k
      }
1051
0
    } else { // Advance to the next component
1052
942
      auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry);
1053
942
      for (std::unique_ptr<Entry> &Content :
1054
513
           llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1055
513
        auto *DirContent = dyn_cast<RedirectingDirectoryEntry>(Content.get());
1056
513
        if (
DirContent && 513
Name.equals(Content->getName())225
)
1057
123
          return DirContent;
1058
880
      }
1059
942
    }
1060
880
1061
880
    // ... or create a new one
1062
880
    std::unique_ptr<Entry> E = llvm::make_unique<RedirectingDirectoryEntry>(
1063
880
        Name,
1064
880
        Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1065
880
               0, 0, 0, file_type::directory_file, sys::fs::all_all));
1066
880
1067
880
    if (
!ParentEntry880
) { // Add a new root to the overlay
1068
61
      FS->Roots.push_back(std::move(E));
1069
61
      ParentEntry = FS->Roots.back().get();
1070
61
      return ParentEntry;
1071
61
    }
1072
819
1073
819
    auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry);
1074
819
    DE->addContent(std::move(E));
1075
819
    return DE->getLastContent();
1076
819
  }
1077
1078
  void uniqueOverlayTree(RedirectingFileSystem *FS, Entry *SrcE,
1079
1.35k
                         Entry *NewParentE = nullptr) {
1080
1.35k
    StringRef Name = SrcE->getName();
1081
1.35k
    switch (SrcE->getKind()) {
1082
1.02k
    case EK_Directory: {
1083
1.02k
      auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE);
1084
1.02k
      assert(DE && "Must be a directory");
1085
1.02k
      // Empty directories could be present in the YAML as a way to
1086
1.02k
      // describe a file for a current directory after some of its subdir
1087
1.02k
      // is parsed. This only leads to redundant walks, ignore it.
1088
1.02k
      if (!Name.empty())
1089
1.01k
        NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
1090
1.02k
      for (std::unique_ptr<Entry> &SubEntry :
1091
1.02k
           llvm::make_range(DE->contents_begin(), DE->contents_end()))
1092
1.27k
        uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
1093
1.02k
      break;
1094
1.35k
    }
1095
331
    case EK_File: {
1096
331
      auto *FE = dyn_cast<RedirectingFileEntry>(SrcE);
1097
331
      assert(FE && "Must be a file");
1098
331
      assert(NewParentE && "Parent entry must exist");
1099
331
      auto *DE = dyn_cast<RedirectingDirectoryEntry>(NewParentE);
1100
331
      DE->addContent(llvm::make_unique<RedirectingFileEntry>(
1101
331
          Name, FE->getExternalContentsPath(), FE->getUseName()));
1102
331
      break;
1103
1.35k
    }
1104
1.35k
    }
1105
1.35k
  }
1106
1107
505
  std::unique_ptr<Entry> parseEntry(yaml::Node *N, RedirectingFileSystem *FS) {
1108
505
    yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
1109
505
    if (
!M505
) {
1110
2
      error(N, "expected mapping node for file or directory entry");
1111
2
      return nullptr;
1112
2
    }
1113
503
1114
503
    KeyStatusPair Fields[] = {
1115
503
      KeyStatusPair("name", true),
1116
503
      KeyStatusPair("type", true),
1117
503
      KeyStatusPair("contents", false),
1118
503
      KeyStatusPair("external-contents", false),
1119
503
      KeyStatusPair("use-external-name", false),
1120
503
    };
1121
503
1122
503
    DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1123
503
1124
503
    bool HasContents = false; // external or otherwise
1125
503
    std::vector<std::unique_ptr<Entry>> EntryArrayContents;
1126
503
    std::string ExternalContentsPath;
1127
503
    std::string Name;
1128
503
    auto UseExternalName = RedirectingFileEntry::NK_NotSet;
1129
503
    EntryKind Kind;
1130
503
1131
1.99k
    for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
1132
1.49k
         
++I1.49k
) {
1133
1.50k
      StringRef Key;
1134
1.50k
      // Reuse the buffer for key and value, since we don't look at key after
1135
1.50k
      // parsing value.
1136
1.50k
      SmallString<256> Buffer;
1137
1.50k
      if (!parseScalarString(I->getKey(), Key, Buffer))
1138
0
        return nullptr;
1139
1.50k
1140
1.50k
      
if (1.50k
!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys)1.50k
)
1141
2
        return nullptr;
1142
1.50k
1143
1.50k
      StringRef Value;
1144
1.50k
      if (
Key == "name"1.50k
) {
1145
500
        if (!parseScalarString(I->getValue(), Value, Buffer))
1146
1
          return nullptr;
1147
499
1148
499
        
if (499
FS->UseCanonicalizedPaths499
) {
1149
499
          SmallString<256> Path(Value);
1150
499
          // Guarantee that old YAML files containing paths with ".." and "."
1151
499
          // are properly canonicalized before read into the VFS.
1152
499
          Path = sys::path::remove_leading_dotslash(Path);
1153
499
          sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1154
499
          Name = Path.str();
1155
499
        } else {
1156
0
          Name = Value;
1157
0
        }
1158
1.50k
      } else 
if (1.00k
Key == "type"1.00k
) {
1159
499
        if (!parseScalarString(I->getValue(), Value, Buffer))
1160
0
          return nullptr;
1161
499
        
if (499
Value == "file"499
)
1162
336
          Kind = EK_File;
1163
163
        else 
if (163
Value == "directory"163
)
1164
162
          Kind = EK_Directory;
1165
1
        else {
1166
1
          error(I->getValue(), "unknown value for 'type'");
1167
1
          return nullptr;
1168
1
        }
1169
502
      } else 
if (502
Key == "contents"502
) {
1170
163
        if (
HasContents163
) {
1171
0
          error(I->getKey(),
1172
0
                "entry already has 'contents' or 'external-contents'");
1173
0
          return nullptr;
1174
0
        }
1175
163
        HasContents = true;
1176
163
        yaml::SequenceNode *Contents =
1177
163
            dyn_cast<yaml::SequenceNode>(I->getValue());
1178
163
        if (
!Contents163
) {
1179
2
          // FIXME: this is only for directories, what about files?
1180
2
          error(I->getValue(), "expected array");
1181
2
          return nullptr;
1182
2
        }
1183
161
1184
161
        for (yaml::SequenceNode::iterator I = Contents->begin(),
1185
161
                                          E = Contents->end();
1186
577
             
I != E577
;
++I416
) {
1187
417
          if (std::unique_ptr<Entry> E = parseEntry(&*I, FS))
1188
416
            EntryArrayContents.push_back(std::move(E));
1189
417
          else
1190
1
            return nullptr;
1191
417
        }
1192
502
      } else 
if (339
Key == "external-contents"339
) {
1193
335
        if (
HasContents335
) {
1194
0
          error(I->getKey(),
1195
0
                "entry already has 'contents' or 'external-contents'");
1196
0
          return nullptr;
1197
0
        }
1198
335
        HasContents = true;
1199
335
        if (!parseScalarString(I->getValue(), Value, Buffer))
1200
2
          return nullptr;
1201
333
1202
333
        SmallString<256> FullPath;
1203
333
        if (
FS->IsRelativeOverlay333
) {
1204
32
          FullPath = FS->getExternalContentsPrefixDir();
1205
32
          assert(!FullPath.empty() &&
1206
32
                 "External contents prefix directory must exist");
1207
32
          llvm::sys::path::append(FullPath, Value);
1208
333
        } else {
1209
301
          FullPath = Value;
1210
301
        }
1211
333
1212
333
        if (
FS->UseCanonicalizedPaths333
) {
1213
333
          // Guarantee that old YAML files containing paths with ".." and "."
1214
333
          // are properly canonicalized before read into the VFS.
1215
333
          FullPath = sys::path::remove_leading_dotslash(FullPath);
1216
333
          sys::path::remove_dots(FullPath, /*remove_dot_dot=*/true);
1217
333
        }
1218
335
        ExternalContentsPath = FullPath.str();
1219
339
      } else 
if (4
Key == "use-external-name"4
) {
1220
4
        bool Val;
1221
4
        if (!parseScalarBool(I->getValue(), Val))
1222
0
          return nullptr;
1223
4
        
UseExternalName = Val ? 4
RedirectingFileEntry::NK_External2
1224
2
                              : RedirectingFileEntry::NK_Virtual;
1225
0
      } else {
1226
0
        llvm_unreachable("key missing from Keys");
1227
1.00k
      }
1228
1.50k
    }
1229
503
1230
494
    
if (494
Stream.failed()494
)
1231
0
      return nullptr;
1232
494
1233
494
    // check for missing keys
1234
494
    
if (494
!HasContents494
) {
1235
1
      error(N, "missing key 'contents' or 'external-contents'");
1236
1
      return nullptr;
1237
1
    }
1238
493
    
if (493
!checkMissingKeys(N, Keys)493
)
1239
3
      return nullptr;
1240
490
1241
490
    // check invalid configuration
1242
490
    
if (490
Kind == EK_Directory &&
1243
490
        
UseExternalName != RedirectingFileEntry::NK_NotSet159
) {
1244
0
      error(N, "'use-external-name' is not supported for directories");
1245
0
      return nullptr;
1246
0
    }
1247
490
1248
490
    // Remove trailing slash(es), being careful not to remove the root path
1249
490
    StringRef Trimmed(Name);
1250
490
    size_t RootPathLen = sys::path::root_path(Trimmed).size();
1251
490
    while (Trimmed.size() > RootPathLen &&
1252
480
           sys::path::is_separator(Trimmed.back()))
1253
0
      Trimmed = Trimmed.slice(0, Trimmed.size()-1);
1254
490
    // Get the last component
1255
490
    StringRef LastComponent = sys::path::filename(Trimmed);
1256
490
1257
490
    std::unique_ptr<Entry> Result;
1258
490
    switch (Kind) {
1259
331
    case EK_File:
1260
331
      Result = llvm::make_unique<RedirectingFileEntry>(
1261
331
          LastComponent, std::move(ExternalContentsPath), UseExternalName);
1262
331
      break;
1263
159
    case EK_Directory:
1264
159
      Result = llvm::make_unique<RedirectingDirectoryEntry>(
1265
159
          LastComponent, std::move(EntryArrayContents),
1266
159
          Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1267
159
                 0, 0, 0, file_type::directory_file, sys::fs::all_all));
1268
159
      break;
1269
490
    }
1270
490
1271
490
    StringRef Parent = sys::path::parent_path(Trimmed);
1272
490
    if (Parent.empty())
1273
368
      return Result;
1274
122
1275
122
    // if 'name' contains multiple components, create implicit directory entries
1276
122
    for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
1277
122
                                     E = sys::path::rend(Parent);
1278
984
         
I != E984
;
++I862
) {
1279
862
      std::vector<std::unique_ptr<Entry>> Entries;
1280
862
      Entries.push_back(std::move(Result));
1281
862
      Result = llvm::make_unique<RedirectingDirectoryEntry>(
1282
862
          *I, std::move(Entries),
1283
862
          Status("", getNextVirtualUniqueID(), std::chrono::system_clock::now(),
1284
862
                 0, 0, 0, file_type::directory_file, sys::fs::all_all));
1285
862
    }
1286
505
    return Result;
1287
505
  }
1288
1289
public:
1290
92
  RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}
1291
1292
  // false on error
1293
92
  bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {
1294
92
    yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
1295
92
    if (
!Top92
) {
1296
0
      error(Root, "expected mapping node");
1297
0
      return false;
1298
0
    }
1299
92
1300
92
    KeyStatusPair Fields[] = {
1301
92
      KeyStatusPair("version", true),
1302
92
      KeyStatusPair("case-sensitive", false),
1303
92
      KeyStatusPair("use-external-names", false),
1304
92
      KeyStatusPair("overlay-relative", false),
1305
92
      KeyStatusPair("ignore-non-existent-contents", false),
1306
92
      KeyStatusPair("roots", true),
1307
92
    };
1308
92
1309
92
    DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1310
92
    std::vector<std::unique_ptr<Entry>> RootEntries;
1311
92
1312
92
    // Parse configuration and 'roots'
1313
291
    for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
1314
199
         
++I199
) {
1315
225
      SmallString<10> KeyBuffer;
1316
225
      StringRef Key;
1317
225
      if (!parseScalarString(I->getKey(), Key, KeyBuffer))
1318
0
        return false;
1319
225
1320
225
      
if (225
!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys)225
)
1321
5
        return false;
1322
220
1323
220
      
if (220
Key == "roots"220
) {
1324
80
        yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
1325
80
        if (
!Roots80
) {
1326
2
          error(I->getValue(), "expected array");
1327
2
          return false;
1328
2
        }
1329
78
1330
78
        for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
1331
152
             
I != E152
;
++I74
) {
1332
88
          if (std::unique_ptr<Entry> E = parseEntry(&*I, FS))
1333
74
            RootEntries.push_back(std::move(E));
1334
88
          else
1335
14
            return false;
1336
88
        }
1337
220
      } else 
if (140
Key == "version"140
) {
1338
91
        StringRef VersionString;
1339
91
        SmallString<4> Storage;
1340
91
        if (!parseScalarString(I->getValue(), VersionString, Storage))
1341
0
          return false;
1342
91
        int Version;
1343
91
        if (
VersionString.getAsInteger<int>(10, Version)91
) {
1344
1
          error(I->getValue(), "expected integer");
1345
1
          return false;
1346
1
        }
1347
90
        
if (90
Version < 090
) {
1348
1
          error(I->getValue(), "invalid version number");
1349
1
          return false;
1350
1
        }
1351
89
        
if (89
Version != 089
) {
1352
1
          error(I->getValue(), "version mismatch, expected 0");
1353
1
          return false;
1354
1
        }
1355
49
      } else 
if (49
Key == "case-sensitive"49
) {
1356
13
        if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
1357
2
          return false;
1358
36
      } else 
if (36
Key == "overlay-relative"36
) {
1359
6
        if (!parseScalarBool(I->getValue(), FS->IsRelativeOverlay))
1360
0
          return false;
1361
30
      } else 
if (30
Key == "use-external-names"30
) {
1362
16
        if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
1363
0
          return false;
1364
14
      } else 
if (14
Key == "ignore-non-existent-contents"14
) {
1365
14
        if (!parseScalarBool(I->getValue(), FS->IgnoreNonExistentContents))
1366
0
          return false;
1367
0
      } else {
1368
0
        llvm_unreachable("key missing from Keys");
1369
140
      }
1370
225
    }
1371
92
1372
66
    
if (66
Stream.failed()66
)
1373
4
      return false;
1374
62
1375
62
    
if (62
!checkMissingKeys(Top, Keys)62
)
1376
1
      return false;
1377
61
1378
61
    // Now that we sucessefully parsed the YAML file, canonicalize the internal
1379
61
    // representation to a proper directory tree so that we can search faster
1380
61
    // inside the VFS.
1381
61
    for (std::unique_ptr<Entry> &E : RootEntries)
1382
74
      uniqueOverlayTree(FS, E.get());
1383
92
1384
92
    return true;
1385
92
  }
1386
};
1387
} // end of anonymous namespace
1388
1389
2.38k
Entry::~Entry() = default;
1390
1391
RedirectingFileSystem *
1392
RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer,
1393
                              SourceMgr::DiagHandlerTy DiagHandler,
1394
                              StringRef YAMLFilePath, void *DiagContext,
1395
92
                              IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1396
92
1397
92
  SourceMgr SM;
1398
92
  yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
1399
92
1400
92
  SM.setDiagHandler(DiagHandler, DiagContext);
1401
92
  yaml::document_iterator DI = Stream.begin();
1402
92
  yaml::Node *Root = DI->getRoot();
1403
92
  if (
DI == Stream.end() || 92
!Root92
) {
1404
0
    SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
1405
0
    return nullptr;
1406
0
  }
1407
92
1408
92
  RedirectingFileSystemParser P(Stream);
1409
92
1410
92
  std::unique_ptr<RedirectingFileSystem> FS(
1411
92
      new RedirectingFileSystem(std::move(ExternalFS)));
1412
92
1413
92
  if (
!YAMLFilePath.empty()92
) {
1414
53
    // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed
1415
53
    // to each 'external-contents' path.
1416
53
    //
1417
53
    // Example:
1418
53
    //    -ivfsoverlay dummy.cache/vfs/vfs.yaml
1419
53
    // yields:
1420
53
    //  FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs
1421
53
    //
1422
53
    SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath);
1423
53
    std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir);
1424
53
    assert(!EC && "Overlay dir final path must be absolute");
1425
53
    (void)EC;
1426
53
    FS->setExternalContentsPrefixDir(OverlayAbsDir);
1427
53
  }
1428
92
1429
92
  if (!P.parse(Root, FS.get()))
1430
31
    return nullptr;
1431
61
1432
61
  return FS.release();
1433
61
}
1434
1435
1.78k
ErrorOr<Entry *> RedirectingFileSystem::lookupPath(const Twine &Path_) {
1436
1.78k
  SmallString<256> Path;
1437
1.78k
  Path_.toVector(Path);
1438
1.78k
1439
1.78k
  // Handle relative paths
1440
1.78k
  if (std::error_code EC = makeAbsolute(Path))
1441
0
    return EC;
1442
1.78k
1443
1.78k
  // Canonicalize path by removing ".", "..", "./", etc components. This is
1444
1.78k
  // a VFS request, do bot bother about symlinks in the path components
1445
1.78k
  // but canonicalize in order to perform the correct entry search.
1446
1.78k
  
if (1.78k
UseCanonicalizedPaths1.78k
) {
1447
1.78k
    Path = sys::path::remove_leading_dotslash(Path);
1448
1.78k
    sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
1449
1.78k
  }
1450
1.78k
1451
1.78k
  if (Path.empty())
1452
0
    return make_error_code(llvm::errc::invalid_argument);
1453
1.78k
1454
1.78k
  sys::path::const_iterator Start = sys::path::begin(Path);
1455
1.78k
  sys::path::const_iterator End = sys::path::end(Path);
1456
1.78k
  for (const std::unique_ptr<Entry> &Root : Roots) {
1457
1.78k
    ErrorOr<Entry *> Result = lookupPath(Start, End, Root.get());
1458
1.78k
    if (
Result || 1.78k
Result.getError() != llvm::errc::no_such_file_or_directory1.27k
)
1459
509
      return Result;
1460
1.27k
  }
1461
1.27k
  return make_error_code(llvm::errc::no_such_file_or_directory);
1462
1.27k
}
1463
1464
ErrorOr<Entry *>
1465
RedirectingFileSystem::lookupPath(sys::path::const_iterator Start,
1466
18.2k
                                  sys::path::const_iterator End, Entry *From) {
1467
18.2k
#ifndef LLVM_ON_WIN32
1468
18.2k
  assert(!isTraversalComponent(*Start) &&
1469
18.2k
         !isTraversalComponent(From->getName()) &&
1470
18.2k
         "Paths should not contain traversal components");
1471
#else
1472
  // FIXME: this is here to support windows, remove it once canonicalized
1473
  // paths become globally default.
1474
  if (Start->equals("."))
1475
    ++Start;
1476
#endif
1477
1478
18.2k
  StringRef FromName = From->getName();
1479
18.2k
1480
18.2k
  // Forward the search to the next component in case this is an empty one.
1481
18.2k
  if (
!FromName.empty()18.2k
) {
1482
18.2k
    if (
CaseSensitive ? 18.2k
!Start->equals(FromName)17.2k
1483
1.05k
                      : !Start->equals_lower(FromName))
1484
18.2k
      // failure to match
1485
2.76k
      return make_error_code(llvm::errc::no_such_file_or_directory);
1486
15.5k
1487
15.5k
    ++Start;
1488
15.5k
1489
15.5k
    if (
Start == End15.5k
) {
1490
509
      // Match!
1491
509
      return From;
1492
509
    }
1493
15.0k
  }
1494
15.0k
1495
15.0k
  auto *DE = dyn_cast<RedirectingDirectoryEntry>(From);
1496
15.0k
  if (!DE)
1497
0
    return make_error_code(llvm::errc::not_a_directory);
1498
15.0k
1499
15.0k
  for (const std::unique_ptr<Entry> &DirEntry :
1500
16.4k
       llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1501
16.4k
    ErrorOr<Entry *> Result = lookupPath(Start, End, DirEntry.get());
1502
16.4k
    if (
Result || 16.4k
Result.getError() != llvm::errc::no_such_file_or_directory11.4k
)
1503
5.06k
      return Result;
1504
9.94k
  }
1505
9.94k
  return make_error_code(llvm::errc::no_such_file_or_directory);
1506
9.94k
}
1507
1508
static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames,
1509
186
                                      Status ExternalStatus) {
1510
186
  Status S = ExternalStatus;
1511
186
  if (!UseExternalNames)
1512
71
    S = Status::copyWithNewName(S, Path.str());
1513
186
  S.IsVFSMapped = true;
1514
186
  return S;
1515
186
}
1516
1517
450
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) {
1518
450
  assert(E != nullptr);
1519
450
  if (auto *
F450
= dyn_cast<RedirectingFileEntry>(E)) {
1520
131
    ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
1521
131
    assert(!S || S->getName() == F->getExternalContentsPath());
1522
131
    if (S)
1523
129
      return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
1524
129
                                     *S);
1525
2
    return S;
1526
0
  } else { // directory
1527
319
    auto *DE = cast<RedirectingDirectoryEntry>(E);
1528
319
    return Status::copyWithNewName(DE->getStatus(), Path.str());
1529
319
  }
1530
0
}
1531
1532
1.44k
ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {
1533
1.44k
  ErrorOr<Entry *> Result = lookupPath(Path);
1534
1.44k
  if (!Result)
1535
1.01k
    return Result.getError();
1536
428
  return status(Path, *Result);
1537
428
}
1538
1539
namespace {
1540
/// Provide a file wrapper with an overriden status.
1541
class FileWithFixedStatus : public File {
1542
  std::unique_ptr<File> InnerFile;
1543
  Status S;
1544
1545
public:
1546
  FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S)
1547
57
      : InnerFile(std::move(InnerFile)), S(std::move(S)) {}
1548
1549
53
  ErrorOr<Status> status() override { return S; }
1550
  ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
1551
  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
1552
49
            bool IsVolatile) override {
1553
49
    return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator,
1554
49
                                IsVolatile);
1555
49
  }
1556
0
  std::error_code close() override { return InnerFile->close(); }
1557
};
1558
} // end anonymous namespace
1559
1560
ErrorOr<std::unique_ptr<File>>
1561
308
RedirectingFileSystem::openFileForRead(const Twine &Path) {
1562
308
  ErrorOr<Entry *> E = lookupPath(Path);
1563
308
  if (!E)
1564
251
    return E.getError();
1565
57
1566
57
  auto *F = dyn_cast<RedirectingFileEntry>(*E);
1567
57
  if (!F) // FIXME: errc::not_a_file?
1568
0
    return make_error_code(llvm::errc::invalid_argument);
1569
57
1570
57
  auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
1571
57
  if (!Result)
1572
0
    return Result;
1573
57
1574
57
  auto ExternalStatus = (*Result)->status();
1575
57
  if (!ExternalStatus)
1576
0
    return ExternalStatus.getError();
1577
57
1578
57
  // FIXME: Update the status with the name and VFSMapped.
1579
57
  Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames),
1580
57
                                     *ExternalStatus);
1581
57
  return std::unique_ptr<File>(
1582
57
      llvm::make_unique<FileWithFixedStatus>(std::move(*Result), S));
1583
57
}
1584
1585
IntrusiveRefCntPtr<FileSystem>
1586
vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1587
                    SourceMgr::DiagHandlerTy DiagHandler,
1588
                    StringRef YAMLFilePath,
1589
                    void *DiagContext,
1590
90
                    IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1591
90
  return RedirectingFileSystem::create(std::move(Buffer), DiagHandler,
1592
90
                                       YAMLFilePath, DiagContext,
1593
90
                                       std::move(ExternalFS));
1594
90
}
1595
1596
static void getVFSEntries(Entry *SrcE, SmallVectorImpl<StringRef> &Path,
1597
30
                          SmallVectorImpl<YAMLVFSEntry> &Entries) {
1598
30
  auto Kind = SrcE->getKind();
1599
30
  if (
Kind == EK_Directory30
) {
1600
28
    auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE);
1601
28
    assert(DE && "Must be a directory");
1602
28
    for (std::unique_ptr<Entry> &SubEntry :
1603
28
         llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1604
28
      Path.push_back(SubEntry->getName());
1605
28
      getVFSEntries(SubEntry.get(), Path, Entries);
1606
28
      Path.pop_back();
1607
28
    }
1608
28
    return;
1609
28
  }
1610
2
1611
30
  assert(Kind == EK_File && "Must be a EK_File");
1612
2
  auto *FE = dyn_cast<RedirectingFileEntry>(SrcE);
1613
2
  assert(FE && "Must be a file");
1614
2
  SmallString<128> VPath;
1615
2
  for (auto &Comp : Path)
1616
30
    llvm::sys::path::append(VPath, Comp);
1617
30
  Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath()));
1618
30
}
1619
1620
void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
1621
                             SourceMgr::DiagHandlerTy DiagHandler,
1622
                             StringRef YAMLFilePath,
1623
                             SmallVectorImpl<YAMLVFSEntry> &CollectedEntries,
1624
                             void *DiagContext,
1625
2
                             IntrusiveRefCntPtr<FileSystem> ExternalFS) {
1626
2
  RedirectingFileSystem *VFS = RedirectingFileSystem::create(
1627
2
      std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext,
1628
2
      std::move(ExternalFS));
1629
2
  ErrorOr<Entry *> RootE = VFS->lookupPath("/");
1630
2
  if (!RootE)
1631
0
    return;
1632
2
  SmallVector<StringRef, 8> Components;
1633
2
  Components.push_back("/");
1634
2
  getVFSEntries(*RootE, Components, CollectedEntries);
1635
2
}
1636
1637
89.2k
UniqueID vfs::getNextVirtualUniqueID() {
1638
89.2k
  static std::atomic<unsigned> UID;
1639
89.2k
  unsigned ID = ++UID;
1640
89.2k
  // The following assumes that uint64_t max will never collide with a real
1641
89.2k
  // dev_t value from the OS.
1642
89.2k
  return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
1643
89.2k
}
1644
1645
522
void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1646
522
  assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1647
522
  assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1648
522
  assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1649
522
  Mappings.emplace_back(VirtualPath, RealPath);
1650
522
}
1651
1652
namespace {
1653
class JSONWriter {
1654
  llvm::raw_ostream &OS;
1655
  SmallVector<StringRef, 16> DirStack;
1656
136
  inline unsigned getDirIndent() { return 4 * DirStack.size(); }
1657
522
  inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1658
  bool containedIn(StringRef Parent, StringRef Path);
1659
  StringRef containedPart(StringRef Parent, StringRef Path);
1660
  void startDirectory(StringRef Path);
1661
  void endDirectory();
1662
  void writeEntry(StringRef VPath, StringRef RPath);
1663
1664
public:
1665
24
  JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1666
  void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> UseExternalNames,
1667
             Optional<bool> IsCaseSensitive, Optional<bool> IsOverlayRelative,
1668
             Optional<bool> IgnoreNonExistentContents, StringRef OverlayDir);
1669
};
1670
}
1671
1672
63
bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
1673
63
  using namespace llvm::sys;
1674
63
  // Compare each path component.
1675
63
  auto IParent = path::begin(Parent), EParent = path::end(Parent);
1676
63
  for (auto IChild = path::begin(Path), EChild = path::end(Path);
1677
852
       
IParent != EParent && 852
IChild != EChild822
;
++IParent, ++IChild789
) {
1678
803
    if (*IParent != *IChild)
1679
14
      return false;
1680
803
  }
1681
63
  // Have we exhausted the parent path?
1682
49
  return IParent == EParent;
1683
63
}
1684
1685
30
StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1686
30
  assert(!Parent.empty());
1687
30
  assert(containedIn(Parent, Path));
1688
30
  return Path.slice(Parent.size() + 1, StringRef::npos);
1689
30
}
1690
1691
68
void JSONWriter::startDirectory(StringRef Path) {
1692
68
  StringRef Name =
1693
68
      DirStack.empty() ? 
Path38
:
containedPart(DirStack.back(), Path)30
;
1694
68
  DirStack.push_back(Path);
1695
68
  unsigned Indent = getDirIndent();
1696
68
  OS.indent(Indent) << "{\n";
1697
68
  OS.indent(Indent + 2) << "'type': 'directory',\n";
1698
68
  OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1699
68
  OS.indent(Indent + 2) << "'contents': [\n";
1700
68
}
1701
1702
68
void JSONWriter::endDirectory() {
1703
68
  unsigned Indent = getDirIndent();
1704
68
  OS.indent(Indent + 2) << "]\n";
1705
68
  OS.indent(Indent) << "}";
1706
68
1707
68
  DirStack.pop_back();
1708
68
}
1709
1710
522
void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1711
522
  unsigned Indent = getFileIndent();
1712
522
  OS.indent(Indent) << "{\n";
1713
522
  OS.indent(Indent + 2) << "'type': 'file',\n";
1714
522
  OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1715
522
  OS.indent(Indent + 2) << "'external-contents': \""
1716
522
                        << llvm::yaml::escape(RPath) << "\"\n";
1717
522
  OS.indent(Indent) << "}";
1718
522
}
1719
1720
void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1721
                       Optional<bool> UseExternalNames,
1722
                       Optional<bool> IsCaseSensitive,
1723
                       Optional<bool> IsOverlayRelative,
1724
                       Optional<bool> IgnoreNonExistentContents,
1725
24
                       StringRef OverlayDir) {
1726
24
  using namespace llvm::sys;
1727
24
1728
24
  OS << "{\n"
1729
24
        "  'version': 0,\n";
1730
24
  if (IsCaseSensitive.hasValue())
1731
17
    OS << "  'case-sensitive': '"
1732
17
       << (IsCaseSensitive.getValue() ? 
"true"16
:
"false"1
) << "',\n";
1733
24
  if (UseExternalNames.hasValue())
1734
16
    OS << "  'use-external-names': '"
1735
16
       << (UseExternalNames.getValue() ? 
"true"0
:
"false"16
) << "',\n";
1736
24
  bool UseOverlayRelative = false;
1737
24
  if (
IsOverlayRelative.hasValue()24
) {
1738
16
    UseOverlayRelative = IsOverlayRelative.getValue();
1739
16
    OS << "  'overlay-relative': '"
1740
16
       << (UseOverlayRelative ? 
"true"16
:
"false"0
) << "',\n";
1741
16
  }
1742
24
  if (IgnoreNonExistentContents.hasValue())
1743
16
    OS << "  'ignore-non-existent-contents': '"
1744
16
       << (IgnoreNonExistentContents.getValue() ? 
"true"0
:
"false"16
) << "',\n";
1745
24
  OS << "  'roots': [\n";
1746
24
1747
24
  if (
!Entries.empty()24
) {
1748
23
    const YAMLVFSEntry &Entry = Entries.front();
1749
23
    startDirectory(path::parent_path(Entry.VPath));
1750
23
1751
23
    StringRef RPath = Entry.RPath;
1752
23
    if (
UseOverlayRelative23
) {
1753
16
      unsigned OverlayDirLen = OverlayDir.size();
1754
16
      assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
1755
16
             "Overlay dir must be contained in RPath");
1756
16
      RPath = RPath.slice(OverlayDirLen, RPath.size());
1757
16
    }
1758
23
1759
23
    writeEntry(path::filename(Entry.VPath), RPath);
1760
23
1761
499
    for (const auto &Entry : Entries.slice(1)) {
1762
499
      StringRef Dir = path::parent_path(Entry.VPath);
1763
499
      if (Dir == DirStack.back())
1764
454
        OS << ",\n";
1765
45
      else {
1766
78
        while (
!DirStack.empty() && 78
!containedIn(DirStack.back(), Dir)63
) {
1767
33
          OS << "\n";
1768
33
          endDirectory();
1769
33
        }
1770
45
        OS << ",\n";
1771
45
        startDirectory(Dir);
1772
45
      }
1773
499
      StringRef RPath = Entry.RPath;
1774
499
      if (
UseOverlayRelative499
) {
1775
491
        unsigned OverlayDirLen = OverlayDir.size();
1776
491
        assert(RPath.substr(0, OverlayDirLen) == OverlayDir &&
1777
491
               "Overlay dir must be contained in RPath");
1778
491
        RPath = RPath.slice(OverlayDirLen, RPath.size());
1779
491
      }
1780
499
      writeEntry(path::filename(Entry.VPath), RPath);
1781
499
    }
1782
23
1783
58
    while (
!DirStack.empty()58
) {
1784
35
      OS << "\n";
1785
35
      endDirectory();
1786
35
    }
1787
23
    OS << "\n";
1788
23
  }
1789
24
1790
24
  OS << "  ]\n"
1791
24
     << "}\n";
1792
24
}
1793
1794
24
void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
1795
24
  std::sort(Mappings.begin(), Mappings.end(),
1796
3.54k
            [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
1797
3.54k
    return LHS.VPath < RHS.VPath;
1798
3.54k
  });
1799
24
1800
24
  JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive,
1801
24
                       IsOverlayRelative, IgnoreNonExistentContents,
1802
24
                       OverlayDir);
1803
24
}
1804
1805
VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(
1806
    const Twine &_Path, RedirectingFileSystem &FS,
1807
    RedirectingDirectoryEntry::iterator Begin,
1808
    RedirectingDirectoryEntry::iterator End, std::error_code &EC)
1809
22
    : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
1810
22
  while (
Current != End22
) {
1811
22
    SmallString<128> PathStr(Dir);
1812
22
    llvm::sys::path::append(PathStr, (*Current)->getName());
1813
22
    llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
1814
22
    if (
S22
) {
1815
22
      CurrentEntry = *S;
1816
22
      return;
1817
22
    }
1818
0
    // Skip entries which do not map to a reliable external content.
1819
0
    
if (0
FS.ignoreNonExistentContents() &&
1820
0
        
S.getError() == llvm::errc::no_such_file_or_directory0
) {
1821
0
      ++Current;
1822
0
      continue;
1823
0
    } else {
1824
0
      EC = S.getError();
1825
0
      break;
1826
0
    }
1827
22
  }
1828
22
}
1829
1830
40
std::error_code VFSFromYamlDirIterImpl::increment() {
1831
40
  assert(Current != End && "cannot iterate past end");
1832
41
  while (
++Current != End41
) {
1833
19
    SmallString<128> PathStr(Dir);
1834
19
    llvm::sys::path::append(PathStr, (*Current)->getName());
1835
19
    llvm::ErrorOr<vfs::Status> S = FS.status(PathStr);
1836
19
    if (
!S19
) {
1837
1
      // Skip entries which do not map to a reliable external content.
1838
1
      if (FS.ignoreNonExistentContents() &&
1839
1
          
S.getError() == llvm::errc::no_such_file_or_directory1
) {
1840
1
        continue;
1841
0
      } else {
1842
0
        return S.getError();
1843
0
      }
1844
18
    }
1845
18
    CurrentEntry = *S;
1846
18
    break;
1847
18
  }
1848
40
1849
40
  
if (40
Current == End40
)
1850
22
    CurrentEntry = Status();
1851
40
  return std::error_code();
1852
40
}
1853
1854
vfs::recursive_directory_iterator::recursive_directory_iterator(FileSystem &FS_,
1855
                                                           const Twine &Path,
1856
                                                           std::error_code &EC)
1857
265
    : FS(&FS_) {
1858
265
  directory_iterator I = FS->dir_begin(Path, EC);
1859
265
  if (
I != directory_iterator()265
) {
1860
262
    State = std::make_shared<IterState>();
1861
262
    State->push(I);
1862
262
  }
1863
265
}
1864
1865
vfs::recursive_directory_iterator &
1866
1.08k
recursive_directory_iterator::increment(std::error_code &EC) {
1867
1.08k
  assert(FS && State && !State->empty() && "incrementing past end");
1868
1.08k
  assert(State->top()->isStatusKnown() && "non-canonical end iterator");
1869
1.08k
  vfs::directory_iterator End;
1870
1.08k
  if (
State->top()->isDirectory()1.08k
) {
1871
104
    vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
1872
104
    if (
I != End104
) {
1873
100
      State->push(I);
1874
100
      return *this;
1875
100
    }
1876
988
  }
1877
988
1878
1.35k
  
while (988
!State->empty() && 1.35k
State->top().increment(EC) == End1.08k
)
1879
362
    State->pop();
1880
988
1881
988
  if (State->empty())
1882
262
    State.reset(); // end iterator
1883
1.08k
1884
1.08k
  return *this;
1885
1.08k
}