1*0a6a1f1dSLionel Sambuc //===- VirtualFileSystem.cpp - Virtual File System Layer --------*- C++ -*-===//
2*0a6a1f1dSLionel Sambuc //
3*0a6a1f1dSLionel Sambuc // The LLVM Compiler Infrastructure
4*0a6a1f1dSLionel Sambuc //
5*0a6a1f1dSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6*0a6a1f1dSLionel Sambuc // License. See LICENSE.TXT for details.
7*0a6a1f1dSLionel Sambuc //
8*0a6a1f1dSLionel Sambuc //===----------------------------------------------------------------------===//
9*0a6a1f1dSLionel Sambuc // This file implements the VirtualFileSystem interface.
10*0a6a1f1dSLionel Sambuc //===----------------------------------------------------------------------===//
11*0a6a1f1dSLionel Sambuc
12*0a6a1f1dSLionel Sambuc #include "clang/Basic/VirtualFileSystem.h"
13*0a6a1f1dSLionel Sambuc #include "llvm/ADT/DenseMap.h"
14*0a6a1f1dSLionel Sambuc #include "llvm/ADT/STLExtras.h"
15*0a6a1f1dSLionel Sambuc #include "llvm/ADT/StringExtras.h"
16*0a6a1f1dSLionel Sambuc #include "llvm/ADT/StringSet.h"
17*0a6a1f1dSLionel Sambuc #include "llvm/ADT/iterator_range.h"
18*0a6a1f1dSLionel Sambuc #include "llvm/Support/Errc.h"
19*0a6a1f1dSLionel Sambuc #include "llvm/Support/MemoryBuffer.h"
20*0a6a1f1dSLionel Sambuc #include "llvm/Support/Path.h"
21*0a6a1f1dSLionel Sambuc #include "llvm/Support/YAMLParser.h"
22*0a6a1f1dSLionel Sambuc #if !defined(_LIBCPP_HAS_NO_THREADS) && defined(__minix)
23*0a6a1f1dSLionel Sambuc #include <atomic>
24*0a6a1f1dSLionel Sambuc #endif // !defined(_LIBCPP_HAS_NO_THREADS) && defined(__minix)
25*0a6a1f1dSLionel Sambuc #include <memory>
26*0a6a1f1dSLionel Sambuc
27*0a6a1f1dSLionel Sambuc using namespace clang;
28*0a6a1f1dSLionel Sambuc using namespace clang::vfs;
29*0a6a1f1dSLionel Sambuc using namespace llvm;
30*0a6a1f1dSLionel Sambuc using llvm::sys::fs::file_status;
31*0a6a1f1dSLionel Sambuc using llvm::sys::fs::file_type;
32*0a6a1f1dSLionel Sambuc using llvm::sys::fs::perms;
33*0a6a1f1dSLionel Sambuc using llvm::sys::fs::UniqueID;
34*0a6a1f1dSLionel Sambuc
Status(const file_status & Status)35*0a6a1f1dSLionel Sambuc Status::Status(const file_status &Status)
36*0a6a1f1dSLionel Sambuc : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
37*0a6a1f1dSLionel Sambuc User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
38*0a6a1f1dSLionel Sambuc Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false) {}
39*0a6a1f1dSLionel Sambuc
Status(StringRef Name,StringRef ExternalName,UniqueID UID,sys::TimeValue MTime,uint32_t User,uint32_t Group,uint64_t Size,file_type Type,perms Perms)40*0a6a1f1dSLionel Sambuc Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
41*0a6a1f1dSLionel Sambuc sys::TimeValue MTime, uint32_t User, uint32_t Group,
42*0a6a1f1dSLionel Sambuc uint64_t Size, file_type Type, perms Perms)
43*0a6a1f1dSLionel Sambuc : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
44*0a6a1f1dSLionel Sambuc Type(Type), Perms(Perms), IsVFSMapped(false) {}
45*0a6a1f1dSLionel Sambuc
equivalent(const Status & Other) const46*0a6a1f1dSLionel Sambuc bool Status::equivalent(const Status &Other) const {
47*0a6a1f1dSLionel Sambuc return getUniqueID() == Other.getUniqueID();
48*0a6a1f1dSLionel Sambuc }
isDirectory() const49*0a6a1f1dSLionel Sambuc bool Status::isDirectory() const {
50*0a6a1f1dSLionel Sambuc return Type == file_type::directory_file;
51*0a6a1f1dSLionel Sambuc }
isRegularFile() const52*0a6a1f1dSLionel Sambuc bool Status::isRegularFile() const {
53*0a6a1f1dSLionel Sambuc return Type == file_type::regular_file;
54*0a6a1f1dSLionel Sambuc }
isOther() const55*0a6a1f1dSLionel Sambuc bool Status::isOther() const {
56*0a6a1f1dSLionel Sambuc return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
57*0a6a1f1dSLionel Sambuc }
isSymlink() const58*0a6a1f1dSLionel Sambuc bool Status::isSymlink() const {
59*0a6a1f1dSLionel Sambuc return Type == file_type::symlink_file;
60*0a6a1f1dSLionel Sambuc }
isStatusKnown() const61*0a6a1f1dSLionel Sambuc bool Status::isStatusKnown() const {
62*0a6a1f1dSLionel Sambuc return Type != file_type::status_error;
63*0a6a1f1dSLionel Sambuc }
exists() const64*0a6a1f1dSLionel Sambuc bool Status::exists() const {
65*0a6a1f1dSLionel Sambuc return isStatusKnown() && Type != file_type::file_not_found;
66*0a6a1f1dSLionel Sambuc }
67*0a6a1f1dSLionel Sambuc
~File()68*0a6a1f1dSLionel Sambuc File::~File() {}
69*0a6a1f1dSLionel Sambuc
~FileSystem()70*0a6a1f1dSLionel Sambuc FileSystem::~FileSystem() {}
71*0a6a1f1dSLionel Sambuc
72*0a6a1f1dSLionel Sambuc ErrorOr<std::unique_ptr<MemoryBuffer>>
getBufferForFile(const llvm::Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)73*0a6a1f1dSLionel Sambuc FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,
74*0a6a1f1dSLionel Sambuc bool RequiresNullTerminator, bool IsVolatile) {
75*0a6a1f1dSLionel Sambuc auto F = openFileForRead(Name);
76*0a6a1f1dSLionel Sambuc if (!F)
77*0a6a1f1dSLionel Sambuc return F.getError();
78*0a6a1f1dSLionel Sambuc
79*0a6a1f1dSLionel Sambuc return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
80*0a6a1f1dSLionel Sambuc }
81*0a6a1f1dSLionel Sambuc
82*0a6a1f1dSLionel Sambuc //===-----------------------------------------------------------------------===/
83*0a6a1f1dSLionel Sambuc // RealFileSystem implementation
84*0a6a1f1dSLionel Sambuc //===-----------------------------------------------------------------------===/
85*0a6a1f1dSLionel Sambuc
86*0a6a1f1dSLionel Sambuc namespace {
87*0a6a1f1dSLionel Sambuc /// \brief Wrapper around a raw file descriptor.
88*0a6a1f1dSLionel Sambuc class RealFile : public File {
89*0a6a1f1dSLionel Sambuc int FD;
90*0a6a1f1dSLionel Sambuc Status S;
91*0a6a1f1dSLionel Sambuc friend class RealFileSystem;
RealFile(int FD)92*0a6a1f1dSLionel Sambuc RealFile(int FD) : FD(FD) {
93*0a6a1f1dSLionel Sambuc assert(FD >= 0 && "Invalid or inactive file descriptor");
94*0a6a1f1dSLionel Sambuc }
95*0a6a1f1dSLionel Sambuc
96*0a6a1f1dSLionel Sambuc public:
97*0a6a1f1dSLionel Sambuc ~RealFile();
98*0a6a1f1dSLionel Sambuc ErrorOr<Status> status() override;
99*0a6a1f1dSLionel Sambuc ErrorOr<std::unique_ptr<MemoryBuffer>>
100*0a6a1f1dSLionel Sambuc getBuffer(const Twine &Name, int64_t FileSize = -1,
101*0a6a1f1dSLionel Sambuc bool RequiresNullTerminator = true,
102*0a6a1f1dSLionel Sambuc bool IsVolatile = false) override;
103*0a6a1f1dSLionel Sambuc std::error_code close() override;
104*0a6a1f1dSLionel Sambuc void setName(StringRef Name) override;
105*0a6a1f1dSLionel Sambuc };
106*0a6a1f1dSLionel Sambuc } // end anonymous namespace
~RealFile()107*0a6a1f1dSLionel Sambuc RealFile::~RealFile() { close(); }
108*0a6a1f1dSLionel Sambuc
status()109*0a6a1f1dSLionel Sambuc ErrorOr<Status> RealFile::status() {
110*0a6a1f1dSLionel Sambuc assert(FD != -1 && "cannot stat closed file");
111*0a6a1f1dSLionel Sambuc if (!S.isStatusKnown()) {
112*0a6a1f1dSLionel Sambuc file_status RealStatus;
113*0a6a1f1dSLionel Sambuc if (std::error_code EC = sys::fs::status(FD, RealStatus))
114*0a6a1f1dSLionel Sambuc return EC;
115*0a6a1f1dSLionel Sambuc Status NewS(RealStatus);
116*0a6a1f1dSLionel Sambuc NewS.setName(S.getName());
117*0a6a1f1dSLionel Sambuc S = std::move(NewS);
118*0a6a1f1dSLionel Sambuc }
119*0a6a1f1dSLionel Sambuc return S;
120*0a6a1f1dSLionel Sambuc }
121*0a6a1f1dSLionel Sambuc
122*0a6a1f1dSLionel Sambuc ErrorOr<std::unique_ptr<MemoryBuffer>>
getBuffer(const Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool IsVolatile)123*0a6a1f1dSLionel Sambuc RealFile::getBuffer(const Twine &Name, int64_t FileSize,
124*0a6a1f1dSLionel Sambuc bool RequiresNullTerminator, bool IsVolatile) {
125*0a6a1f1dSLionel Sambuc assert(FD != -1 && "cannot get buffer for closed file");
126*0a6a1f1dSLionel Sambuc return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator,
127*0a6a1f1dSLionel Sambuc IsVolatile);
128*0a6a1f1dSLionel Sambuc }
129*0a6a1f1dSLionel Sambuc
130*0a6a1f1dSLionel Sambuc // FIXME: This is terrible, we need this for ::close.
131*0a6a1f1dSLionel Sambuc #if !defined(_MSC_VER) && !defined(__MINGW32__)
132*0a6a1f1dSLionel Sambuc #include <unistd.h>
133*0a6a1f1dSLionel Sambuc #include <sys/uio.h>
134*0a6a1f1dSLionel Sambuc #else
135*0a6a1f1dSLionel Sambuc #include <io.h>
136*0a6a1f1dSLionel Sambuc #ifndef S_ISFIFO
137*0a6a1f1dSLionel Sambuc #define S_ISFIFO(x) (0)
138*0a6a1f1dSLionel Sambuc #endif
139*0a6a1f1dSLionel Sambuc #endif
close()140*0a6a1f1dSLionel Sambuc std::error_code RealFile::close() {
141*0a6a1f1dSLionel Sambuc if (::close(FD))
142*0a6a1f1dSLionel Sambuc return std::error_code(errno, std::generic_category());
143*0a6a1f1dSLionel Sambuc FD = -1;
144*0a6a1f1dSLionel Sambuc return std::error_code();
145*0a6a1f1dSLionel Sambuc }
146*0a6a1f1dSLionel Sambuc
setName(StringRef Name)147*0a6a1f1dSLionel Sambuc void RealFile::setName(StringRef Name) {
148*0a6a1f1dSLionel Sambuc S.setName(Name);
149*0a6a1f1dSLionel Sambuc }
150*0a6a1f1dSLionel Sambuc
151*0a6a1f1dSLionel Sambuc namespace {
152*0a6a1f1dSLionel Sambuc /// \brief The file system according to your operating system.
153*0a6a1f1dSLionel Sambuc class RealFileSystem : public FileSystem {
154*0a6a1f1dSLionel Sambuc public:
155*0a6a1f1dSLionel Sambuc ErrorOr<Status> status(const Twine &Path) override;
156*0a6a1f1dSLionel Sambuc ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
157*0a6a1f1dSLionel Sambuc directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
158*0a6a1f1dSLionel Sambuc };
159*0a6a1f1dSLionel Sambuc } // end anonymous namespace
160*0a6a1f1dSLionel Sambuc
status(const Twine & Path)161*0a6a1f1dSLionel Sambuc ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
162*0a6a1f1dSLionel Sambuc sys::fs::file_status RealStatus;
163*0a6a1f1dSLionel Sambuc if (std::error_code EC = sys::fs::status(Path, RealStatus))
164*0a6a1f1dSLionel Sambuc return EC;
165*0a6a1f1dSLionel Sambuc Status Result(RealStatus);
166*0a6a1f1dSLionel Sambuc Result.setName(Path.str());
167*0a6a1f1dSLionel Sambuc return Result;
168*0a6a1f1dSLionel Sambuc }
169*0a6a1f1dSLionel Sambuc
170*0a6a1f1dSLionel Sambuc ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine & Name)171*0a6a1f1dSLionel Sambuc RealFileSystem::openFileForRead(const Twine &Name) {
172*0a6a1f1dSLionel Sambuc int FD;
173*0a6a1f1dSLionel Sambuc if (std::error_code EC = sys::fs::openFileForRead(Name, FD))
174*0a6a1f1dSLionel Sambuc return EC;
175*0a6a1f1dSLionel Sambuc std::unique_ptr<File> Result(new RealFile(FD));
176*0a6a1f1dSLionel Sambuc Result->setName(Name.str());
177*0a6a1f1dSLionel Sambuc return std::move(Result);
178*0a6a1f1dSLionel Sambuc }
179*0a6a1f1dSLionel Sambuc
getRealFileSystem()180*0a6a1f1dSLionel Sambuc IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
181*0a6a1f1dSLionel Sambuc static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
182*0a6a1f1dSLionel Sambuc return FS;
183*0a6a1f1dSLionel Sambuc }
184*0a6a1f1dSLionel Sambuc
185*0a6a1f1dSLionel Sambuc namespace {
186*0a6a1f1dSLionel Sambuc class RealFSDirIter : public clang::vfs::detail::DirIterImpl {
187*0a6a1f1dSLionel Sambuc std::string Path;
188*0a6a1f1dSLionel Sambuc llvm::sys::fs::directory_iterator Iter;
189*0a6a1f1dSLionel Sambuc public:
RealFSDirIter(const Twine & _Path,std::error_code & EC)190*0a6a1f1dSLionel Sambuc RealFSDirIter(const Twine &_Path, std::error_code &EC)
191*0a6a1f1dSLionel Sambuc : Path(_Path.str()), Iter(Path, EC) {
192*0a6a1f1dSLionel Sambuc if (!EC && Iter != llvm::sys::fs::directory_iterator()) {
193*0a6a1f1dSLionel Sambuc llvm::sys::fs::file_status S;
194*0a6a1f1dSLionel Sambuc EC = Iter->status(S);
195*0a6a1f1dSLionel Sambuc if (!EC) {
196*0a6a1f1dSLionel Sambuc CurrentEntry = Status(S);
197*0a6a1f1dSLionel Sambuc CurrentEntry.setName(Iter->path());
198*0a6a1f1dSLionel Sambuc }
199*0a6a1f1dSLionel Sambuc }
200*0a6a1f1dSLionel Sambuc }
201*0a6a1f1dSLionel Sambuc
increment()202*0a6a1f1dSLionel Sambuc std::error_code increment() override {
203*0a6a1f1dSLionel Sambuc std::error_code EC;
204*0a6a1f1dSLionel Sambuc Iter.increment(EC);
205*0a6a1f1dSLionel Sambuc if (EC) {
206*0a6a1f1dSLionel Sambuc return EC;
207*0a6a1f1dSLionel Sambuc } else if (Iter == llvm::sys::fs::directory_iterator()) {
208*0a6a1f1dSLionel Sambuc CurrentEntry = Status();
209*0a6a1f1dSLionel Sambuc } else {
210*0a6a1f1dSLionel Sambuc llvm::sys::fs::file_status S;
211*0a6a1f1dSLionel Sambuc EC = Iter->status(S);
212*0a6a1f1dSLionel Sambuc CurrentEntry = Status(S);
213*0a6a1f1dSLionel Sambuc CurrentEntry.setName(Iter->path());
214*0a6a1f1dSLionel Sambuc }
215*0a6a1f1dSLionel Sambuc return EC;
216*0a6a1f1dSLionel Sambuc }
217*0a6a1f1dSLionel Sambuc };
218*0a6a1f1dSLionel Sambuc }
219*0a6a1f1dSLionel Sambuc
dir_begin(const Twine & Dir,std::error_code & EC)220*0a6a1f1dSLionel Sambuc directory_iterator RealFileSystem::dir_begin(const Twine &Dir,
221*0a6a1f1dSLionel Sambuc std::error_code &EC) {
222*0a6a1f1dSLionel Sambuc return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC));
223*0a6a1f1dSLionel Sambuc }
224*0a6a1f1dSLionel Sambuc
225*0a6a1f1dSLionel Sambuc //===-----------------------------------------------------------------------===/
226*0a6a1f1dSLionel Sambuc // OverlayFileSystem implementation
227*0a6a1f1dSLionel Sambuc //===-----------------------------------------------------------------------===/
OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS)228*0a6a1f1dSLionel Sambuc OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
229*0a6a1f1dSLionel Sambuc pushOverlay(BaseFS);
230*0a6a1f1dSLionel Sambuc }
231*0a6a1f1dSLionel Sambuc
pushOverlay(IntrusiveRefCntPtr<FileSystem> FS)232*0a6a1f1dSLionel Sambuc void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
233*0a6a1f1dSLionel Sambuc FSList.push_back(FS);
234*0a6a1f1dSLionel Sambuc }
235*0a6a1f1dSLionel Sambuc
status(const Twine & Path)236*0a6a1f1dSLionel Sambuc ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
237*0a6a1f1dSLionel Sambuc // FIXME: handle symlinks that cross file systems
238*0a6a1f1dSLionel Sambuc for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
239*0a6a1f1dSLionel Sambuc ErrorOr<Status> Status = (*I)->status(Path);
240*0a6a1f1dSLionel Sambuc if (Status || Status.getError() != llvm::errc::no_such_file_or_directory)
241*0a6a1f1dSLionel Sambuc return Status;
242*0a6a1f1dSLionel Sambuc }
243*0a6a1f1dSLionel Sambuc return make_error_code(llvm::errc::no_such_file_or_directory);
244*0a6a1f1dSLionel Sambuc }
245*0a6a1f1dSLionel Sambuc
246*0a6a1f1dSLionel Sambuc ErrorOr<std::unique_ptr<File>>
openFileForRead(const llvm::Twine & Path)247*0a6a1f1dSLionel Sambuc OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {
248*0a6a1f1dSLionel Sambuc // FIXME: handle symlinks that cross file systems
249*0a6a1f1dSLionel Sambuc for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
250*0a6a1f1dSLionel Sambuc auto Result = (*I)->openFileForRead(Path);
251*0a6a1f1dSLionel Sambuc if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
252*0a6a1f1dSLionel Sambuc return Result;
253*0a6a1f1dSLionel Sambuc }
254*0a6a1f1dSLionel Sambuc return make_error_code(llvm::errc::no_such_file_or_directory);
255*0a6a1f1dSLionel Sambuc }
256*0a6a1f1dSLionel Sambuc
~DirIterImpl()257*0a6a1f1dSLionel Sambuc clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
258*0a6a1f1dSLionel Sambuc
259*0a6a1f1dSLionel Sambuc namespace {
260*0a6a1f1dSLionel Sambuc class OverlayFSDirIterImpl : public clang::vfs::detail::DirIterImpl {
261*0a6a1f1dSLionel Sambuc OverlayFileSystem &Overlays;
262*0a6a1f1dSLionel Sambuc std::string Path;
263*0a6a1f1dSLionel Sambuc OverlayFileSystem::iterator CurrentFS;
264*0a6a1f1dSLionel Sambuc directory_iterator CurrentDirIter;
265*0a6a1f1dSLionel Sambuc llvm::StringSet<> SeenNames;
266*0a6a1f1dSLionel Sambuc
incrementFS()267*0a6a1f1dSLionel Sambuc std::error_code incrementFS() {
268*0a6a1f1dSLionel Sambuc assert(CurrentFS != Overlays.overlays_end() && "incrementing past end");
269*0a6a1f1dSLionel Sambuc ++CurrentFS;
270*0a6a1f1dSLionel Sambuc for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) {
271*0a6a1f1dSLionel Sambuc std::error_code EC;
272*0a6a1f1dSLionel Sambuc CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
273*0a6a1f1dSLionel Sambuc if (EC && EC != errc::no_such_file_or_directory)
274*0a6a1f1dSLionel Sambuc return EC;
275*0a6a1f1dSLionel Sambuc if (CurrentDirIter != directory_iterator())
276*0a6a1f1dSLionel Sambuc break; // found
277*0a6a1f1dSLionel Sambuc }
278*0a6a1f1dSLionel Sambuc return std::error_code();
279*0a6a1f1dSLionel Sambuc }
280*0a6a1f1dSLionel Sambuc
incrementDirIter(bool IsFirstTime)281*0a6a1f1dSLionel Sambuc std::error_code incrementDirIter(bool IsFirstTime) {
282*0a6a1f1dSLionel Sambuc assert((IsFirstTime || CurrentDirIter != directory_iterator()) &&
283*0a6a1f1dSLionel Sambuc "incrementing past end");
284*0a6a1f1dSLionel Sambuc std::error_code EC;
285*0a6a1f1dSLionel Sambuc if (!IsFirstTime)
286*0a6a1f1dSLionel Sambuc CurrentDirIter.increment(EC);
287*0a6a1f1dSLionel Sambuc if (!EC && CurrentDirIter == directory_iterator())
288*0a6a1f1dSLionel Sambuc EC = incrementFS();
289*0a6a1f1dSLionel Sambuc return EC;
290*0a6a1f1dSLionel Sambuc }
291*0a6a1f1dSLionel Sambuc
incrementImpl(bool IsFirstTime)292*0a6a1f1dSLionel Sambuc std::error_code incrementImpl(bool IsFirstTime) {
293*0a6a1f1dSLionel Sambuc while (true) {
294*0a6a1f1dSLionel Sambuc std::error_code EC = incrementDirIter(IsFirstTime);
295*0a6a1f1dSLionel Sambuc if (EC || CurrentDirIter == directory_iterator()) {
296*0a6a1f1dSLionel Sambuc CurrentEntry = Status();
297*0a6a1f1dSLionel Sambuc return EC;
298*0a6a1f1dSLionel Sambuc }
299*0a6a1f1dSLionel Sambuc CurrentEntry = *CurrentDirIter;
300*0a6a1f1dSLionel Sambuc StringRef Name = llvm::sys::path::filename(CurrentEntry.getName());
301*0a6a1f1dSLionel Sambuc if (SeenNames.insert(Name).second)
302*0a6a1f1dSLionel Sambuc return EC; // name not seen before
303*0a6a1f1dSLionel Sambuc }
304*0a6a1f1dSLionel Sambuc llvm_unreachable("returned above");
305*0a6a1f1dSLionel Sambuc }
306*0a6a1f1dSLionel Sambuc
307*0a6a1f1dSLionel Sambuc public:
OverlayFSDirIterImpl(const Twine & Path,OverlayFileSystem & FS,std::error_code & EC)308*0a6a1f1dSLionel Sambuc OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS,
309*0a6a1f1dSLionel Sambuc std::error_code &EC)
310*0a6a1f1dSLionel Sambuc : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) {
311*0a6a1f1dSLionel Sambuc CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC);
312*0a6a1f1dSLionel Sambuc EC = incrementImpl(true);
313*0a6a1f1dSLionel Sambuc }
314*0a6a1f1dSLionel Sambuc
increment()315*0a6a1f1dSLionel Sambuc std::error_code increment() override { return incrementImpl(false); }
316*0a6a1f1dSLionel Sambuc };
317*0a6a1f1dSLionel Sambuc } // end anonymous namespace
318*0a6a1f1dSLionel Sambuc
dir_begin(const Twine & Dir,std::error_code & EC)319*0a6a1f1dSLionel Sambuc directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir,
320*0a6a1f1dSLionel Sambuc std::error_code &EC) {
321*0a6a1f1dSLionel Sambuc return directory_iterator(
322*0a6a1f1dSLionel Sambuc std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
323*0a6a1f1dSLionel Sambuc }
324*0a6a1f1dSLionel Sambuc
325*0a6a1f1dSLionel Sambuc //===-----------------------------------------------------------------------===/
326*0a6a1f1dSLionel Sambuc // VFSFromYAML implementation
327*0a6a1f1dSLionel Sambuc //===-----------------------------------------------------------------------===/
328*0a6a1f1dSLionel Sambuc
329*0a6a1f1dSLionel Sambuc // Allow DenseMap<StringRef, ...>. This is useful below because we know all the
330*0a6a1f1dSLionel Sambuc // strings are literals and will outlive the map, and there is no reason to
331*0a6a1f1dSLionel Sambuc // store them.
332*0a6a1f1dSLionel Sambuc namespace llvm {
333*0a6a1f1dSLionel Sambuc template<>
334*0a6a1f1dSLionel Sambuc struct DenseMapInfo<StringRef> {
335*0a6a1f1dSLionel Sambuc // This assumes that "" will never be a valid key.
getEmptyKeyllvm::DenseMapInfo336*0a6a1f1dSLionel Sambuc static inline StringRef getEmptyKey() { return StringRef(""); }
getTombstoneKeyllvm::DenseMapInfo337*0a6a1f1dSLionel Sambuc static inline StringRef getTombstoneKey() { return StringRef(); }
getHashValuellvm::DenseMapInfo338*0a6a1f1dSLionel Sambuc static unsigned getHashValue(StringRef Val) { return HashString(Val); }
isEqualllvm::DenseMapInfo339*0a6a1f1dSLionel Sambuc static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
340*0a6a1f1dSLionel Sambuc };
341*0a6a1f1dSLionel Sambuc }
342*0a6a1f1dSLionel Sambuc
343*0a6a1f1dSLionel Sambuc namespace {
344*0a6a1f1dSLionel Sambuc
345*0a6a1f1dSLionel Sambuc enum EntryKind {
346*0a6a1f1dSLionel Sambuc EK_Directory,
347*0a6a1f1dSLionel Sambuc EK_File
348*0a6a1f1dSLionel Sambuc };
349*0a6a1f1dSLionel Sambuc
350*0a6a1f1dSLionel Sambuc /// \brief A single file or directory in the VFS.
351*0a6a1f1dSLionel Sambuc class Entry {
352*0a6a1f1dSLionel Sambuc EntryKind Kind;
353*0a6a1f1dSLionel Sambuc std::string Name;
354*0a6a1f1dSLionel Sambuc
355*0a6a1f1dSLionel Sambuc public:
356*0a6a1f1dSLionel Sambuc virtual ~Entry();
Entry(EntryKind K,StringRef Name)357*0a6a1f1dSLionel Sambuc Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
getName() const358*0a6a1f1dSLionel Sambuc StringRef getName() const { return Name; }
getKind() const359*0a6a1f1dSLionel Sambuc EntryKind getKind() const { return Kind; }
360*0a6a1f1dSLionel Sambuc };
361*0a6a1f1dSLionel Sambuc
362*0a6a1f1dSLionel Sambuc class DirectoryEntry : public Entry {
363*0a6a1f1dSLionel Sambuc std::vector<Entry *> Contents;
364*0a6a1f1dSLionel Sambuc Status S;
365*0a6a1f1dSLionel Sambuc
366*0a6a1f1dSLionel Sambuc public:
367*0a6a1f1dSLionel Sambuc virtual ~DirectoryEntry();
DirectoryEntry(StringRef Name,std::vector<Entry * > Contents,Status S)368*0a6a1f1dSLionel Sambuc DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
369*0a6a1f1dSLionel Sambuc : Entry(EK_Directory, Name), Contents(std::move(Contents)),
370*0a6a1f1dSLionel Sambuc S(std::move(S)) {}
getStatus()371*0a6a1f1dSLionel Sambuc Status getStatus() { return S; }
372*0a6a1f1dSLionel Sambuc typedef std::vector<Entry *>::iterator iterator;
contents_begin()373*0a6a1f1dSLionel Sambuc iterator contents_begin() { return Contents.begin(); }
contents_end()374*0a6a1f1dSLionel Sambuc iterator contents_end() { return Contents.end(); }
classof(const Entry * E)375*0a6a1f1dSLionel Sambuc static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
376*0a6a1f1dSLionel Sambuc };
377*0a6a1f1dSLionel Sambuc
378*0a6a1f1dSLionel Sambuc class FileEntry : public Entry {
379*0a6a1f1dSLionel Sambuc public:
380*0a6a1f1dSLionel Sambuc enum NameKind {
381*0a6a1f1dSLionel Sambuc NK_NotSet,
382*0a6a1f1dSLionel Sambuc NK_External,
383*0a6a1f1dSLionel Sambuc NK_Virtual
384*0a6a1f1dSLionel Sambuc };
385*0a6a1f1dSLionel Sambuc private:
386*0a6a1f1dSLionel Sambuc std::string ExternalContentsPath;
387*0a6a1f1dSLionel Sambuc NameKind UseName;
388*0a6a1f1dSLionel Sambuc public:
FileEntry(StringRef Name,StringRef ExternalContentsPath,NameKind UseName)389*0a6a1f1dSLionel Sambuc FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
390*0a6a1f1dSLionel Sambuc : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
391*0a6a1f1dSLionel Sambuc UseName(UseName) {}
getExternalContentsPath() const392*0a6a1f1dSLionel Sambuc StringRef getExternalContentsPath() const { return ExternalContentsPath; }
393*0a6a1f1dSLionel Sambuc /// \brief whether to use the external path as the name for this file.
useExternalName(bool GlobalUseExternalName) const394*0a6a1f1dSLionel Sambuc bool useExternalName(bool GlobalUseExternalName) const {
395*0a6a1f1dSLionel Sambuc return UseName == NK_NotSet ? GlobalUseExternalName
396*0a6a1f1dSLionel Sambuc : (UseName == NK_External);
397*0a6a1f1dSLionel Sambuc }
classof(const Entry * E)398*0a6a1f1dSLionel Sambuc static bool classof(const Entry *E) { return E->getKind() == EK_File; }
399*0a6a1f1dSLionel Sambuc };
400*0a6a1f1dSLionel Sambuc
401*0a6a1f1dSLionel Sambuc class VFSFromYAML;
402*0a6a1f1dSLionel Sambuc
403*0a6a1f1dSLionel Sambuc class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {
404*0a6a1f1dSLionel Sambuc std::string Dir;
405*0a6a1f1dSLionel Sambuc VFSFromYAML &FS;
406*0a6a1f1dSLionel Sambuc DirectoryEntry::iterator Current, End;
407*0a6a1f1dSLionel Sambuc public:
408*0a6a1f1dSLionel Sambuc VFSFromYamlDirIterImpl(const Twine &Path, VFSFromYAML &FS,
409*0a6a1f1dSLionel Sambuc DirectoryEntry::iterator Begin,
410*0a6a1f1dSLionel Sambuc DirectoryEntry::iterator End, std::error_code &EC);
411*0a6a1f1dSLionel Sambuc std::error_code increment() override;
412*0a6a1f1dSLionel Sambuc };
413*0a6a1f1dSLionel Sambuc
414*0a6a1f1dSLionel Sambuc /// \brief A virtual file system parsed from a YAML file.
415*0a6a1f1dSLionel Sambuc ///
416*0a6a1f1dSLionel Sambuc /// Currently, this class allows creating virtual directories and mapping
417*0a6a1f1dSLionel Sambuc /// virtual file paths to existing external files, available in \c ExternalFS.
418*0a6a1f1dSLionel Sambuc ///
419*0a6a1f1dSLionel Sambuc /// The basic structure of the parsed file is:
420*0a6a1f1dSLionel Sambuc /// \verbatim
421*0a6a1f1dSLionel Sambuc /// {
422*0a6a1f1dSLionel Sambuc /// 'version': <version number>,
423*0a6a1f1dSLionel Sambuc /// <optional configuration>
424*0a6a1f1dSLionel Sambuc /// 'roots': [
425*0a6a1f1dSLionel Sambuc /// <directory entries>
426*0a6a1f1dSLionel Sambuc /// ]
427*0a6a1f1dSLionel Sambuc /// }
428*0a6a1f1dSLionel Sambuc /// \endverbatim
429*0a6a1f1dSLionel Sambuc ///
430*0a6a1f1dSLionel Sambuc /// All configuration options are optional.
431*0a6a1f1dSLionel Sambuc /// 'case-sensitive': <boolean, default=true>
432*0a6a1f1dSLionel Sambuc /// 'use-external-names': <boolean, default=true>
433*0a6a1f1dSLionel Sambuc ///
434*0a6a1f1dSLionel Sambuc /// Virtual directories are represented as
435*0a6a1f1dSLionel Sambuc /// \verbatim
436*0a6a1f1dSLionel Sambuc /// {
437*0a6a1f1dSLionel Sambuc /// 'type': 'directory',
438*0a6a1f1dSLionel Sambuc /// 'name': <string>,
439*0a6a1f1dSLionel Sambuc /// 'contents': [ <file or directory entries> ]
440*0a6a1f1dSLionel Sambuc /// }
441*0a6a1f1dSLionel Sambuc /// \endverbatim
442*0a6a1f1dSLionel Sambuc ///
443*0a6a1f1dSLionel Sambuc /// The default attributes for virtual directories are:
444*0a6a1f1dSLionel Sambuc /// \verbatim
445*0a6a1f1dSLionel Sambuc /// MTime = now() when created
446*0a6a1f1dSLionel Sambuc /// Perms = 0777
447*0a6a1f1dSLionel Sambuc /// User = Group = 0
448*0a6a1f1dSLionel Sambuc /// Size = 0
449*0a6a1f1dSLionel Sambuc /// UniqueID = unspecified unique value
450*0a6a1f1dSLionel Sambuc /// \endverbatim
451*0a6a1f1dSLionel Sambuc ///
452*0a6a1f1dSLionel Sambuc /// Re-mapped files are represented as
453*0a6a1f1dSLionel Sambuc /// \verbatim
454*0a6a1f1dSLionel Sambuc /// {
455*0a6a1f1dSLionel Sambuc /// 'type': 'file',
456*0a6a1f1dSLionel Sambuc /// 'name': <string>,
457*0a6a1f1dSLionel Sambuc /// 'use-external-name': <boolean> # Optional
458*0a6a1f1dSLionel Sambuc /// 'external-contents': <path to external file>)
459*0a6a1f1dSLionel Sambuc /// }
460*0a6a1f1dSLionel Sambuc /// \endverbatim
461*0a6a1f1dSLionel Sambuc ///
462*0a6a1f1dSLionel Sambuc /// and inherit their attributes from the external contents.
463*0a6a1f1dSLionel Sambuc ///
464*0a6a1f1dSLionel Sambuc /// In both cases, the 'name' field may contain multiple path components (e.g.
465*0a6a1f1dSLionel Sambuc /// /path/to/file). However, any directory that contains more than one child
466*0a6a1f1dSLionel Sambuc /// must be uniquely represented by a directory entry.
467*0a6a1f1dSLionel Sambuc class VFSFromYAML : public vfs::FileSystem {
468*0a6a1f1dSLionel Sambuc std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
469*0a6a1f1dSLionel Sambuc /// \brief The file system to use for external references.
470*0a6a1f1dSLionel Sambuc IntrusiveRefCntPtr<FileSystem> ExternalFS;
471*0a6a1f1dSLionel Sambuc
472*0a6a1f1dSLionel Sambuc /// @name Configuration
473*0a6a1f1dSLionel Sambuc /// @{
474*0a6a1f1dSLionel Sambuc
475*0a6a1f1dSLionel Sambuc /// \brief Whether to perform case-sensitive comparisons.
476*0a6a1f1dSLionel Sambuc ///
477*0a6a1f1dSLionel Sambuc /// Currently, case-insensitive matching only works correctly with ASCII.
478*0a6a1f1dSLionel Sambuc bool CaseSensitive;
479*0a6a1f1dSLionel Sambuc
480*0a6a1f1dSLionel Sambuc /// \brief Whether to use to use the value of 'external-contents' for the
481*0a6a1f1dSLionel Sambuc /// names of files. This global value is overridable on a per-file basis.
482*0a6a1f1dSLionel Sambuc bool UseExternalNames;
483*0a6a1f1dSLionel Sambuc /// @}
484*0a6a1f1dSLionel Sambuc
485*0a6a1f1dSLionel Sambuc friend class VFSFromYAMLParser;
486*0a6a1f1dSLionel Sambuc
487*0a6a1f1dSLionel Sambuc private:
VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)488*0a6a1f1dSLionel Sambuc VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
489*0a6a1f1dSLionel Sambuc : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
490*0a6a1f1dSLionel Sambuc
491*0a6a1f1dSLionel Sambuc /// \brief Looks up \p Path in \c Roots.
492*0a6a1f1dSLionel Sambuc ErrorOr<Entry *> lookupPath(const Twine &Path);
493*0a6a1f1dSLionel Sambuc
494*0a6a1f1dSLionel Sambuc /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
495*0a6a1f1dSLionel Sambuc /// recursing into the contents of \p From if it is a directory.
496*0a6a1f1dSLionel Sambuc ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
497*0a6a1f1dSLionel Sambuc sys::path::const_iterator End, Entry *From);
498*0a6a1f1dSLionel Sambuc
499*0a6a1f1dSLionel Sambuc /// \brief Get the status of a given an \c Entry.
500*0a6a1f1dSLionel Sambuc ErrorOr<Status> status(const Twine &Path, Entry *E);
501*0a6a1f1dSLionel Sambuc
502*0a6a1f1dSLionel Sambuc public:
503*0a6a1f1dSLionel Sambuc ~VFSFromYAML();
504*0a6a1f1dSLionel Sambuc
505*0a6a1f1dSLionel Sambuc /// \brief Parses \p Buffer, which is expected to be in YAML format and
506*0a6a1f1dSLionel Sambuc /// returns a virtual file system representing its contents.
507*0a6a1f1dSLionel Sambuc static VFSFromYAML *create(std::unique_ptr<MemoryBuffer> Buffer,
508*0a6a1f1dSLionel Sambuc SourceMgr::DiagHandlerTy DiagHandler,
509*0a6a1f1dSLionel Sambuc void *DiagContext,
510*0a6a1f1dSLionel Sambuc IntrusiveRefCntPtr<FileSystem> ExternalFS);
511*0a6a1f1dSLionel Sambuc
512*0a6a1f1dSLionel Sambuc ErrorOr<Status> status(const Twine &Path) override;
513*0a6a1f1dSLionel Sambuc ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
514*0a6a1f1dSLionel Sambuc
dir_begin(const Twine & Dir,std::error_code & EC)515*0a6a1f1dSLionel Sambuc directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
516*0a6a1f1dSLionel Sambuc ErrorOr<Entry *> E = lookupPath(Dir);
517*0a6a1f1dSLionel Sambuc if (!E) {
518*0a6a1f1dSLionel Sambuc EC = E.getError();
519*0a6a1f1dSLionel Sambuc return directory_iterator();
520*0a6a1f1dSLionel Sambuc }
521*0a6a1f1dSLionel Sambuc ErrorOr<Status> S = status(Dir, *E);
522*0a6a1f1dSLionel Sambuc if (!S) {
523*0a6a1f1dSLionel Sambuc EC = S.getError();
524*0a6a1f1dSLionel Sambuc return directory_iterator();
525*0a6a1f1dSLionel Sambuc }
526*0a6a1f1dSLionel Sambuc if (!S->isDirectory()) {
527*0a6a1f1dSLionel Sambuc EC = std::error_code(static_cast<int>(errc::not_a_directory),
528*0a6a1f1dSLionel Sambuc std::system_category());
529*0a6a1f1dSLionel Sambuc return directory_iterator();
530*0a6a1f1dSLionel Sambuc }
531*0a6a1f1dSLionel Sambuc
532*0a6a1f1dSLionel Sambuc DirectoryEntry *D = cast<DirectoryEntry>(*E);
533*0a6a1f1dSLionel Sambuc return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,
534*0a6a1f1dSLionel Sambuc *this, D->contents_begin(), D->contents_end(), EC));
535*0a6a1f1dSLionel Sambuc }
536*0a6a1f1dSLionel Sambuc };
537*0a6a1f1dSLionel Sambuc
538*0a6a1f1dSLionel Sambuc /// \brief A helper class to hold the common YAML parsing state.
539*0a6a1f1dSLionel Sambuc class VFSFromYAMLParser {
540*0a6a1f1dSLionel Sambuc yaml::Stream &Stream;
541*0a6a1f1dSLionel Sambuc
error(yaml::Node * N,const Twine & Msg)542*0a6a1f1dSLionel Sambuc void error(yaml::Node *N, const Twine &Msg) {
543*0a6a1f1dSLionel Sambuc Stream.printError(N, Msg);
544*0a6a1f1dSLionel Sambuc }
545*0a6a1f1dSLionel Sambuc
546*0a6a1f1dSLionel Sambuc // false on error
parseScalarString(yaml::Node * N,StringRef & Result,SmallVectorImpl<char> & Storage)547*0a6a1f1dSLionel Sambuc bool parseScalarString(yaml::Node *N, StringRef &Result,
548*0a6a1f1dSLionel Sambuc SmallVectorImpl<char> &Storage) {
549*0a6a1f1dSLionel Sambuc yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
550*0a6a1f1dSLionel Sambuc if (!S) {
551*0a6a1f1dSLionel Sambuc error(N, "expected string");
552*0a6a1f1dSLionel Sambuc return false;
553*0a6a1f1dSLionel Sambuc }
554*0a6a1f1dSLionel Sambuc Result = S->getValue(Storage);
555*0a6a1f1dSLionel Sambuc return true;
556*0a6a1f1dSLionel Sambuc }
557*0a6a1f1dSLionel Sambuc
558*0a6a1f1dSLionel Sambuc // false on error
parseScalarBool(yaml::Node * N,bool & Result)559*0a6a1f1dSLionel Sambuc bool parseScalarBool(yaml::Node *N, bool &Result) {
560*0a6a1f1dSLionel Sambuc SmallString<5> Storage;
561*0a6a1f1dSLionel Sambuc StringRef Value;
562*0a6a1f1dSLionel Sambuc if (!parseScalarString(N, Value, Storage))
563*0a6a1f1dSLionel Sambuc return false;
564*0a6a1f1dSLionel Sambuc
565*0a6a1f1dSLionel Sambuc if (Value.equals_lower("true") || Value.equals_lower("on") ||
566*0a6a1f1dSLionel Sambuc Value.equals_lower("yes") || Value == "1") {
567*0a6a1f1dSLionel Sambuc Result = true;
568*0a6a1f1dSLionel Sambuc return true;
569*0a6a1f1dSLionel Sambuc } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
570*0a6a1f1dSLionel Sambuc Value.equals_lower("no") || Value == "0") {
571*0a6a1f1dSLionel Sambuc Result = false;
572*0a6a1f1dSLionel Sambuc return true;
573*0a6a1f1dSLionel Sambuc }
574*0a6a1f1dSLionel Sambuc
575*0a6a1f1dSLionel Sambuc error(N, "expected boolean value");
576*0a6a1f1dSLionel Sambuc return false;
577*0a6a1f1dSLionel Sambuc }
578*0a6a1f1dSLionel Sambuc
579*0a6a1f1dSLionel Sambuc struct KeyStatus {
KeyStatus__anonfd6d57aa0511::VFSFromYAMLParser::KeyStatus580*0a6a1f1dSLionel Sambuc KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
581*0a6a1f1dSLionel Sambuc bool Required;
582*0a6a1f1dSLionel Sambuc bool Seen;
583*0a6a1f1dSLionel Sambuc };
584*0a6a1f1dSLionel Sambuc typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
585*0a6a1f1dSLionel Sambuc
586*0a6a1f1dSLionel Sambuc // false on error
checkDuplicateOrUnknownKey(yaml::Node * KeyNode,StringRef Key,DenseMap<StringRef,KeyStatus> & Keys)587*0a6a1f1dSLionel Sambuc bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
588*0a6a1f1dSLionel Sambuc DenseMap<StringRef, KeyStatus> &Keys) {
589*0a6a1f1dSLionel Sambuc if (!Keys.count(Key)) {
590*0a6a1f1dSLionel Sambuc error(KeyNode, "unknown key");
591*0a6a1f1dSLionel Sambuc return false;
592*0a6a1f1dSLionel Sambuc }
593*0a6a1f1dSLionel Sambuc KeyStatus &S = Keys[Key];
594*0a6a1f1dSLionel Sambuc if (S.Seen) {
595*0a6a1f1dSLionel Sambuc error(KeyNode, Twine("duplicate key '") + Key + "'");
596*0a6a1f1dSLionel Sambuc return false;
597*0a6a1f1dSLionel Sambuc }
598*0a6a1f1dSLionel Sambuc S.Seen = true;
599*0a6a1f1dSLionel Sambuc return true;
600*0a6a1f1dSLionel Sambuc }
601*0a6a1f1dSLionel Sambuc
602*0a6a1f1dSLionel Sambuc // false on error
checkMissingKeys(yaml::Node * Obj,DenseMap<StringRef,KeyStatus> & Keys)603*0a6a1f1dSLionel Sambuc bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
604*0a6a1f1dSLionel Sambuc for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
605*0a6a1f1dSLionel Sambuc E = Keys.end();
606*0a6a1f1dSLionel Sambuc I != E; ++I) {
607*0a6a1f1dSLionel Sambuc if (I->second.Required && !I->second.Seen) {
608*0a6a1f1dSLionel Sambuc error(Obj, Twine("missing key '") + I->first + "'");
609*0a6a1f1dSLionel Sambuc return false;
610*0a6a1f1dSLionel Sambuc }
611*0a6a1f1dSLionel Sambuc }
612*0a6a1f1dSLionel Sambuc return true;
613*0a6a1f1dSLionel Sambuc }
614*0a6a1f1dSLionel Sambuc
parseEntry(yaml::Node * N)615*0a6a1f1dSLionel Sambuc Entry *parseEntry(yaml::Node *N) {
616*0a6a1f1dSLionel Sambuc yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
617*0a6a1f1dSLionel Sambuc if (!M) {
618*0a6a1f1dSLionel Sambuc error(N, "expected mapping node for file or directory entry");
619*0a6a1f1dSLionel Sambuc return nullptr;
620*0a6a1f1dSLionel Sambuc }
621*0a6a1f1dSLionel Sambuc
622*0a6a1f1dSLionel Sambuc KeyStatusPair Fields[] = {
623*0a6a1f1dSLionel Sambuc KeyStatusPair("name", true),
624*0a6a1f1dSLionel Sambuc KeyStatusPair("type", true),
625*0a6a1f1dSLionel Sambuc KeyStatusPair("contents", false),
626*0a6a1f1dSLionel Sambuc KeyStatusPair("external-contents", false),
627*0a6a1f1dSLionel Sambuc KeyStatusPair("use-external-name", false),
628*0a6a1f1dSLionel Sambuc };
629*0a6a1f1dSLionel Sambuc
630*0a6a1f1dSLionel Sambuc DenseMap<StringRef, KeyStatus> Keys(
631*0a6a1f1dSLionel Sambuc &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
632*0a6a1f1dSLionel Sambuc
633*0a6a1f1dSLionel Sambuc bool HasContents = false; // external or otherwise
634*0a6a1f1dSLionel Sambuc std::vector<Entry *> EntryArrayContents;
635*0a6a1f1dSLionel Sambuc std::string ExternalContentsPath;
636*0a6a1f1dSLionel Sambuc std::string Name;
637*0a6a1f1dSLionel Sambuc FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
638*0a6a1f1dSLionel Sambuc EntryKind Kind;
639*0a6a1f1dSLionel Sambuc
640*0a6a1f1dSLionel Sambuc for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
641*0a6a1f1dSLionel Sambuc ++I) {
642*0a6a1f1dSLionel Sambuc StringRef Key;
643*0a6a1f1dSLionel Sambuc // Reuse the buffer for key and value, since we don't look at key after
644*0a6a1f1dSLionel Sambuc // parsing value.
645*0a6a1f1dSLionel Sambuc SmallString<256> Buffer;
646*0a6a1f1dSLionel Sambuc if (!parseScalarString(I->getKey(), Key, Buffer))
647*0a6a1f1dSLionel Sambuc return nullptr;
648*0a6a1f1dSLionel Sambuc
649*0a6a1f1dSLionel Sambuc if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
650*0a6a1f1dSLionel Sambuc return nullptr;
651*0a6a1f1dSLionel Sambuc
652*0a6a1f1dSLionel Sambuc StringRef Value;
653*0a6a1f1dSLionel Sambuc if (Key == "name") {
654*0a6a1f1dSLionel Sambuc if (!parseScalarString(I->getValue(), Value, Buffer))
655*0a6a1f1dSLionel Sambuc return nullptr;
656*0a6a1f1dSLionel Sambuc Name = Value;
657*0a6a1f1dSLionel Sambuc } else if (Key == "type") {
658*0a6a1f1dSLionel Sambuc if (!parseScalarString(I->getValue(), Value, Buffer))
659*0a6a1f1dSLionel Sambuc return nullptr;
660*0a6a1f1dSLionel Sambuc if (Value == "file")
661*0a6a1f1dSLionel Sambuc Kind = EK_File;
662*0a6a1f1dSLionel Sambuc else if (Value == "directory")
663*0a6a1f1dSLionel Sambuc Kind = EK_Directory;
664*0a6a1f1dSLionel Sambuc else {
665*0a6a1f1dSLionel Sambuc error(I->getValue(), "unknown value for 'type'");
666*0a6a1f1dSLionel Sambuc return nullptr;
667*0a6a1f1dSLionel Sambuc }
668*0a6a1f1dSLionel Sambuc } else if (Key == "contents") {
669*0a6a1f1dSLionel Sambuc if (HasContents) {
670*0a6a1f1dSLionel Sambuc error(I->getKey(),
671*0a6a1f1dSLionel Sambuc "entry already has 'contents' or 'external-contents'");
672*0a6a1f1dSLionel Sambuc return nullptr;
673*0a6a1f1dSLionel Sambuc }
674*0a6a1f1dSLionel Sambuc HasContents = true;
675*0a6a1f1dSLionel Sambuc yaml::SequenceNode *Contents =
676*0a6a1f1dSLionel Sambuc dyn_cast<yaml::SequenceNode>(I->getValue());
677*0a6a1f1dSLionel Sambuc if (!Contents) {
678*0a6a1f1dSLionel Sambuc // FIXME: this is only for directories, what about files?
679*0a6a1f1dSLionel Sambuc error(I->getValue(), "expected array");
680*0a6a1f1dSLionel Sambuc return nullptr;
681*0a6a1f1dSLionel Sambuc }
682*0a6a1f1dSLionel Sambuc
683*0a6a1f1dSLionel Sambuc for (yaml::SequenceNode::iterator I = Contents->begin(),
684*0a6a1f1dSLionel Sambuc E = Contents->end();
685*0a6a1f1dSLionel Sambuc I != E; ++I) {
686*0a6a1f1dSLionel Sambuc if (Entry *E = parseEntry(&*I))
687*0a6a1f1dSLionel Sambuc EntryArrayContents.push_back(E);
688*0a6a1f1dSLionel Sambuc else
689*0a6a1f1dSLionel Sambuc return nullptr;
690*0a6a1f1dSLionel Sambuc }
691*0a6a1f1dSLionel Sambuc } else if (Key == "external-contents") {
692*0a6a1f1dSLionel Sambuc if (HasContents) {
693*0a6a1f1dSLionel Sambuc error(I->getKey(),
694*0a6a1f1dSLionel Sambuc "entry already has 'contents' or 'external-contents'");
695*0a6a1f1dSLionel Sambuc return nullptr;
696*0a6a1f1dSLionel Sambuc }
697*0a6a1f1dSLionel Sambuc HasContents = true;
698*0a6a1f1dSLionel Sambuc if (!parseScalarString(I->getValue(), Value, Buffer))
699*0a6a1f1dSLionel Sambuc return nullptr;
700*0a6a1f1dSLionel Sambuc ExternalContentsPath = Value;
701*0a6a1f1dSLionel Sambuc } else if (Key == "use-external-name") {
702*0a6a1f1dSLionel Sambuc bool Val;
703*0a6a1f1dSLionel Sambuc if (!parseScalarBool(I->getValue(), Val))
704*0a6a1f1dSLionel Sambuc return nullptr;
705*0a6a1f1dSLionel Sambuc UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
706*0a6a1f1dSLionel Sambuc } else {
707*0a6a1f1dSLionel Sambuc llvm_unreachable("key missing from Keys");
708*0a6a1f1dSLionel Sambuc }
709*0a6a1f1dSLionel Sambuc }
710*0a6a1f1dSLionel Sambuc
711*0a6a1f1dSLionel Sambuc if (Stream.failed())
712*0a6a1f1dSLionel Sambuc return nullptr;
713*0a6a1f1dSLionel Sambuc
714*0a6a1f1dSLionel Sambuc // check for missing keys
715*0a6a1f1dSLionel Sambuc if (!HasContents) {
716*0a6a1f1dSLionel Sambuc error(N, "missing key 'contents' or 'external-contents'");
717*0a6a1f1dSLionel Sambuc return nullptr;
718*0a6a1f1dSLionel Sambuc }
719*0a6a1f1dSLionel Sambuc if (!checkMissingKeys(N, Keys))
720*0a6a1f1dSLionel Sambuc return nullptr;
721*0a6a1f1dSLionel Sambuc
722*0a6a1f1dSLionel Sambuc // check invalid configuration
723*0a6a1f1dSLionel Sambuc if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
724*0a6a1f1dSLionel Sambuc error(N, "'use-external-name' is not supported for directories");
725*0a6a1f1dSLionel Sambuc return nullptr;
726*0a6a1f1dSLionel Sambuc }
727*0a6a1f1dSLionel Sambuc
728*0a6a1f1dSLionel Sambuc // Remove trailing slash(es), being careful not to remove the root path
729*0a6a1f1dSLionel Sambuc StringRef Trimmed(Name);
730*0a6a1f1dSLionel Sambuc size_t RootPathLen = sys::path::root_path(Trimmed).size();
731*0a6a1f1dSLionel Sambuc while (Trimmed.size() > RootPathLen &&
732*0a6a1f1dSLionel Sambuc sys::path::is_separator(Trimmed.back()))
733*0a6a1f1dSLionel Sambuc Trimmed = Trimmed.slice(0, Trimmed.size()-1);
734*0a6a1f1dSLionel Sambuc // Get the last component
735*0a6a1f1dSLionel Sambuc StringRef LastComponent = sys::path::filename(Trimmed);
736*0a6a1f1dSLionel Sambuc
737*0a6a1f1dSLionel Sambuc Entry *Result = nullptr;
738*0a6a1f1dSLionel Sambuc switch (Kind) {
739*0a6a1f1dSLionel Sambuc case EK_File:
740*0a6a1f1dSLionel Sambuc Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
741*0a6a1f1dSLionel Sambuc UseExternalName);
742*0a6a1f1dSLionel Sambuc break;
743*0a6a1f1dSLionel Sambuc case EK_Directory:
744*0a6a1f1dSLionel Sambuc Result = new DirectoryEntry(LastComponent, std::move(EntryArrayContents),
745*0a6a1f1dSLionel Sambuc Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
746*0a6a1f1dSLionel Sambuc 0, file_type::directory_file, sys::fs::all_all));
747*0a6a1f1dSLionel Sambuc break;
748*0a6a1f1dSLionel Sambuc }
749*0a6a1f1dSLionel Sambuc
750*0a6a1f1dSLionel Sambuc StringRef Parent = sys::path::parent_path(Trimmed);
751*0a6a1f1dSLionel Sambuc if (Parent.empty())
752*0a6a1f1dSLionel Sambuc return Result;
753*0a6a1f1dSLionel Sambuc
754*0a6a1f1dSLionel Sambuc // if 'name' contains multiple components, create implicit directory entries
755*0a6a1f1dSLionel Sambuc for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
756*0a6a1f1dSLionel Sambuc E = sys::path::rend(Parent);
757*0a6a1f1dSLionel Sambuc I != E; ++I) {
758*0a6a1f1dSLionel Sambuc Result = new DirectoryEntry(*I, llvm::makeArrayRef(Result),
759*0a6a1f1dSLionel Sambuc Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
760*0a6a1f1dSLionel Sambuc 0, file_type::directory_file, sys::fs::all_all));
761*0a6a1f1dSLionel Sambuc }
762*0a6a1f1dSLionel Sambuc return Result;
763*0a6a1f1dSLionel Sambuc }
764*0a6a1f1dSLionel Sambuc
765*0a6a1f1dSLionel Sambuc public:
VFSFromYAMLParser(yaml::Stream & S)766*0a6a1f1dSLionel Sambuc VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
767*0a6a1f1dSLionel Sambuc
768*0a6a1f1dSLionel Sambuc // false on error
parse(yaml::Node * Root,VFSFromYAML * FS)769*0a6a1f1dSLionel Sambuc bool parse(yaml::Node *Root, VFSFromYAML *FS) {
770*0a6a1f1dSLionel Sambuc yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
771*0a6a1f1dSLionel Sambuc if (!Top) {
772*0a6a1f1dSLionel Sambuc error(Root, "expected mapping node");
773*0a6a1f1dSLionel Sambuc return false;
774*0a6a1f1dSLionel Sambuc }
775*0a6a1f1dSLionel Sambuc
776*0a6a1f1dSLionel Sambuc KeyStatusPair Fields[] = {
777*0a6a1f1dSLionel Sambuc KeyStatusPair("version", true),
778*0a6a1f1dSLionel Sambuc KeyStatusPair("case-sensitive", false),
779*0a6a1f1dSLionel Sambuc KeyStatusPair("use-external-names", false),
780*0a6a1f1dSLionel Sambuc KeyStatusPair("roots", true),
781*0a6a1f1dSLionel Sambuc };
782*0a6a1f1dSLionel Sambuc
783*0a6a1f1dSLionel Sambuc DenseMap<StringRef, KeyStatus> Keys(
784*0a6a1f1dSLionel Sambuc &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
785*0a6a1f1dSLionel Sambuc
786*0a6a1f1dSLionel Sambuc // Parse configuration and 'roots'
787*0a6a1f1dSLionel Sambuc for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
788*0a6a1f1dSLionel Sambuc ++I) {
789*0a6a1f1dSLionel Sambuc SmallString<10> KeyBuffer;
790*0a6a1f1dSLionel Sambuc StringRef Key;
791*0a6a1f1dSLionel Sambuc if (!parseScalarString(I->getKey(), Key, KeyBuffer))
792*0a6a1f1dSLionel Sambuc return false;
793*0a6a1f1dSLionel Sambuc
794*0a6a1f1dSLionel Sambuc if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
795*0a6a1f1dSLionel Sambuc return false;
796*0a6a1f1dSLionel Sambuc
797*0a6a1f1dSLionel Sambuc if (Key == "roots") {
798*0a6a1f1dSLionel Sambuc yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
799*0a6a1f1dSLionel Sambuc if (!Roots) {
800*0a6a1f1dSLionel Sambuc error(I->getValue(), "expected array");
801*0a6a1f1dSLionel Sambuc return false;
802*0a6a1f1dSLionel Sambuc }
803*0a6a1f1dSLionel Sambuc
804*0a6a1f1dSLionel Sambuc for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
805*0a6a1f1dSLionel Sambuc I != E; ++I) {
806*0a6a1f1dSLionel Sambuc if (Entry *E = parseEntry(&*I))
807*0a6a1f1dSLionel Sambuc FS->Roots.push_back(E);
808*0a6a1f1dSLionel Sambuc else
809*0a6a1f1dSLionel Sambuc return false;
810*0a6a1f1dSLionel Sambuc }
811*0a6a1f1dSLionel Sambuc } else if (Key == "version") {
812*0a6a1f1dSLionel Sambuc StringRef VersionString;
813*0a6a1f1dSLionel Sambuc SmallString<4> Storage;
814*0a6a1f1dSLionel Sambuc if (!parseScalarString(I->getValue(), VersionString, Storage))
815*0a6a1f1dSLionel Sambuc return false;
816*0a6a1f1dSLionel Sambuc int Version;
817*0a6a1f1dSLionel Sambuc if (VersionString.getAsInteger<int>(10, Version)) {
818*0a6a1f1dSLionel Sambuc error(I->getValue(), "expected integer");
819*0a6a1f1dSLionel Sambuc return false;
820*0a6a1f1dSLionel Sambuc }
821*0a6a1f1dSLionel Sambuc if (Version < 0) {
822*0a6a1f1dSLionel Sambuc error(I->getValue(), "invalid version number");
823*0a6a1f1dSLionel Sambuc return false;
824*0a6a1f1dSLionel Sambuc }
825*0a6a1f1dSLionel Sambuc if (Version != 0) {
826*0a6a1f1dSLionel Sambuc error(I->getValue(), "version mismatch, expected 0");
827*0a6a1f1dSLionel Sambuc return false;
828*0a6a1f1dSLionel Sambuc }
829*0a6a1f1dSLionel Sambuc } else if (Key == "case-sensitive") {
830*0a6a1f1dSLionel Sambuc if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
831*0a6a1f1dSLionel Sambuc return false;
832*0a6a1f1dSLionel Sambuc } else if (Key == "use-external-names") {
833*0a6a1f1dSLionel Sambuc if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
834*0a6a1f1dSLionel Sambuc return false;
835*0a6a1f1dSLionel Sambuc } else {
836*0a6a1f1dSLionel Sambuc llvm_unreachable("key missing from Keys");
837*0a6a1f1dSLionel Sambuc }
838*0a6a1f1dSLionel Sambuc }
839*0a6a1f1dSLionel Sambuc
840*0a6a1f1dSLionel Sambuc if (Stream.failed())
841*0a6a1f1dSLionel Sambuc return false;
842*0a6a1f1dSLionel Sambuc
843*0a6a1f1dSLionel Sambuc if (!checkMissingKeys(Top, Keys))
844*0a6a1f1dSLionel Sambuc return false;
845*0a6a1f1dSLionel Sambuc return true;
846*0a6a1f1dSLionel Sambuc }
847*0a6a1f1dSLionel Sambuc };
848*0a6a1f1dSLionel Sambuc } // end of anonymous namespace
849*0a6a1f1dSLionel Sambuc
~Entry()850*0a6a1f1dSLionel Sambuc Entry::~Entry() {}
~DirectoryEntry()851*0a6a1f1dSLionel Sambuc DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
852*0a6a1f1dSLionel Sambuc
~VFSFromYAML()853*0a6a1f1dSLionel Sambuc VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
854*0a6a1f1dSLionel Sambuc
create(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)855*0a6a1f1dSLionel Sambuc VFSFromYAML *VFSFromYAML::create(std::unique_ptr<MemoryBuffer> Buffer,
856*0a6a1f1dSLionel Sambuc SourceMgr::DiagHandlerTy DiagHandler,
857*0a6a1f1dSLionel Sambuc void *DiagContext,
858*0a6a1f1dSLionel Sambuc IntrusiveRefCntPtr<FileSystem> ExternalFS) {
859*0a6a1f1dSLionel Sambuc
860*0a6a1f1dSLionel Sambuc SourceMgr SM;
861*0a6a1f1dSLionel Sambuc yaml::Stream Stream(Buffer->getMemBufferRef(), SM);
862*0a6a1f1dSLionel Sambuc
863*0a6a1f1dSLionel Sambuc SM.setDiagHandler(DiagHandler, DiagContext);
864*0a6a1f1dSLionel Sambuc yaml::document_iterator DI = Stream.begin();
865*0a6a1f1dSLionel Sambuc yaml::Node *Root = DI->getRoot();
866*0a6a1f1dSLionel Sambuc if (DI == Stream.end() || !Root) {
867*0a6a1f1dSLionel Sambuc SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
868*0a6a1f1dSLionel Sambuc return nullptr;
869*0a6a1f1dSLionel Sambuc }
870*0a6a1f1dSLionel Sambuc
871*0a6a1f1dSLionel Sambuc VFSFromYAMLParser P(Stream);
872*0a6a1f1dSLionel Sambuc
873*0a6a1f1dSLionel Sambuc std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
874*0a6a1f1dSLionel Sambuc if (!P.parse(Root, FS.get()))
875*0a6a1f1dSLionel Sambuc return nullptr;
876*0a6a1f1dSLionel Sambuc
877*0a6a1f1dSLionel Sambuc return FS.release();
878*0a6a1f1dSLionel Sambuc }
879*0a6a1f1dSLionel Sambuc
lookupPath(const Twine & Path_)880*0a6a1f1dSLionel Sambuc ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
881*0a6a1f1dSLionel Sambuc SmallString<256> Path;
882*0a6a1f1dSLionel Sambuc Path_.toVector(Path);
883*0a6a1f1dSLionel Sambuc
884*0a6a1f1dSLionel Sambuc // Handle relative paths
885*0a6a1f1dSLionel Sambuc if (std::error_code EC = sys::fs::make_absolute(Path))
886*0a6a1f1dSLionel Sambuc return EC;
887*0a6a1f1dSLionel Sambuc
888*0a6a1f1dSLionel Sambuc if (Path.empty())
889*0a6a1f1dSLionel Sambuc return make_error_code(llvm::errc::invalid_argument);
890*0a6a1f1dSLionel Sambuc
891*0a6a1f1dSLionel Sambuc sys::path::const_iterator Start = sys::path::begin(Path);
892*0a6a1f1dSLionel Sambuc sys::path::const_iterator End = sys::path::end(Path);
893*0a6a1f1dSLionel Sambuc for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
894*0a6a1f1dSLionel Sambuc I != E; ++I) {
895*0a6a1f1dSLionel Sambuc ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
896*0a6a1f1dSLionel Sambuc if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
897*0a6a1f1dSLionel Sambuc return Result;
898*0a6a1f1dSLionel Sambuc }
899*0a6a1f1dSLionel Sambuc return make_error_code(llvm::errc::no_such_file_or_directory);
900*0a6a1f1dSLionel Sambuc }
901*0a6a1f1dSLionel Sambuc
lookupPath(sys::path::const_iterator Start,sys::path::const_iterator End,Entry * From)902*0a6a1f1dSLionel Sambuc ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
903*0a6a1f1dSLionel Sambuc sys::path::const_iterator End,
904*0a6a1f1dSLionel Sambuc Entry *From) {
905*0a6a1f1dSLionel Sambuc if (Start->equals("."))
906*0a6a1f1dSLionel Sambuc ++Start;
907*0a6a1f1dSLionel Sambuc
908*0a6a1f1dSLionel Sambuc // FIXME: handle ..
909*0a6a1f1dSLionel Sambuc if (CaseSensitive ? !Start->equals(From->getName())
910*0a6a1f1dSLionel Sambuc : !Start->equals_lower(From->getName()))
911*0a6a1f1dSLionel Sambuc // failure to match
912*0a6a1f1dSLionel Sambuc return make_error_code(llvm::errc::no_such_file_or_directory);
913*0a6a1f1dSLionel Sambuc
914*0a6a1f1dSLionel Sambuc ++Start;
915*0a6a1f1dSLionel Sambuc
916*0a6a1f1dSLionel Sambuc if (Start == End) {
917*0a6a1f1dSLionel Sambuc // Match!
918*0a6a1f1dSLionel Sambuc return From;
919*0a6a1f1dSLionel Sambuc }
920*0a6a1f1dSLionel Sambuc
921*0a6a1f1dSLionel Sambuc DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
922*0a6a1f1dSLionel Sambuc if (!DE)
923*0a6a1f1dSLionel Sambuc return make_error_code(llvm::errc::not_a_directory);
924*0a6a1f1dSLionel Sambuc
925*0a6a1f1dSLionel Sambuc for (DirectoryEntry::iterator I = DE->contents_begin(),
926*0a6a1f1dSLionel Sambuc E = DE->contents_end();
927*0a6a1f1dSLionel Sambuc I != E; ++I) {
928*0a6a1f1dSLionel Sambuc ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
929*0a6a1f1dSLionel Sambuc if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)
930*0a6a1f1dSLionel Sambuc return Result;
931*0a6a1f1dSLionel Sambuc }
932*0a6a1f1dSLionel Sambuc return make_error_code(llvm::errc::no_such_file_or_directory);
933*0a6a1f1dSLionel Sambuc }
934*0a6a1f1dSLionel Sambuc
status(const Twine & Path,Entry * E)935*0a6a1f1dSLionel Sambuc ErrorOr<Status> VFSFromYAML::status(const Twine &Path, Entry *E) {
936*0a6a1f1dSLionel Sambuc assert(E != nullptr);
937*0a6a1f1dSLionel Sambuc std::string PathStr(Path.str());
938*0a6a1f1dSLionel Sambuc if (FileEntry *F = dyn_cast<FileEntry>(E)) {
939*0a6a1f1dSLionel Sambuc ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
940*0a6a1f1dSLionel Sambuc assert(!S || S->getName() == F->getExternalContentsPath());
941*0a6a1f1dSLionel Sambuc if (S && !F->useExternalName(UseExternalNames))
942*0a6a1f1dSLionel Sambuc S->setName(PathStr);
943*0a6a1f1dSLionel Sambuc if (S)
944*0a6a1f1dSLionel Sambuc S->IsVFSMapped = true;
945*0a6a1f1dSLionel Sambuc return S;
946*0a6a1f1dSLionel Sambuc } else { // directory
947*0a6a1f1dSLionel Sambuc DirectoryEntry *DE = cast<DirectoryEntry>(E);
948*0a6a1f1dSLionel Sambuc Status S = DE->getStatus();
949*0a6a1f1dSLionel Sambuc S.setName(PathStr);
950*0a6a1f1dSLionel Sambuc return S;
951*0a6a1f1dSLionel Sambuc }
952*0a6a1f1dSLionel Sambuc }
953*0a6a1f1dSLionel Sambuc
status(const Twine & Path)954*0a6a1f1dSLionel Sambuc ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
955*0a6a1f1dSLionel Sambuc ErrorOr<Entry *> Result = lookupPath(Path);
956*0a6a1f1dSLionel Sambuc if (!Result)
957*0a6a1f1dSLionel Sambuc return Result.getError();
958*0a6a1f1dSLionel Sambuc return status(Path, *Result);
959*0a6a1f1dSLionel Sambuc }
960*0a6a1f1dSLionel Sambuc
openFileForRead(const Twine & Path)961*0a6a1f1dSLionel Sambuc ErrorOr<std::unique_ptr<File>> VFSFromYAML::openFileForRead(const Twine &Path) {
962*0a6a1f1dSLionel Sambuc ErrorOr<Entry *> E = lookupPath(Path);
963*0a6a1f1dSLionel Sambuc if (!E)
964*0a6a1f1dSLionel Sambuc return E.getError();
965*0a6a1f1dSLionel Sambuc
966*0a6a1f1dSLionel Sambuc FileEntry *F = dyn_cast<FileEntry>(*E);
967*0a6a1f1dSLionel Sambuc if (!F) // FIXME: errc::not_a_file?
968*0a6a1f1dSLionel Sambuc return make_error_code(llvm::errc::invalid_argument);
969*0a6a1f1dSLionel Sambuc
970*0a6a1f1dSLionel Sambuc auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath());
971*0a6a1f1dSLionel Sambuc if (!Result)
972*0a6a1f1dSLionel Sambuc return Result;
973*0a6a1f1dSLionel Sambuc
974*0a6a1f1dSLionel Sambuc if (!F->useExternalName(UseExternalNames))
975*0a6a1f1dSLionel Sambuc (*Result)->setName(Path.str());
976*0a6a1f1dSLionel Sambuc
977*0a6a1f1dSLionel Sambuc return Result;
978*0a6a1f1dSLionel Sambuc }
979*0a6a1f1dSLionel Sambuc
980*0a6a1f1dSLionel Sambuc IntrusiveRefCntPtr<FileSystem>
getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,SourceMgr::DiagHandlerTy DiagHandler,void * DiagContext,IntrusiveRefCntPtr<FileSystem> ExternalFS)981*0a6a1f1dSLionel Sambuc vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,
982*0a6a1f1dSLionel Sambuc SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,
983*0a6a1f1dSLionel Sambuc IntrusiveRefCntPtr<FileSystem> ExternalFS) {
984*0a6a1f1dSLionel Sambuc return VFSFromYAML::create(std::move(Buffer), DiagHandler, DiagContext,
985*0a6a1f1dSLionel Sambuc ExternalFS);
986*0a6a1f1dSLionel Sambuc }
987*0a6a1f1dSLionel Sambuc
getNextVirtualUniqueID()988*0a6a1f1dSLionel Sambuc UniqueID vfs::getNextVirtualUniqueID() {
989*0a6a1f1dSLionel Sambuc #if !defined(_LIBCPP_HAS_NO_THREADS) && defined(__minix)
990*0a6a1f1dSLionel Sambuc static std::atomic<unsigned> UID;
991*0a6a1f1dSLionel Sambuc #else
992*0a6a1f1dSLionel Sambuc static unsigned UID;
993*0a6a1f1dSLionel Sambuc #endif // !defined(_LIBCPP_HAS_NO_THREADS) && defined(__minix)
994*0a6a1f1dSLionel Sambuc unsigned ID = ++UID;
995*0a6a1f1dSLionel Sambuc // The following assumes that uint64_t max will never collide with a real
996*0a6a1f1dSLionel Sambuc // dev_t value from the OS.
997*0a6a1f1dSLionel Sambuc return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
998*0a6a1f1dSLionel Sambuc }
999*0a6a1f1dSLionel Sambuc
1000*0a6a1f1dSLionel Sambuc #ifndef NDEBUG
pathHasTraversal(StringRef Path)1001*0a6a1f1dSLionel Sambuc static bool pathHasTraversal(StringRef Path) {
1002*0a6a1f1dSLionel Sambuc using namespace llvm::sys;
1003*0a6a1f1dSLionel Sambuc for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
1004*0a6a1f1dSLionel Sambuc if (Comp == "." || Comp == "..")
1005*0a6a1f1dSLionel Sambuc return true;
1006*0a6a1f1dSLionel Sambuc return false;
1007*0a6a1f1dSLionel Sambuc }
1008*0a6a1f1dSLionel Sambuc #endif
1009*0a6a1f1dSLionel Sambuc
addFileMapping(StringRef VirtualPath,StringRef RealPath)1010*0a6a1f1dSLionel Sambuc void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
1011*0a6a1f1dSLionel Sambuc assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
1012*0a6a1f1dSLionel Sambuc assert(sys::path::is_absolute(RealPath) && "real path not absolute");
1013*0a6a1f1dSLionel Sambuc assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
1014*0a6a1f1dSLionel Sambuc Mappings.emplace_back(VirtualPath, RealPath);
1015*0a6a1f1dSLionel Sambuc }
1016*0a6a1f1dSLionel Sambuc
1017*0a6a1f1dSLionel Sambuc namespace {
1018*0a6a1f1dSLionel Sambuc class JSONWriter {
1019*0a6a1f1dSLionel Sambuc llvm::raw_ostream &OS;
1020*0a6a1f1dSLionel Sambuc SmallVector<StringRef, 16> DirStack;
getDirIndent()1021*0a6a1f1dSLionel Sambuc inline unsigned getDirIndent() { return 4 * DirStack.size(); }
getFileIndent()1022*0a6a1f1dSLionel Sambuc inline unsigned getFileIndent() { return 4 * (DirStack.size() + 1); }
1023*0a6a1f1dSLionel Sambuc bool containedIn(StringRef Parent, StringRef Path);
1024*0a6a1f1dSLionel Sambuc StringRef containedPart(StringRef Parent, StringRef Path);
1025*0a6a1f1dSLionel Sambuc void startDirectory(StringRef Path);
1026*0a6a1f1dSLionel Sambuc void endDirectory();
1027*0a6a1f1dSLionel Sambuc void writeEntry(StringRef VPath, StringRef RPath);
1028*0a6a1f1dSLionel Sambuc
1029*0a6a1f1dSLionel Sambuc public:
JSONWriter(llvm::raw_ostream & OS)1030*0a6a1f1dSLionel Sambuc JSONWriter(llvm::raw_ostream &OS) : OS(OS) {}
1031*0a6a1f1dSLionel Sambuc void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> IsCaseSensitive);
1032*0a6a1f1dSLionel Sambuc };
1033*0a6a1f1dSLionel Sambuc }
1034*0a6a1f1dSLionel Sambuc
containedIn(StringRef Parent,StringRef Path)1035*0a6a1f1dSLionel Sambuc bool JSONWriter::containedIn(StringRef Parent, StringRef Path) {
1036*0a6a1f1dSLionel Sambuc using namespace llvm::sys;
1037*0a6a1f1dSLionel Sambuc // Compare each path component.
1038*0a6a1f1dSLionel Sambuc auto IParent = path::begin(Parent), EParent = path::end(Parent);
1039*0a6a1f1dSLionel Sambuc for (auto IChild = path::begin(Path), EChild = path::end(Path);
1040*0a6a1f1dSLionel Sambuc IParent != EParent && IChild != EChild; ++IParent, ++IChild) {
1041*0a6a1f1dSLionel Sambuc if (*IParent != *IChild)
1042*0a6a1f1dSLionel Sambuc return false;
1043*0a6a1f1dSLionel Sambuc }
1044*0a6a1f1dSLionel Sambuc // Have we exhausted the parent path?
1045*0a6a1f1dSLionel Sambuc return IParent == EParent;
1046*0a6a1f1dSLionel Sambuc }
1047*0a6a1f1dSLionel Sambuc
containedPart(StringRef Parent,StringRef Path)1048*0a6a1f1dSLionel Sambuc StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) {
1049*0a6a1f1dSLionel Sambuc assert(!Parent.empty());
1050*0a6a1f1dSLionel Sambuc assert(containedIn(Parent, Path));
1051*0a6a1f1dSLionel Sambuc return Path.slice(Parent.size() + 1, StringRef::npos);
1052*0a6a1f1dSLionel Sambuc }
1053*0a6a1f1dSLionel Sambuc
startDirectory(StringRef Path)1054*0a6a1f1dSLionel Sambuc void JSONWriter::startDirectory(StringRef Path) {
1055*0a6a1f1dSLionel Sambuc StringRef Name =
1056*0a6a1f1dSLionel Sambuc DirStack.empty() ? Path : containedPart(DirStack.back(), Path);
1057*0a6a1f1dSLionel Sambuc DirStack.push_back(Path);
1058*0a6a1f1dSLionel Sambuc unsigned Indent = getDirIndent();
1059*0a6a1f1dSLionel Sambuc OS.indent(Indent) << "{\n";
1060*0a6a1f1dSLionel Sambuc OS.indent(Indent + 2) << "'type': 'directory',\n";
1061*0a6a1f1dSLionel Sambuc OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n";
1062*0a6a1f1dSLionel Sambuc OS.indent(Indent + 2) << "'contents': [\n";
1063*0a6a1f1dSLionel Sambuc }
1064*0a6a1f1dSLionel Sambuc
endDirectory()1065*0a6a1f1dSLionel Sambuc void JSONWriter::endDirectory() {
1066*0a6a1f1dSLionel Sambuc unsigned Indent = getDirIndent();
1067*0a6a1f1dSLionel Sambuc OS.indent(Indent + 2) << "]\n";
1068*0a6a1f1dSLionel Sambuc OS.indent(Indent) << "}";
1069*0a6a1f1dSLionel Sambuc
1070*0a6a1f1dSLionel Sambuc DirStack.pop_back();
1071*0a6a1f1dSLionel Sambuc }
1072*0a6a1f1dSLionel Sambuc
writeEntry(StringRef VPath,StringRef RPath)1073*0a6a1f1dSLionel Sambuc void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) {
1074*0a6a1f1dSLionel Sambuc unsigned Indent = getFileIndent();
1075*0a6a1f1dSLionel Sambuc OS.indent(Indent) << "{\n";
1076*0a6a1f1dSLionel Sambuc OS.indent(Indent + 2) << "'type': 'file',\n";
1077*0a6a1f1dSLionel Sambuc OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n";
1078*0a6a1f1dSLionel Sambuc OS.indent(Indent + 2) << "'external-contents': \""
1079*0a6a1f1dSLionel Sambuc << llvm::yaml::escape(RPath) << "\"\n";
1080*0a6a1f1dSLionel Sambuc OS.indent(Indent) << "}";
1081*0a6a1f1dSLionel Sambuc }
1082*0a6a1f1dSLionel Sambuc
write(ArrayRef<YAMLVFSEntry> Entries,Optional<bool> IsCaseSensitive)1083*0a6a1f1dSLionel Sambuc void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries,
1084*0a6a1f1dSLionel Sambuc Optional<bool> IsCaseSensitive) {
1085*0a6a1f1dSLionel Sambuc using namespace llvm::sys;
1086*0a6a1f1dSLionel Sambuc
1087*0a6a1f1dSLionel Sambuc OS << "{\n"
1088*0a6a1f1dSLionel Sambuc " 'version': 0,\n";
1089*0a6a1f1dSLionel Sambuc if (IsCaseSensitive.hasValue())
1090*0a6a1f1dSLionel Sambuc OS << " 'case-sensitive': '"
1091*0a6a1f1dSLionel Sambuc << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n";
1092*0a6a1f1dSLionel Sambuc OS << " 'roots': [\n";
1093*0a6a1f1dSLionel Sambuc
1094*0a6a1f1dSLionel Sambuc if (!Entries.empty()) {
1095*0a6a1f1dSLionel Sambuc const YAMLVFSEntry &Entry = Entries.front();
1096*0a6a1f1dSLionel Sambuc startDirectory(path::parent_path(Entry.VPath));
1097*0a6a1f1dSLionel Sambuc writeEntry(path::filename(Entry.VPath), Entry.RPath);
1098*0a6a1f1dSLionel Sambuc
1099*0a6a1f1dSLionel Sambuc for (const auto &Entry : Entries.slice(1)) {
1100*0a6a1f1dSLionel Sambuc StringRef Dir = path::parent_path(Entry.VPath);
1101*0a6a1f1dSLionel Sambuc if (Dir == DirStack.back())
1102*0a6a1f1dSLionel Sambuc OS << ",\n";
1103*0a6a1f1dSLionel Sambuc else {
1104*0a6a1f1dSLionel Sambuc while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) {
1105*0a6a1f1dSLionel Sambuc OS << "\n";
1106*0a6a1f1dSLionel Sambuc endDirectory();
1107*0a6a1f1dSLionel Sambuc }
1108*0a6a1f1dSLionel Sambuc OS << ",\n";
1109*0a6a1f1dSLionel Sambuc startDirectory(Dir);
1110*0a6a1f1dSLionel Sambuc }
1111*0a6a1f1dSLionel Sambuc writeEntry(path::filename(Entry.VPath), Entry.RPath);
1112*0a6a1f1dSLionel Sambuc }
1113*0a6a1f1dSLionel Sambuc
1114*0a6a1f1dSLionel Sambuc while (!DirStack.empty()) {
1115*0a6a1f1dSLionel Sambuc OS << "\n";
1116*0a6a1f1dSLionel Sambuc endDirectory();
1117*0a6a1f1dSLionel Sambuc }
1118*0a6a1f1dSLionel Sambuc OS << "\n";
1119*0a6a1f1dSLionel Sambuc }
1120*0a6a1f1dSLionel Sambuc
1121*0a6a1f1dSLionel Sambuc OS << " ]\n"
1122*0a6a1f1dSLionel Sambuc << "}\n";
1123*0a6a1f1dSLionel Sambuc }
1124*0a6a1f1dSLionel Sambuc
write(llvm::raw_ostream & OS)1125*0a6a1f1dSLionel Sambuc void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
1126*0a6a1f1dSLionel Sambuc std::sort(Mappings.begin(), Mappings.end(),
1127*0a6a1f1dSLionel Sambuc [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) {
1128*0a6a1f1dSLionel Sambuc return LHS.VPath < RHS.VPath;
1129*0a6a1f1dSLionel Sambuc });
1130*0a6a1f1dSLionel Sambuc
1131*0a6a1f1dSLionel Sambuc JSONWriter(OS).write(Mappings, IsCaseSensitive);
1132*0a6a1f1dSLionel Sambuc }
1133*0a6a1f1dSLionel Sambuc
VFSFromYamlDirIterImpl(const Twine & _Path,VFSFromYAML & FS,DirectoryEntry::iterator Begin,DirectoryEntry::iterator End,std::error_code & EC)1134*0a6a1f1dSLionel Sambuc VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(const Twine &_Path,
1135*0a6a1f1dSLionel Sambuc VFSFromYAML &FS,
1136*0a6a1f1dSLionel Sambuc DirectoryEntry::iterator Begin,
1137*0a6a1f1dSLionel Sambuc DirectoryEntry::iterator End,
1138*0a6a1f1dSLionel Sambuc std::error_code &EC)
1139*0a6a1f1dSLionel Sambuc : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {
1140*0a6a1f1dSLionel Sambuc if (Current != End) {
1141*0a6a1f1dSLionel Sambuc SmallString<128> PathStr(Dir);
1142*0a6a1f1dSLionel Sambuc llvm::sys::path::append(PathStr, (*Current)->getName());
1143*0a6a1f1dSLionel Sambuc llvm::ErrorOr<vfs::Status> S = FS.status(PathStr.str());
1144*0a6a1f1dSLionel Sambuc if (S)
1145*0a6a1f1dSLionel Sambuc CurrentEntry = *S;
1146*0a6a1f1dSLionel Sambuc else
1147*0a6a1f1dSLionel Sambuc EC = S.getError();
1148*0a6a1f1dSLionel Sambuc }
1149*0a6a1f1dSLionel Sambuc }
1150*0a6a1f1dSLionel Sambuc
increment()1151*0a6a1f1dSLionel Sambuc std::error_code VFSFromYamlDirIterImpl::increment() {
1152*0a6a1f1dSLionel Sambuc assert(Current != End && "cannot iterate past end");
1153*0a6a1f1dSLionel Sambuc if (++Current != End) {
1154*0a6a1f1dSLionel Sambuc SmallString<128> PathStr(Dir);
1155*0a6a1f1dSLionel Sambuc llvm::sys::path::append(PathStr, (*Current)->getName());
1156*0a6a1f1dSLionel Sambuc llvm::ErrorOr<vfs::Status> S = FS.status(PathStr.str());
1157*0a6a1f1dSLionel Sambuc if (!S)
1158*0a6a1f1dSLionel Sambuc return S.getError();
1159*0a6a1f1dSLionel Sambuc CurrentEntry = *S;
1160*0a6a1f1dSLionel Sambuc } else {
1161*0a6a1f1dSLionel Sambuc CurrentEntry = Status();
1162*0a6a1f1dSLionel Sambuc }
1163*0a6a1f1dSLionel Sambuc return std::error_code();
1164*0a6a1f1dSLionel Sambuc }
1165*0a6a1f1dSLionel Sambuc
recursive_directory_iterator(FileSystem & FS_,const Twine & Path,std::error_code & EC)1166*0a6a1f1dSLionel Sambuc vfs::recursive_directory_iterator::recursive_directory_iterator(FileSystem &FS_,
1167*0a6a1f1dSLionel Sambuc const Twine &Path,
1168*0a6a1f1dSLionel Sambuc std::error_code &EC)
1169*0a6a1f1dSLionel Sambuc : FS(&FS_) {
1170*0a6a1f1dSLionel Sambuc directory_iterator I = FS->dir_begin(Path, EC);
1171*0a6a1f1dSLionel Sambuc if (!EC && I != directory_iterator()) {
1172*0a6a1f1dSLionel Sambuc State = std::make_shared<IterState>();
1173*0a6a1f1dSLionel Sambuc State->push(I);
1174*0a6a1f1dSLionel Sambuc }
1175*0a6a1f1dSLionel Sambuc }
1176*0a6a1f1dSLionel Sambuc
1177*0a6a1f1dSLionel Sambuc vfs::recursive_directory_iterator &
increment(std::error_code & EC)1178*0a6a1f1dSLionel Sambuc recursive_directory_iterator::increment(std::error_code &EC) {
1179*0a6a1f1dSLionel Sambuc assert(FS && State && !State->empty() && "incrementing past end");
1180*0a6a1f1dSLionel Sambuc assert(State->top()->isStatusKnown() && "non-canonical end iterator");
1181*0a6a1f1dSLionel Sambuc vfs::directory_iterator End;
1182*0a6a1f1dSLionel Sambuc if (State->top()->isDirectory()) {
1183*0a6a1f1dSLionel Sambuc vfs::directory_iterator I = FS->dir_begin(State->top()->getName(), EC);
1184*0a6a1f1dSLionel Sambuc if (EC)
1185*0a6a1f1dSLionel Sambuc return *this;
1186*0a6a1f1dSLionel Sambuc if (I != End) {
1187*0a6a1f1dSLionel Sambuc State->push(I);
1188*0a6a1f1dSLionel Sambuc return *this;
1189*0a6a1f1dSLionel Sambuc }
1190*0a6a1f1dSLionel Sambuc }
1191*0a6a1f1dSLionel Sambuc
1192*0a6a1f1dSLionel Sambuc while (!State->empty() && State->top().increment(EC) == End)
1193*0a6a1f1dSLionel Sambuc State->pop();
1194*0a6a1f1dSLionel Sambuc
1195*0a6a1f1dSLionel Sambuc if (State->empty())
1196*0a6a1f1dSLionel Sambuc State.reset(); // end iterator
1197*0a6a1f1dSLionel Sambuc
1198*0a6a1f1dSLionel Sambuc return *this;
1199*0a6a1f1dSLionel Sambuc }
1200