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