blob: f6d88c1860d10263f39a3845a24ce1caecf32073 [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 Langmuirc8130a72014-02-20 21:59:23 +000014#include "llvm/ADT/OwningPtr.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000015#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/StringExtras.h"
17#include "llvm/Support/Atomic.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000018#include "llvm/Support/MemoryBuffer.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000019#include "llvm/Support/Path.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000020#include "llvm/Support/YAMLParser.h"
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,
68 OwningPtr<MemoryBuffer> &Result,
69 int64_t FileSize,
70 bool RequiresNullTerminator) {
71 llvm::OwningPtr<File> F;
72 if (error_code EC = openFileForRead(Name, F))
73 return EC;
74
75 error_code EC = F->getBuffer(Name, Result, FileSize, RequiresNullTerminator);
76 return EC;
77}
78
79//===-----------------------------------------------------------------------===/
80// RealFileSystem implementation
81//===-----------------------------------------------------------------------===/
82
83/// \brief Wrapper around a raw file descriptor.
84class RealFile : public File {
85 int FD;
86 friend class RealFileSystem;
87 RealFile(int FD) : FD(FD) {
88 assert(FD >= 0 && "Invalid or inactive file descriptor");
89 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +000090
Ben Langmuirc8130a72014-02-20 21:59:23 +000091public:
92 ~RealFile();
93 ErrorOr<Status> status() LLVM_OVERRIDE;
94 error_code getBuffer(const Twine &Name, OwningPtr<MemoryBuffer> &Result,
95 int64_t FileSize = -1,
96 bool RequiresNullTerminator = true) LLVM_OVERRIDE;
97 error_code close() LLVM_OVERRIDE;
98};
Ben Langmuird51ba0b2014-02-21 23:39:37 +000099RealFile::~RealFile() { close(); }
Ben Langmuirc8130a72014-02-20 21:59:23 +0000100
101ErrorOr<Status> RealFile::status() {
102 assert(FD != -1 && "cannot stat closed file");
103 file_status RealStatus;
104 if (error_code EC = sys::fs::status(FD, RealStatus))
105 return EC;
106 return Status(RealStatus);
107}
108
109error_code RealFile::getBuffer(const Twine &Name,
110 OwningPtr<MemoryBuffer> &Result,
111 int64_t FileSize, bool RequiresNullTerminator) {
112 assert(FD != -1 && "cannot get buffer for closed file");
113 return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
114 RequiresNullTerminator);
115}
116
117// FIXME: This is terrible, we need this for ::close.
118#if !defined(_MSC_VER) && !defined(__MINGW32__)
119#include <unistd.h>
120#include <sys/uio.h>
121#else
122#include <io.h>
123#ifndef S_ISFIFO
124#define S_ISFIFO(x) (0)
125#endif
126#endif
127error_code RealFile::close() {
128 if (::close(FD))
129 return error_code(errno, system_category());
130 FD = -1;
131 return error_code::success();
132}
133
134/// \brief The file system according to your operating system.
135class RealFileSystem : public FileSystem {
136public:
137 ErrorOr<Status> status(const Twine &Path) LLVM_OVERRIDE;
138 error_code openFileForRead(const Twine &Path,
139 OwningPtr<File> &Result) LLVM_OVERRIDE;
140};
141
142ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
143 sys::fs::file_status RealStatus;
144 if (error_code EC = sys::fs::status(Path, RealStatus))
145 return EC;
146 Status Result(RealStatus);
147 Result.setName(Path.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000148 return Result;
149}
150
151error_code RealFileSystem::openFileForRead(const Twine &Name,
152 OwningPtr<File> &Result) {
153 int FD;
154 if (error_code EC = sys::fs::openFileForRead(Name, FD))
155 return EC;
156 Result.reset(new RealFile(FD));
157 return error_code::success();
158}
159
160IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
161 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
162 return FS;
163}
164
165//===-----------------------------------------------------------------------===/
166// OverlayFileSystem implementation
167//===-----------------------------------------------------------------------===/
168OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
169 pushOverlay(BaseFS);
170}
171
172void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
173 FSList.push_back(FS);
174}
175
176ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
177 // FIXME: handle symlinks that cross file systems
178 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
179 ErrorOr<Status> Status = (*I)->status(Path);
180 if (Status || Status.getError() != errc::no_such_file_or_directory)
181 return Status;
182 }
183 return error_code(errc::no_such_file_or_directory, system_category());
184}
185
186error_code OverlayFileSystem::openFileForRead(const llvm::Twine &Path,
187 OwningPtr<File> &Result) {
188 // FIXME: handle symlinks that cross file systems
189 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
190 error_code EC = (*I)->openFileForRead(Path, Result);
191 if (!EC || EC != errc::no_such_file_or_directory)
192 return EC;
193 }
194 return error_code(errc::no_such_file_or_directory, system_category());
195}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000196
197//===-----------------------------------------------------------------------===/
198// VFSFromYAML implementation
199//===-----------------------------------------------------------------------===/
200
201// Allow DenseMap<StringRef, ...>. This is useful below because we know all the
202// strings are literals and will outlive the map, and there is no reason to
203// store them.
204namespace llvm {
205 template<>
206 struct DenseMapInfo<StringRef> {
207 // This assumes that "" will never be a valid key.
208 static inline StringRef getEmptyKey() { return StringRef(""); }
209 static inline StringRef getTombstoneKey() { return StringRef(); }
210 static unsigned getHashValue(StringRef Val) { return HashString(Val); }
211 static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
212 };
213}
214
215namespace {
216
217enum EntryKind {
218 EK_Directory,
219 EK_File
220};
221
222/// \brief A single file or directory in the VFS.
223class Entry {
224 EntryKind Kind;
225 std::string Name;
226
227public:
228 virtual ~Entry();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000229 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
230 StringRef getName() const { return Name; }
231 EntryKind getKind() const { return Kind; }
232};
233
234class DirectoryEntry : public Entry {
235 std::vector<Entry *> Contents;
236 Status S;
237
238public:
239 virtual ~DirectoryEntry();
240#if LLVM_HAS_RVALUE_REFERENCES
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000241 DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
242 : Entry(EK_Directory, Name), Contents(std::move(Contents)),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000243 S(std::move(S)) {}
244#endif
245 DirectoryEntry(StringRef Name, ArrayRef<Entry *> Contents, const Status &S)
246 : Entry(EK_Directory, Name), Contents(Contents), S(S) {}
247 Status getStatus() { return S; }
248 typedef std::vector<Entry *>::iterator iterator;
249 iterator contents_begin() { return Contents.begin(); }
250 iterator contents_end() { return Contents.end(); }
251 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
252};
253
254class FileEntry : public Entry {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000255public:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000256 enum NameKind {
257 NK_NotSet,
258 NK_External,
259 NK_Virtual
260 };
261private:
262 std::string ExternalContentsPath;
263 NameKind UseName;
264public:
265 FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
266 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
267 UseName(UseName) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000268 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
Ben Langmuirb59cf672014-02-27 00:25:12 +0000269 /// \brief whether to use the external path as the name for this file.
270 NameKind useName() const { return UseName; }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000271 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
272};
273
274/// \brief A virtual file system parsed from a YAML file.
275///
276/// Currently, this class allows creating virtual directories and mapping
277/// virtual file paths to existing external files, available in \c ExternalFS.
278///
279/// The basic structure of the parsed file is:
280/// \verbatim
281/// {
282/// 'version': <version number>,
283/// <optional configuration>
284/// 'roots': [
285/// <directory entries>
286/// ]
287/// }
288/// \endverbatim
289///
290/// All configuration options are optional.
291/// 'case-sensitive': <boolean, default=true>
Ben Langmuirb59cf672014-02-27 00:25:12 +0000292/// 'use-external-names': <boolean, default=true>
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000293///
294/// Virtual directories are represented as
295/// \verbatim
296/// {
297/// 'type': 'directory',
298/// 'name': <string>,
299/// 'contents': [ <file or directory entries> ]
300/// }
301/// \endverbatim
302///
303/// The default attributes for virtual directories are:
304/// \verbatim
305/// MTime = now() when created
306/// Perms = 0777
307/// User = Group = 0
308/// Size = 0
309/// UniqueID = unspecified unique value
310/// \endverbatim
311///
312/// Re-mapped files are represented as
313/// \verbatim
314/// {
315/// 'type': 'file',
316/// 'name': <string>,
Ben Langmuirb59cf672014-02-27 00:25:12 +0000317/// 'use-external-name': <boolean> # Optional
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000318/// 'external-contents': <path to external file>)
319/// }
320/// \endverbatim
321///
322/// and inherit their attributes from the external contents.
323///
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000324/// In both cases, the 'name' field may contain multiple path components (e.g.
325/// /path/to/file). However, any directory that contains more than one child
326/// must be uniquely represented by a directory entry.
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000327class VFSFromYAML : public vfs::FileSystem {
328 std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
329 /// \brief The file system to use for external references.
330 IntrusiveRefCntPtr<FileSystem> ExternalFS;
331
332 /// @name Configuration
333 /// @{
334
335 /// \brief Whether to perform case-sensitive comparisons.
336 ///
337 /// Currently, case-insensitive matching only works correctly with ASCII.
Ben Langmuirb59cf672014-02-27 00:25:12 +0000338 bool CaseSensitive;
339
340 /// \brief Whether to use to use the value of 'external-contents' for the
341 /// names of files. This global value is overridable on a per-file basis.
342 bool UseExternalNames;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000343 /// @}
344
345 friend class VFSFromYAMLParser;
346
347private:
348 VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
Ben Langmuirb59cf672014-02-27 00:25:12 +0000349 : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000350
351 /// \brief Looks up \p Path in \c Roots.
352 ErrorOr<Entry *> lookupPath(const Twine &Path);
353
354 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
355 /// recursing into the contents of \p From if it is a directory.
356 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
357 sys::path::const_iterator End, Entry *From);
358
359public:
360 ~VFSFromYAML();
361
362 /// \brief Parses \p Buffer, which is expected to be in YAML format and
363 /// returns a virtual file system representing its contents.
364 ///
365 /// Takes ownership of \p Buffer.
366 static VFSFromYAML *create(MemoryBuffer *Buffer,
367 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000368 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000369 IntrusiveRefCntPtr<FileSystem> ExternalFS);
370
371 ErrorOr<Status> status(const Twine &Path) LLVM_OVERRIDE;
372 error_code openFileForRead(const Twine &Path,
373 OwningPtr<File> &Result) LLVM_OVERRIDE;
374};
375
376/// \brief A helper class to hold the common YAML parsing state.
377class VFSFromYAMLParser {
378 yaml::Stream &Stream;
379
380 void error(yaml::Node *N, const Twine &Msg) {
381 Stream.printError(N, Msg);
382 }
383
384 // false on error
385 bool parseScalarString(yaml::Node *N, StringRef &Result,
386 SmallVectorImpl<char> &Storage) {
387 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
388 if (!S) {
389 error(N, "expected string");
390 return false;
391 }
392 Result = S->getValue(Storage);
393 return true;
394 }
395
396 // false on error
397 bool parseScalarBool(yaml::Node *N, bool &Result) {
398 SmallString<5> Storage;
399 StringRef Value;
400 if (!parseScalarString(N, Value, Storage))
401 return false;
402
403 if (Value.equals_lower("true") || Value.equals_lower("on") ||
404 Value.equals_lower("yes") || Value == "1") {
405 Result = true;
406 return true;
407 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
408 Value.equals_lower("no") || Value == "0") {
409 Result = false;
410 return true;
411 }
412
413 error(N, "expected boolean value");
414 return false;
415 }
416
417 struct KeyStatus {
418 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
419 bool Required;
420 bool Seen;
421 };
422 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
423
424 // false on error
425 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
426 DenseMap<StringRef, KeyStatus> &Keys) {
427 if (!Keys.count(Key)) {
428 error(KeyNode, "unknown key");
429 return false;
430 }
431 KeyStatus &S = Keys[Key];
432 if (S.Seen) {
433 error(KeyNode, Twine("duplicate key '") + Key + "'");
434 return false;
435 }
436 S.Seen = true;
437 return true;
438 }
439
440 // false on error
441 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
442 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
443 E = Keys.end();
444 I != E; ++I) {
445 if (I->second.Required && !I->second.Seen) {
446 error(Obj, Twine("missing key '") + I->first + "'");
447 return false;
448 }
449 }
450 return true;
451 }
452
453 Entry *parseEntry(yaml::Node *N) {
454 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
455 if (!M) {
456 error(N, "expected mapping node for file or directory entry");
457 return NULL;
458 }
459
460 KeyStatusPair Fields[] = {
461 KeyStatusPair("name", true),
462 KeyStatusPair("type", true),
463 KeyStatusPair("contents", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000464 KeyStatusPair("external-contents", false),
465 KeyStatusPair("use-external-name", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000466 };
467
468 DenseMap<StringRef, KeyStatus> Keys(
469 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
470
471 bool HasContents = false; // external or otherwise
472 std::vector<Entry *> EntryArrayContents;
473 std::string ExternalContentsPath;
474 std::string Name;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000475 FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000476 EntryKind Kind;
477
478 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
479 ++I) {
480 StringRef Key;
481 // Reuse the buffer for key and value, since we don't look at key after
482 // parsing value.
483 SmallString<256> Buffer;
484 if (!parseScalarString(I->getKey(), Key, Buffer))
485 return NULL;
486
487 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
488 return NULL;
489
490 StringRef Value;
491 if (Key == "name") {
492 if (!parseScalarString(I->getValue(), Value, Buffer))
493 return NULL;
494 Name = Value;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000495 } else if (Key == "type") {
496 if (!parseScalarString(I->getValue(), Value, Buffer))
497 return NULL;
498 if (Value == "file")
499 Kind = EK_File;
500 else if (Value == "directory")
501 Kind = EK_Directory;
502 else {
503 error(I->getValue(), "unknown value for 'type'");
504 return NULL;
505 }
506 } else if (Key == "contents") {
507 if (HasContents) {
508 error(I->getKey(),
509 "entry already has 'contents' or 'external-contents'");
510 return NULL;
511 }
512 HasContents = true;
513 yaml::SequenceNode *Contents =
514 dyn_cast<yaml::SequenceNode>(I->getValue());
515 if (!Contents) {
516 // FIXME: this is only for directories, what about files?
517 error(I->getValue(), "expected array");
518 return NULL;
519 }
520
521 for (yaml::SequenceNode::iterator I = Contents->begin(),
522 E = Contents->end();
523 I != E; ++I) {
524 if (Entry *E = parseEntry(&*I))
525 EntryArrayContents.push_back(E);
526 else
527 return NULL;
528 }
529 } else if (Key == "external-contents") {
530 if (HasContents) {
531 error(I->getKey(),
532 "entry already has 'contents' or 'external-contents'");
533 return NULL;
534 }
535 HasContents = true;
536 if (!parseScalarString(I->getValue(), Value, Buffer))
537 return NULL;
538 ExternalContentsPath = Value;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000539 } else if (Key == "use-external-name") {
540 bool Val;
541 if (!parseScalarBool(I->getValue(), Val))
542 return NULL;
543 UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000544 } else {
545 llvm_unreachable("key missing from Keys");
546 }
547 }
548
549 if (Stream.failed())
550 return NULL;
551
552 // check for missing keys
553 if (!HasContents) {
554 error(N, "missing key 'contents' or 'external-contents'");
555 return NULL;
556 }
557 if (!checkMissingKeys(N, Keys))
558 return NULL;
559
Ben Langmuirb59cf672014-02-27 00:25:12 +0000560 // check invalid configuration
561 if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
562 error(N, "'use-external-name' is not supported for directories");
563 return NULL;
564 }
565
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000566 // Remove trailing slash(es)
567 StringRef Trimmed(Name);
568 while (Trimmed.size() > 1 && sys::path::is_separator(Trimmed.back()))
569 Trimmed = Trimmed.slice(0, Trimmed.size()-1);
570 // Get the last component
571 StringRef LastComponent = sys::path::filename(Trimmed);
572
573 Entry *Result = 0;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000574 switch (Kind) {
575 case EK_File:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000576 Result = new FileEntry(LastComponent, llvm_move(ExternalContentsPath),
577 UseExternalName);
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000578 break;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000579 case EK_Directory:
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000580 Result = new DirectoryEntry(LastComponent, llvm_move(EntryArrayContents),
581 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
582 0, file_type::directory_file, sys::fs::all_all));
583 break;
584 }
585
586 StringRef Parent = sys::path::parent_path(Trimmed);
587 if (Parent.empty())
588 return Result;
589
590 // if 'name' contains multiple components, create implicit directory entries
591 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
592 E = sys::path::rend(Parent);
593 I != E; ++I) {
594 Result = new DirectoryEntry(*I, Result,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000595 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
596 0, file_type::directory_file, sys::fs::all_all));
597 }
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000598 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000599 }
600
601public:
602 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
603
604 // false on error
605 bool parse(yaml::Node *Root, VFSFromYAML *FS) {
606 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
607 if (!Top) {
608 error(Root, "expected mapping node");
609 return false;
610 }
611
612 KeyStatusPair Fields[] = {
613 KeyStatusPair("version", true),
614 KeyStatusPair("case-sensitive", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000615 KeyStatusPair("use-external-names", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000616 KeyStatusPair("roots", true),
617 };
618
619 DenseMap<StringRef, KeyStatus> Keys(
620 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
621
622 // Parse configuration and 'roots'
623 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
624 ++I) {
625 SmallString<10> KeyBuffer;
626 StringRef Key;
627 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
628 return false;
629
630 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
631 return false;
632
633 if (Key == "roots") {
634 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
635 if (!Roots) {
636 error(I->getValue(), "expected array");
637 return false;
638 }
639
640 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
641 I != E; ++I) {
642 if (Entry *E = parseEntry(&*I))
643 FS->Roots.push_back(E);
644 else
645 return false;
646 }
647 } else if (Key == "version") {
648 StringRef VersionString;
649 SmallString<4> Storage;
650 if (!parseScalarString(I->getValue(), VersionString, Storage))
651 return false;
652 int Version;
653 if (VersionString.getAsInteger<int>(10, Version)) {
654 error(I->getValue(), "expected integer");
655 return false;
656 }
657 if (Version < 0) {
658 error(I->getValue(), "invalid version number");
659 return false;
660 }
661 if (Version != 0) {
662 error(I->getValue(), "version mismatch, expected 0");
663 return false;
664 }
665 } else if (Key == "case-sensitive") {
666 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
667 return false;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000668 } else if (Key == "use-external-names") {
669 if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
670 return false;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000671 } else {
672 llvm_unreachable("key missing from Keys");
673 }
674 }
675
676 if (Stream.failed())
677 return false;
678
679 if (!checkMissingKeys(Top, Keys))
680 return false;
681 return true;
682 }
683};
684} // end of anonymous namespace
685
686Entry::~Entry() {}
687DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
688
689VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
690
691VFSFromYAML *VFSFromYAML::create(MemoryBuffer *Buffer,
692 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000693 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000694 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
695
696 SourceMgr SM;
697 yaml::Stream Stream(Buffer, SM);
698
Ben Langmuir97882e72014-02-24 20:56:37 +0000699 SM.setDiagHandler(DiagHandler, DiagContext);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000700 yaml::document_iterator DI = Stream.begin();
701 yaml::Node *Root = DI->getRoot();
702 if (DI == Stream.end() || !Root) {
703 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
704 return NULL;
705 }
706
707 VFSFromYAMLParser P(Stream);
708
709 OwningPtr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
710 if (!P.parse(Root, FS.get()))
711 return NULL;
712
713 return FS.take();
714}
715
716ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
717 SmallVector<char, 256> Storage;
718 StringRef Path = Path_.toNullTerminatedStringRef(Storage);
719
720 if (Path.empty())
721 return error_code(errc::invalid_argument, system_category());
722
723 sys::path::const_iterator Start = sys::path::begin(Path);
724 sys::path::const_iterator End = sys::path::end(Path);
725 for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
726 I != E; ++I) {
727 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
728 if (Result || Result.getError() != errc::no_such_file_or_directory)
729 return Result;
730 }
731 return error_code(errc::no_such_file_or_directory, system_category());
732}
733
734ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
735 sys::path::const_iterator End,
736 Entry *From) {
737 // FIXME: handle . and ..
738 if (CaseSensitive ? !Start->equals(From->getName())
739 : !Start->equals_lower(From->getName()))
740 // failure to match
741 return error_code(errc::no_such_file_or_directory, system_category());
742
743 ++Start;
744
745 if (Start == End) {
746 // Match!
747 return From;
748 }
749
750 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
751 if (!DE)
752 return error_code(errc::not_a_directory, system_category());
753
754 for (DirectoryEntry::iterator I = DE->contents_begin(),
755 E = DE->contents_end();
756 I != E; ++I) {
757 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
758 if (Result || Result.getError() != errc::no_such_file_or_directory)
759 return Result;
760 }
761 return error_code(errc::no_such_file_or_directory, system_category());
762}
763
764ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
765 ErrorOr<Entry *> Result = lookupPath(Path);
766 if (!Result)
767 return Result.getError();
768
769 std::string PathStr(Path.str());
770 if (FileEntry *F = dyn_cast<FileEntry>(*Result)) {
771 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
Ben Langmuirb59cf672014-02-27 00:25:12 +0000772 assert(!S || S->getName() == F->getExternalContentsPath());
773 if (S && (F->useName() == FileEntry::NK_Virtual ||
774 (F->useName() == FileEntry::NK_NotSet && !UseExternalNames)))
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000775 S->setName(PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000776 return S;
777 } else { // directory
778 DirectoryEntry *DE = cast<DirectoryEntry>(*Result);
779 Status S = DE->getStatus();
780 S.setName(PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000781 return S;
782 }
783}
784
785error_code VFSFromYAML::openFileForRead(const Twine &Path,
786 OwningPtr<vfs::File> &Result) {
787 ErrorOr<Entry *> E = lookupPath(Path);
788 if (!E)
789 return E.getError();
790
791 FileEntry *F = dyn_cast<FileEntry>(*E);
792 if (!F) // FIXME: errc::not_a_file?
793 return error_code(errc::invalid_argument, system_category());
794
Ben Langmuir801272a2014-02-25 18:23:47 +0000795 return ExternalFS->openFileForRead(F->getExternalContentsPath(), Result);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000796}
797
798IntrusiveRefCntPtr<FileSystem>
799vfs::getVFSFromYAML(MemoryBuffer *Buffer, SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000800 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000801 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Ben Langmuir97882e72014-02-24 20:56:37 +0000802 return VFSFromYAML::create(Buffer, DiagHandler, DiagContext, ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000803}
804
805UniqueID vfs::getNextVirtualUniqueID() {
806 static volatile sys::cas_flag UID = 0;
807 sys::cas_flag ID = llvm::sys::AtomicIncrement(&UID);
808 // The following assumes that uint64_t max will never collide with a real
809 // dev_t value from the OS.
810 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
811}