blob: fae6a35a92a71980c1f5e2e0a237745b47a4e6f5 [file] [log] [blame]
Ben Langmuirc8130a72014-02-20 21:59:23 +00001//===- VirtualFileSystem.cpp - Virtual File System Layer --------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9// This file implements the VirtualFileSystem interface.
10//===----------------------------------------------------------------------===//
11
12#include "clang/Basic/VirtualFileSystem.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000013#include "llvm/ADT/DenseMap.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000014#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/StringExtras.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000016#include "llvm/Support/MemoryBuffer.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000017#include "llvm/Support/Path.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000018#include "llvm/Support/YAMLParser.h"
Benjamin Kramer4527fb22014-03-02 17:08:31 +000019#include <atomic>
Ahmed Charlesdfca6f92014-03-09 11:36:40 +000020#include <memory>
Ben Langmuirc8130a72014-02-20 21:59:23 +000021
22using namespace clang;
23using namespace clang::vfs;
24using namespace llvm;
25using llvm::sys::fs::file_status;
26using llvm::sys::fs::file_type;
27using llvm::sys::fs::perms;
28using llvm::sys::fs::UniqueID;
29
30Status::Status(const file_status &Status)
31 : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
32 User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
33 Type(Status.type()), Perms(Status.permissions()) {}
34
35Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
36 sys::TimeValue MTime, uint32_t User, uint32_t Group,
37 uint64_t Size, file_type Type, perms Perms)
Ben Langmuirb59cf672014-02-27 00:25:12 +000038 : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
39 Type(Type), Perms(Perms) {}
Ben Langmuirc8130a72014-02-20 21:59:23 +000040
41bool Status::equivalent(const Status &Other) const {
42 return getUniqueID() == Other.getUniqueID();
43}
44bool Status::isDirectory() const {
45 return Type == file_type::directory_file;
46}
47bool Status::isRegularFile() const {
48 return Type == file_type::regular_file;
49}
50bool Status::isOther() const {
51 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
52}
53bool Status::isSymlink() const {
54 return Type == file_type::symlink_file;
55}
56bool Status::isStatusKnown() const {
57 return Type != file_type::status_error;
58}
59bool Status::exists() const {
60 return isStatusKnown() && Type != file_type::file_not_found;
61}
62
63File::~File() {}
64
65FileSystem::~FileSystem() {}
66
67error_code FileSystem::getBufferForFile(const llvm::Twine &Name,
Ahmed Charlesb8984322014-03-07 20:03:18 +000068 std::unique_ptr<MemoryBuffer> &Result,
Ben Langmuirc8130a72014-02-20 21:59:23 +000069 int64_t FileSize,
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +000070 bool RequiresNullTerminator,
71 bool IsVolatile) {
Ahmed Charlesb8984322014-03-07 20:03:18 +000072 std::unique_ptr<File> F;
Ben Langmuirc8130a72014-02-20 21:59:23 +000073 if (error_code EC = openFileForRead(Name, F))
74 return EC;
75
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +000076 error_code EC = F->getBuffer(Name, Result, FileSize, RequiresNullTerminator,
77 IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +000078 return EC;
79}
80
81//===-----------------------------------------------------------------------===/
82// RealFileSystem implementation
83//===-----------------------------------------------------------------------===/
84
Benjamin Kramer3d6220d2014-03-01 17:21:22 +000085namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +000086/// \brief Wrapper around a raw file descriptor.
87class RealFile : public File {
88 int FD;
Ben Langmuird066d4c2014-02-28 21:16:07 +000089 Status S;
Ben Langmuirc8130a72014-02-20 21:59:23 +000090 friend class RealFileSystem;
91 RealFile(int FD) : FD(FD) {
92 assert(FD >= 0 && "Invalid or inactive file descriptor");
93 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +000094
Ben Langmuirc8130a72014-02-20 21:59:23 +000095public:
96 ~RealFile();
Craig Toppera798a9d2014-03-02 09:32:10 +000097 ErrorOr<Status> status() override;
Ahmed Charlesb8984322014-03-07 20:03:18 +000098 error_code getBuffer(const Twine &Name, std::unique_ptr<MemoryBuffer> &Result,
Ben Langmuirc8130a72014-02-20 21:59:23 +000099 int64_t FileSize = -1,
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +0000100 bool RequiresNullTerminator = true,
101 bool IsVolatile = false) override;
Craig Toppera798a9d2014-03-02 09:32:10 +0000102 error_code close() override;
103 void setName(StringRef Name) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000104};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000105} // end anonymous namespace
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000106RealFile::~RealFile() { close(); }
Ben Langmuirc8130a72014-02-20 21:59:23 +0000107
108ErrorOr<Status> RealFile::status() {
109 assert(FD != -1 && "cannot stat closed file");
Ben Langmuird066d4c2014-02-28 21:16:07 +0000110 if (!S.isStatusKnown()) {
111 file_status RealStatus;
112 if (error_code EC = sys::fs::status(FD, RealStatus))
113 return EC;
114 Status NewS(RealStatus);
115 NewS.setName(S.getName());
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000116 S = std::move(NewS);
Ben Langmuird066d4c2014-02-28 21:16:07 +0000117 }
118 return S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000119}
120
121error_code RealFile::getBuffer(const Twine &Name,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000122 std::unique_ptr<MemoryBuffer> &Result,
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +0000123 int64_t FileSize, bool RequiresNullTerminator,
124 bool IsVolatile) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000125 assert(FD != -1 && "cannot get buffer for closed file");
126 return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +0000127 RequiresNullTerminator, IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000128}
129
130// FIXME: This is terrible, we need this for ::close.
131#if !defined(_MSC_VER) && !defined(__MINGW32__)
132#include <unistd.h>
133#include <sys/uio.h>
134#else
135#include <io.h>
136#ifndef S_ISFIFO
137#define S_ISFIFO(x) (0)
138#endif
139#endif
140error_code RealFile::close() {
141 if (::close(FD))
142 return error_code(errno, system_category());
143 FD = -1;
144 return error_code::success();
145}
146
Ben Langmuird066d4c2014-02-28 21:16:07 +0000147void RealFile::setName(StringRef Name) {
148 S.setName(Name);
149}
150
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000151namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000152/// \brief The file system according to your operating system.
153class RealFileSystem : public FileSystem {
154public:
Craig Toppera798a9d2014-03-02 09:32:10 +0000155 ErrorOr<Status> status(const Twine &Path) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000156 error_code openFileForRead(const Twine &Path,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000157 std::unique_ptr<File> &Result) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000158};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000159} // end anonymous namespace
Ben Langmuirc8130a72014-02-20 21:59:23 +0000160
161ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
162 sys::fs::file_status RealStatus;
163 if (error_code EC = sys::fs::status(Path, RealStatus))
164 return EC;
165 Status Result(RealStatus);
166 Result.setName(Path.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000167 return Result;
168}
169
170error_code RealFileSystem::openFileForRead(const Twine &Name,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000171 std::unique_ptr<File> &Result) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000172 int FD;
173 if (error_code EC = sys::fs::openFileForRead(Name, FD))
174 return EC;
175 Result.reset(new RealFile(FD));
Ben Langmuird066d4c2014-02-28 21:16:07 +0000176 Result->setName(Name.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000177 return error_code::success();
178}
179
180IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
181 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
182 return FS;
183}
184
185//===-----------------------------------------------------------------------===/
186// OverlayFileSystem implementation
187//===-----------------------------------------------------------------------===/
188OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
189 pushOverlay(BaseFS);
190}
191
192void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
193 FSList.push_back(FS);
194}
195
196ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
197 // FIXME: handle symlinks that cross file systems
198 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
199 ErrorOr<Status> Status = (*I)->status(Path);
200 if (Status || Status.getError() != errc::no_such_file_or_directory)
201 return Status;
202 }
203 return error_code(errc::no_such_file_or_directory, system_category());
204}
205
206error_code OverlayFileSystem::openFileForRead(const llvm::Twine &Path,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000207 std::unique_ptr<File> &Result) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000208 // FIXME: handle symlinks that cross file systems
209 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
210 error_code EC = (*I)->openFileForRead(Path, Result);
211 if (!EC || EC != errc::no_such_file_or_directory)
212 return EC;
213 }
214 return error_code(errc::no_such_file_or_directory, system_category());
215}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000216
217//===-----------------------------------------------------------------------===/
218// VFSFromYAML implementation
219//===-----------------------------------------------------------------------===/
220
221// Allow DenseMap<StringRef, ...>. This is useful below because we know all the
222// strings are literals and will outlive the map, and there is no reason to
223// store them.
224namespace llvm {
225 template<>
226 struct DenseMapInfo<StringRef> {
227 // This assumes that "" will never be a valid key.
228 static inline StringRef getEmptyKey() { return StringRef(""); }
229 static inline StringRef getTombstoneKey() { return StringRef(); }
230 static unsigned getHashValue(StringRef Val) { return HashString(Val); }
231 static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
232 };
233}
234
235namespace {
236
237enum EntryKind {
238 EK_Directory,
239 EK_File
240};
241
242/// \brief A single file or directory in the VFS.
243class Entry {
244 EntryKind Kind;
245 std::string Name;
246
247public:
248 virtual ~Entry();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000249 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
250 StringRef getName() const { return Name; }
251 EntryKind getKind() const { return Kind; }
252};
253
254class DirectoryEntry : public Entry {
255 std::vector<Entry *> Contents;
256 Status S;
257
258public:
259 virtual ~DirectoryEntry();
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000260 DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
261 : Entry(EK_Directory, Name), Contents(std::move(Contents)),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000262 S(std::move(S)) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000263 Status getStatus() { return S; }
264 typedef std::vector<Entry *>::iterator iterator;
265 iterator contents_begin() { return Contents.begin(); }
266 iterator contents_end() { return Contents.end(); }
267 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
268};
269
270class FileEntry : public Entry {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000271public:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000272 enum NameKind {
273 NK_NotSet,
274 NK_External,
275 NK_Virtual
276 };
277private:
278 std::string ExternalContentsPath;
279 NameKind UseName;
280public:
281 FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
282 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
283 UseName(UseName) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000284 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
Ben Langmuirb59cf672014-02-27 00:25:12 +0000285 /// \brief whether to use the external path as the name for this file.
Ben Langmuird066d4c2014-02-28 21:16:07 +0000286 bool useExternalName(bool GlobalUseExternalName) const {
287 return UseName == NK_NotSet ? GlobalUseExternalName
288 : (UseName == NK_External);
289 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000290 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
291};
292
293/// \brief A virtual file system parsed from a YAML file.
294///
295/// Currently, this class allows creating virtual directories and mapping
296/// virtual file paths to existing external files, available in \c ExternalFS.
297///
298/// The basic structure of the parsed file is:
299/// \verbatim
300/// {
301/// 'version': <version number>,
302/// <optional configuration>
303/// 'roots': [
304/// <directory entries>
305/// ]
306/// }
307/// \endverbatim
308///
309/// All configuration options are optional.
310/// 'case-sensitive': <boolean, default=true>
Ben Langmuirb59cf672014-02-27 00:25:12 +0000311/// 'use-external-names': <boolean, default=true>
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000312///
313/// Virtual directories are represented as
314/// \verbatim
315/// {
316/// 'type': 'directory',
317/// 'name': <string>,
318/// 'contents': [ <file or directory entries> ]
319/// }
320/// \endverbatim
321///
322/// The default attributes for virtual directories are:
323/// \verbatim
324/// MTime = now() when created
325/// Perms = 0777
326/// User = Group = 0
327/// Size = 0
328/// UniqueID = unspecified unique value
329/// \endverbatim
330///
331/// Re-mapped files are represented as
332/// \verbatim
333/// {
334/// 'type': 'file',
335/// 'name': <string>,
Ben Langmuirb59cf672014-02-27 00:25:12 +0000336/// 'use-external-name': <boolean> # Optional
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000337/// 'external-contents': <path to external file>)
338/// }
339/// \endverbatim
340///
341/// and inherit their attributes from the external contents.
342///
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000343/// In both cases, the 'name' field may contain multiple path components (e.g.
344/// /path/to/file). However, any directory that contains more than one child
345/// must be uniquely represented by a directory entry.
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000346class VFSFromYAML : public vfs::FileSystem {
347 std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
348 /// \brief The file system to use for external references.
349 IntrusiveRefCntPtr<FileSystem> ExternalFS;
350
351 /// @name Configuration
352 /// @{
353
354 /// \brief Whether to perform case-sensitive comparisons.
355 ///
356 /// Currently, case-insensitive matching only works correctly with ASCII.
Ben Langmuirb59cf672014-02-27 00:25:12 +0000357 bool CaseSensitive;
358
359 /// \brief Whether to use to use the value of 'external-contents' for the
360 /// names of files. This global value is overridable on a per-file basis.
361 bool UseExternalNames;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000362 /// @}
363
364 friend class VFSFromYAMLParser;
365
366private:
367 VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
Ben Langmuirb59cf672014-02-27 00:25:12 +0000368 : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000369
370 /// \brief Looks up \p Path in \c Roots.
371 ErrorOr<Entry *> lookupPath(const Twine &Path);
372
373 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
374 /// recursing into the contents of \p From if it is a directory.
375 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
376 sys::path::const_iterator End, Entry *From);
377
378public:
379 ~VFSFromYAML();
380
381 /// \brief Parses \p Buffer, which is expected to be in YAML format and
382 /// returns a virtual file system representing its contents.
383 ///
384 /// Takes ownership of \p Buffer.
385 static VFSFromYAML *create(MemoryBuffer *Buffer,
386 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000387 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000388 IntrusiveRefCntPtr<FileSystem> ExternalFS);
389
Craig Toppera798a9d2014-03-02 09:32:10 +0000390 ErrorOr<Status> status(const Twine &Path) override;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000391 error_code openFileForRead(const Twine &Path,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000392 std::unique_ptr<File> &Result) override;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000393};
394
395/// \brief A helper class to hold the common YAML parsing state.
396class VFSFromYAMLParser {
397 yaml::Stream &Stream;
398
399 void error(yaml::Node *N, const Twine &Msg) {
400 Stream.printError(N, Msg);
401 }
402
403 // false on error
404 bool parseScalarString(yaml::Node *N, StringRef &Result,
405 SmallVectorImpl<char> &Storage) {
406 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
407 if (!S) {
408 error(N, "expected string");
409 return false;
410 }
411 Result = S->getValue(Storage);
412 return true;
413 }
414
415 // false on error
416 bool parseScalarBool(yaml::Node *N, bool &Result) {
417 SmallString<5> Storage;
418 StringRef Value;
419 if (!parseScalarString(N, Value, Storage))
420 return false;
421
422 if (Value.equals_lower("true") || Value.equals_lower("on") ||
423 Value.equals_lower("yes") || Value == "1") {
424 Result = true;
425 return true;
426 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
427 Value.equals_lower("no") || Value == "0") {
428 Result = false;
429 return true;
430 }
431
432 error(N, "expected boolean value");
433 return false;
434 }
435
436 struct KeyStatus {
437 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
438 bool Required;
439 bool Seen;
440 };
441 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
442
443 // false on error
444 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
445 DenseMap<StringRef, KeyStatus> &Keys) {
446 if (!Keys.count(Key)) {
447 error(KeyNode, "unknown key");
448 return false;
449 }
450 KeyStatus &S = Keys[Key];
451 if (S.Seen) {
452 error(KeyNode, Twine("duplicate key '") + Key + "'");
453 return false;
454 }
455 S.Seen = true;
456 return true;
457 }
458
459 // false on error
460 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
461 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
462 E = Keys.end();
463 I != E; ++I) {
464 if (I->second.Required && !I->second.Seen) {
465 error(Obj, Twine("missing key '") + I->first + "'");
466 return false;
467 }
468 }
469 return true;
470 }
471
472 Entry *parseEntry(yaml::Node *N) {
473 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
474 if (!M) {
475 error(N, "expected mapping node for file or directory entry");
Craig Topperf1186c52014-05-08 06:41:40 +0000476 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000477 }
478
479 KeyStatusPair Fields[] = {
480 KeyStatusPair("name", true),
481 KeyStatusPair("type", true),
482 KeyStatusPair("contents", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000483 KeyStatusPair("external-contents", false),
484 KeyStatusPair("use-external-name", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000485 };
486
487 DenseMap<StringRef, KeyStatus> Keys(
488 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
489
490 bool HasContents = false; // external or otherwise
491 std::vector<Entry *> EntryArrayContents;
492 std::string ExternalContentsPath;
493 std::string Name;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000494 FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000495 EntryKind Kind;
496
497 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
498 ++I) {
499 StringRef Key;
500 // Reuse the buffer for key and value, since we don't look at key after
501 // parsing value.
502 SmallString<256> Buffer;
503 if (!parseScalarString(I->getKey(), Key, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000504 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000505
506 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000507 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000508
509 StringRef Value;
510 if (Key == "name") {
511 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000512 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000513 Name = Value;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000514 } else if (Key == "type") {
515 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000516 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000517 if (Value == "file")
518 Kind = EK_File;
519 else if (Value == "directory")
520 Kind = EK_Directory;
521 else {
522 error(I->getValue(), "unknown value for 'type'");
Craig Topperf1186c52014-05-08 06:41:40 +0000523 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000524 }
525 } else if (Key == "contents") {
526 if (HasContents) {
527 error(I->getKey(),
528 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000529 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000530 }
531 HasContents = true;
532 yaml::SequenceNode *Contents =
533 dyn_cast<yaml::SequenceNode>(I->getValue());
534 if (!Contents) {
535 // FIXME: this is only for directories, what about files?
536 error(I->getValue(), "expected array");
Craig Topperf1186c52014-05-08 06:41:40 +0000537 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000538 }
539
540 for (yaml::SequenceNode::iterator I = Contents->begin(),
541 E = Contents->end();
542 I != E; ++I) {
543 if (Entry *E = parseEntry(&*I))
544 EntryArrayContents.push_back(E);
545 else
Craig Topperf1186c52014-05-08 06:41:40 +0000546 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000547 }
548 } else if (Key == "external-contents") {
549 if (HasContents) {
550 error(I->getKey(),
551 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000552 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000553 }
554 HasContents = true;
555 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000556 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000557 ExternalContentsPath = Value;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000558 } else if (Key == "use-external-name") {
559 bool Val;
560 if (!parseScalarBool(I->getValue(), Val))
Craig Topperf1186c52014-05-08 06:41:40 +0000561 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000562 UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000563 } else {
564 llvm_unreachable("key missing from Keys");
565 }
566 }
567
568 if (Stream.failed())
Craig Topperf1186c52014-05-08 06:41:40 +0000569 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000570
571 // check for missing keys
572 if (!HasContents) {
573 error(N, "missing key 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000574 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000575 }
576 if (!checkMissingKeys(N, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000577 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000578
Ben Langmuirb59cf672014-02-27 00:25:12 +0000579 // check invalid configuration
580 if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
581 error(N, "'use-external-name' is not supported for directories");
Craig Topperf1186c52014-05-08 06:41:40 +0000582 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000583 }
584
Ben Langmuir93853232014-03-05 21:32:20 +0000585 // Remove trailing slash(es), being careful not to remove the root path
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000586 StringRef Trimmed(Name);
Ben Langmuir93853232014-03-05 21:32:20 +0000587 size_t RootPathLen = sys::path::root_path(Trimmed).size();
588 while (Trimmed.size() > RootPathLen &&
589 sys::path::is_separator(Trimmed.back()))
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000590 Trimmed = Trimmed.slice(0, Trimmed.size()-1);
591 // Get the last component
592 StringRef LastComponent = sys::path::filename(Trimmed);
593
Craig Topperf1186c52014-05-08 06:41:40 +0000594 Entry *Result = nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000595 switch (Kind) {
596 case EK_File:
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000597 Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000598 UseExternalName);
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000599 break;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000600 case EK_Directory:
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000601 Result = new DirectoryEntry(LastComponent, std::move(EntryArrayContents),
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000602 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
603 0, file_type::directory_file, sys::fs::all_all));
604 break;
605 }
606
607 StringRef Parent = sys::path::parent_path(Trimmed);
608 if (Parent.empty())
609 return Result;
610
611 // if 'name' contains multiple components, create implicit directory entries
612 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
613 E = sys::path::rend(Parent);
614 I != E; ++I) {
Benjamin Kramer3f755aa2014-03-10 17:55:02 +0000615 Result = new DirectoryEntry(*I, llvm::makeArrayRef(Result),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000616 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
617 0, file_type::directory_file, sys::fs::all_all));
618 }
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000619 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000620 }
621
622public:
623 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
624
625 // false on error
626 bool parse(yaml::Node *Root, VFSFromYAML *FS) {
627 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
628 if (!Top) {
629 error(Root, "expected mapping node");
630 return false;
631 }
632
633 KeyStatusPair Fields[] = {
634 KeyStatusPair("version", true),
635 KeyStatusPair("case-sensitive", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000636 KeyStatusPair("use-external-names", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000637 KeyStatusPair("roots", true),
638 };
639
640 DenseMap<StringRef, KeyStatus> Keys(
641 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
642
643 // Parse configuration and 'roots'
644 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
645 ++I) {
646 SmallString<10> KeyBuffer;
647 StringRef Key;
648 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
649 return false;
650
651 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
652 return false;
653
654 if (Key == "roots") {
655 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
656 if (!Roots) {
657 error(I->getValue(), "expected array");
658 return false;
659 }
660
661 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
662 I != E; ++I) {
663 if (Entry *E = parseEntry(&*I))
664 FS->Roots.push_back(E);
665 else
666 return false;
667 }
668 } else if (Key == "version") {
669 StringRef VersionString;
670 SmallString<4> Storage;
671 if (!parseScalarString(I->getValue(), VersionString, Storage))
672 return false;
673 int Version;
674 if (VersionString.getAsInteger<int>(10, Version)) {
675 error(I->getValue(), "expected integer");
676 return false;
677 }
678 if (Version < 0) {
679 error(I->getValue(), "invalid version number");
680 return false;
681 }
682 if (Version != 0) {
683 error(I->getValue(), "version mismatch, expected 0");
684 return false;
685 }
686 } else if (Key == "case-sensitive") {
687 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
688 return false;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000689 } else if (Key == "use-external-names") {
690 if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
691 return false;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000692 } else {
693 llvm_unreachable("key missing from Keys");
694 }
695 }
696
697 if (Stream.failed())
698 return false;
699
700 if (!checkMissingKeys(Top, Keys))
701 return false;
702 return true;
703 }
704};
705} // end of anonymous namespace
706
707Entry::~Entry() {}
708DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
709
710VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
711
712VFSFromYAML *VFSFromYAML::create(MemoryBuffer *Buffer,
713 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000714 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000715 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
716
717 SourceMgr SM;
718 yaml::Stream Stream(Buffer, SM);
719
Ben Langmuir97882e72014-02-24 20:56:37 +0000720 SM.setDiagHandler(DiagHandler, DiagContext);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000721 yaml::document_iterator DI = Stream.begin();
722 yaml::Node *Root = DI->getRoot();
723 if (DI == Stream.end() || !Root) {
724 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
Craig Topperf1186c52014-05-08 06:41:40 +0000725 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000726 }
727
728 VFSFromYAMLParser P(Stream);
729
Ahmed Charlesb8984322014-03-07 20:03:18 +0000730 std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000731 if (!P.parse(Root, FS.get()))
Craig Topperf1186c52014-05-08 06:41:40 +0000732 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000733
Ahmed Charles9a16beb2014-03-07 19:33:25 +0000734 return FS.release();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000735}
736
737ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +0000738 SmallString<256> Path;
739 Path_.toVector(Path);
740
741 // Handle relative paths
742 if (error_code EC = sys::fs::make_absolute(Path))
743 return EC;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000744
745 if (Path.empty())
746 return error_code(errc::invalid_argument, system_category());
747
748 sys::path::const_iterator Start = sys::path::begin(Path);
749 sys::path::const_iterator End = sys::path::end(Path);
750 for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
751 I != E; ++I) {
752 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
753 if (Result || Result.getError() != errc::no_such_file_or_directory)
754 return Result;
755 }
756 return error_code(errc::no_such_file_or_directory, system_category());
757}
758
759ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
760 sys::path::const_iterator End,
761 Entry *From) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +0000762 if (Start->equals("."))
763 ++Start;
764
765 // FIXME: handle ..
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000766 if (CaseSensitive ? !Start->equals(From->getName())
767 : !Start->equals_lower(From->getName()))
768 // failure to match
769 return error_code(errc::no_such_file_or_directory, system_category());
770
771 ++Start;
772
773 if (Start == End) {
774 // Match!
775 return From;
776 }
777
778 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
779 if (!DE)
780 return error_code(errc::not_a_directory, system_category());
781
782 for (DirectoryEntry::iterator I = DE->contents_begin(),
783 E = DE->contents_end();
784 I != E; ++I) {
785 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
786 if (Result || Result.getError() != errc::no_such_file_or_directory)
787 return Result;
788 }
789 return error_code(errc::no_such_file_or_directory, system_category());
790}
791
792ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
793 ErrorOr<Entry *> Result = lookupPath(Path);
794 if (!Result)
795 return Result.getError();
796
797 std::string PathStr(Path.str());
798 if (FileEntry *F = dyn_cast<FileEntry>(*Result)) {
799 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
Ben Langmuirb59cf672014-02-27 00:25:12 +0000800 assert(!S || S->getName() == F->getExternalContentsPath());
Ben Langmuird066d4c2014-02-28 21:16:07 +0000801 if (S && !F->useExternalName(UseExternalNames))
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000802 S->setName(PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000803 return S;
804 } else { // directory
805 DirectoryEntry *DE = cast<DirectoryEntry>(*Result);
806 Status S = DE->getStatus();
807 S.setName(PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000808 return S;
809 }
810}
811
812error_code VFSFromYAML::openFileForRead(const Twine &Path,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000813 std::unique_ptr<vfs::File> &Result) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000814 ErrorOr<Entry *> E = lookupPath(Path);
815 if (!E)
816 return E.getError();
817
818 FileEntry *F = dyn_cast<FileEntry>(*E);
819 if (!F) // FIXME: errc::not_a_file?
820 return error_code(errc::invalid_argument, system_category());
821
Ben Langmuird066d4c2014-02-28 21:16:07 +0000822 if (error_code EC = ExternalFS->openFileForRead(F->getExternalContentsPath(),
823 Result))
824 return EC;
825
826 if (!F->useExternalName(UseExternalNames))
827 Result->setName(Path.str());
828
829 return error_code::success();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000830}
831
832IntrusiveRefCntPtr<FileSystem>
833vfs::getVFSFromYAML(MemoryBuffer *Buffer, SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000834 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000835 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Ben Langmuir97882e72014-02-24 20:56:37 +0000836 return VFSFromYAML::create(Buffer, DiagHandler, DiagContext, ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000837}
838
839UniqueID vfs::getNextVirtualUniqueID() {
Benjamin Kramer4527fb22014-03-02 17:08:31 +0000840 static std::atomic<unsigned> UID;
841 unsigned ID = ++UID;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000842 // The following assumes that uint64_t max will never collide with a real
843 // dev_t value from the OS.
844 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
845}