blob: 539cbb740971f673e462afabc1a85eb52188f728 [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)
38 : Name(Name), ExternalName(ExternalName), UID(UID), MTime(MTime),
39 User(User), Group(Group), Size(Size), Type(Type), Perms(Perms) {}
40
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());
148 Result.setExternalName(Path.str());
149 return Result;
150}
151
152error_code RealFileSystem::openFileForRead(const Twine &Name,
153 OwningPtr<File> &Result) {
154 int FD;
155 if (error_code EC = sys::fs::openFileForRead(Name, FD))
156 return EC;
157 Result.reset(new RealFile(FD));
158 return error_code::success();
159}
160
161IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
162 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
163 return FS;
164}
165
166//===-----------------------------------------------------------------------===/
167// OverlayFileSystem implementation
168//===-----------------------------------------------------------------------===/
169OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
170 pushOverlay(BaseFS);
171}
172
173void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
174 FSList.push_back(FS);
175}
176
177ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
178 // FIXME: handle symlinks that cross file systems
179 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
180 ErrorOr<Status> Status = (*I)->status(Path);
181 if (Status || Status.getError() != errc::no_such_file_or_directory)
182 return Status;
183 }
184 return error_code(errc::no_such_file_or_directory, system_category());
185}
186
187error_code OverlayFileSystem::openFileForRead(const llvm::Twine &Path,
188 OwningPtr<File> &Result) {
189 // FIXME: handle symlinks that cross file systems
190 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
191 error_code EC = (*I)->openFileForRead(Path, Result);
192 if (!EC || EC != errc::no_such_file_or_directory)
193 return EC;
194 }
195 return error_code(errc::no_such_file_or_directory, system_category());
196}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000197
198//===-----------------------------------------------------------------------===/
199// VFSFromYAML implementation
200//===-----------------------------------------------------------------------===/
201
202// Allow DenseMap<StringRef, ...>. This is useful below because we know all the
203// strings are literals and will outlive the map, and there is no reason to
204// store them.
205namespace llvm {
206 template<>
207 struct DenseMapInfo<StringRef> {
208 // This assumes that "" will never be a valid key.
209 static inline StringRef getEmptyKey() { return StringRef(""); }
210 static inline StringRef getTombstoneKey() { return StringRef(); }
211 static unsigned getHashValue(StringRef Val) { return HashString(Val); }
212 static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
213 };
214}
215
216namespace {
217
218enum EntryKind {
219 EK_Directory,
220 EK_File
221};
222
223/// \brief A single file or directory in the VFS.
224class Entry {
225 EntryKind Kind;
226 std::string Name;
227
228public:
229 virtual ~Entry();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000230 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
231 StringRef getName() const { return Name; }
232 EntryKind getKind() const { return Kind; }
233};
234
235class DirectoryEntry : public Entry {
236 std::vector<Entry *> Contents;
237 Status S;
238
239public:
240 virtual ~DirectoryEntry();
241#if LLVM_HAS_RVALUE_REFERENCES
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000242 DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
243 : Entry(EK_Directory, Name), Contents(std::move(Contents)),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000244 S(std::move(S)) {}
245#endif
246 DirectoryEntry(StringRef Name, ArrayRef<Entry *> Contents, const Status &S)
247 : Entry(EK_Directory, Name), Contents(Contents), S(S) {}
248 Status getStatus() { return S; }
249 typedef std::vector<Entry *>::iterator iterator;
250 iterator contents_begin() { return Contents.begin(); }
251 iterator contents_end() { return Contents.end(); }
252 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
253};
254
255class FileEntry : public Entry {
256 std::string ExternalContentsPath;
257
258public:
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000259 FileEntry(StringRef Name, StringRef ExternalContentsPath)
260 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath) {}
261 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
262 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
263};
264
265/// \brief A virtual file system parsed from a YAML file.
266///
267/// Currently, this class allows creating virtual directories and mapping
268/// virtual file paths to existing external files, available in \c ExternalFS.
269///
270/// The basic structure of the parsed file is:
271/// \verbatim
272/// {
273/// 'version': <version number>,
274/// <optional configuration>
275/// 'roots': [
276/// <directory entries>
277/// ]
278/// }
279/// \endverbatim
280///
281/// All configuration options are optional.
282/// 'case-sensitive': <boolean, default=true>
283///
284/// Virtual directories are represented as
285/// \verbatim
286/// {
287/// 'type': 'directory',
288/// 'name': <string>,
289/// 'contents': [ <file or directory entries> ]
290/// }
291/// \endverbatim
292///
293/// The default attributes for virtual directories are:
294/// \verbatim
295/// MTime = now() when created
296/// Perms = 0777
297/// User = Group = 0
298/// Size = 0
299/// UniqueID = unspecified unique value
300/// \endverbatim
301///
302/// Re-mapped files are represented as
303/// \verbatim
304/// {
305/// 'type': 'file',
306/// 'name': <string>,
307/// 'external-contents': <path to external file>)
308/// }
309/// \endverbatim
310///
311/// and inherit their attributes from the external contents.
312///
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000313/// In both cases, the 'name' field may contain multiple path components (e.g.
314/// /path/to/file). However, any directory that contains more than one child
315/// must be uniquely represented by a directory entry.
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000316class VFSFromYAML : public vfs::FileSystem {
317 std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
318 /// \brief The file system to use for external references.
319 IntrusiveRefCntPtr<FileSystem> ExternalFS;
320
321 /// @name Configuration
322 /// @{
323
324 /// \brief Whether to perform case-sensitive comparisons.
325 ///
326 /// Currently, case-insensitive matching only works correctly with ASCII.
327 bool CaseSensitive; ///< Whether to perform case-sensitive comparisons.
328 /// @}
329
330 friend class VFSFromYAMLParser;
331
332private:
333 VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
334 : ExternalFS(ExternalFS), CaseSensitive(true) {}
335
336 /// \brief Looks up \p Path in \c Roots.
337 ErrorOr<Entry *> lookupPath(const Twine &Path);
338
339 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
340 /// recursing into the contents of \p From if it is a directory.
341 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
342 sys::path::const_iterator End, Entry *From);
343
344public:
345 ~VFSFromYAML();
346
347 /// \brief Parses \p Buffer, which is expected to be in YAML format and
348 /// returns a virtual file system representing its contents.
349 ///
350 /// Takes ownership of \p Buffer.
351 static VFSFromYAML *create(MemoryBuffer *Buffer,
352 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000353 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000354 IntrusiveRefCntPtr<FileSystem> ExternalFS);
355
356 ErrorOr<Status> status(const Twine &Path) LLVM_OVERRIDE;
357 error_code openFileForRead(const Twine &Path,
358 OwningPtr<File> &Result) LLVM_OVERRIDE;
359};
360
361/// \brief A helper class to hold the common YAML parsing state.
362class VFSFromYAMLParser {
363 yaml::Stream &Stream;
364
365 void error(yaml::Node *N, const Twine &Msg) {
366 Stream.printError(N, Msg);
367 }
368
369 // false on error
370 bool parseScalarString(yaml::Node *N, StringRef &Result,
371 SmallVectorImpl<char> &Storage) {
372 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
373 if (!S) {
374 error(N, "expected string");
375 return false;
376 }
377 Result = S->getValue(Storage);
378 return true;
379 }
380
381 // false on error
382 bool parseScalarBool(yaml::Node *N, bool &Result) {
383 SmallString<5> Storage;
384 StringRef Value;
385 if (!parseScalarString(N, Value, Storage))
386 return false;
387
388 if (Value.equals_lower("true") || Value.equals_lower("on") ||
389 Value.equals_lower("yes") || Value == "1") {
390 Result = true;
391 return true;
392 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
393 Value.equals_lower("no") || Value == "0") {
394 Result = false;
395 return true;
396 }
397
398 error(N, "expected boolean value");
399 return false;
400 }
401
402 struct KeyStatus {
403 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
404 bool Required;
405 bool Seen;
406 };
407 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
408
409 // false on error
410 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
411 DenseMap<StringRef, KeyStatus> &Keys) {
412 if (!Keys.count(Key)) {
413 error(KeyNode, "unknown key");
414 return false;
415 }
416 KeyStatus &S = Keys[Key];
417 if (S.Seen) {
418 error(KeyNode, Twine("duplicate key '") + Key + "'");
419 return false;
420 }
421 S.Seen = true;
422 return true;
423 }
424
425 // false on error
426 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
427 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
428 E = Keys.end();
429 I != E; ++I) {
430 if (I->second.Required && !I->second.Seen) {
431 error(Obj, Twine("missing key '") + I->first + "'");
432 return false;
433 }
434 }
435 return true;
436 }
437
438 Entry *parseEntry(yaml::Node *N) {
439 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
440 if (!M) {
441 error(N, "expected mapping node for file or directory entry");
442 return NULL;
443 }
444
445 KeyStatusPair Fields[] = {
446 KeyStatusPair("name", true),
447 KeyStatusPair("type", true),
448 KeyStatusPair("contents", false),
449 KeyStatusPair("external-contents", false)
450 };
451
452 DenseMap<StringRef, KeyStatus> Keys(
453 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
454
455 bool HasContents = false; // external or otherwise
456 std::vector<Entry *> EntryArrayContents;
457 std::string ExternalContentsPath;
458 std::string Name;
459 EntryKind Kind;
460
461 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
462 ++I) {
463 StringRef Key;
464 // Reuse the buffer for key and value, since we don't look at key after
465 // parsing value.
466 SmallString<256> Buffer;
467 if (!parseScalarString(I->getKey(), Key, Buffer))
468 return NULL;
469
470 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
471 return NULL;
472
473 StringRef Value;
474 if (Key == "name") {
475 if (!parseScalarString(I->getValue(), Value, Buffer))
476 return NULL;
477 Name = Value;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000478 } else if (Key == "type") {
479 if (!parseScalarString(I->getValue(), Value, Buffer))
480 return NULL;
481 if (Value == "file")
482 Kind = EK_File;
483 else if (Value == "directory")
484 Kind = EK_Directory;
485 else {
486 error(I->getValue(), "unknown value for 'type'");
487 return NULL;
488 }
489 } else if (Key == "contents") {
490 if (HasContents) {
491 error(I->getKey(),
492 "entry already has 'contents' or 'external-contents'");
493 return NULL;
494 }
495 HasContents = true;
496 yaml::SequenceNode *Contents =
497 dyn_cast<yaml::SequenceNode>(I->getValue());
498 if (!Contents) {
499 // FIXME: this is only for directories, what about files?
500 error(I->getValue(), "expected array");
501 return NULL;
502 }
503
504 for (yaml::SequenceNode::iterator I = Contents->begin(),
505 E = Contents->end();
506 I != E; ++I) {
507 if (Entry *E = parseEntry(&*I))
508 EntryArrayContents.push_back(E);
509 else
510 return NULL;
511 }
512 } else if (Key == "external-contents") {
513 if (HasContents) {
514 error(I->getKey(),
515 "entry already has 'contents' or 'external-contents'");
516 return NULL;
517 }
518 HasContents = true;
519 if (!parseScalarString(I->getValue(), Value, Buffer))
520 return NULL;
521 ExternalContentsPath = Value;
522 } else {
523 llvm_unreachable("key missing from Keys");
524 }
525 }
526
527 if (Stream.failed())
528 return NULL;
529
530 // check for missing keys
531 if (!HasContents) {
532 error(N, "missing key 'contents' or 'external-contents'");
533 return NULL;
534 }
535 if (!checkMissingKeys(N, Keys))
536 return NULL;
537
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000538 // Remove trailing slash(es)
539 StringRef Trimmed(Name);
540 while (Trimmed.size() > 1 && sys::path::is_separator(Trimmed.back()))
541 Trimmed = Trimmed.slice(0, Trimmed.size()-1);
542 // Get the last component
543 StringRef LastComponent = sys::path::filename(Trimmed);
544
545 Entry *Result = 0;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000546 switch (Kind) {
547 case EK_File:
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000548 Result = new FileEntry(LastComponent, llvm_move(ExternalContentsPath));
549 break;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000550 case EK_Directory:
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000551 Result = new DirectoryEntry(LastComponent, llvm_move(EntryArrayContents),
552 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
553 0, file_type::directory_file, sys::fs::all_all));
554 break;
555 }
556
557 StringRef Parent = sys::path::parent_path(Trimmed);
558 if (Parent.empty())
559 return Result;
560
561 // if 'name' contains multiple components, create implicit directory entries
562 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
563 E = sys::path::rend(Parent);
564 I != E; ++I) {
565 Result = new DirectoryEntry(*I, Result,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000566 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
567 0, file_type::directory_file, sys::fs::all_all));
568 }
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000569 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000570 }
571
572public:
573 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
574
575 // false on error
576 bool parse(yaml::Node *Root, VFSFromYAML *FS) {
577 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
578 if (!Top) {
579 error(Root, "expected mapping node");
580 return false;
581 }
582
583 KeyStatusPair Fields[] = {
584 KeyStatusPair("version", true),
585 KeyStatusPair("case-sensitive", false),
586 KeyStatusPair("roots", true),
587 };
588
589 DenseMap<StringRef, KeyStatus> Keys(
590 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
591
592 // Parse configuration and 'roots'
593 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
594 ++I) {
595 SmallString<10> KeyBuffer;
596 StringRef Key;
597 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
598 return false;
599
600 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
601 return false;
602
603 if (Key == "roots") {
604 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
605 if (!Roots) {
606 error(I->getValue(), "expected array");
607 return false;
608 }
609
610 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
611 I != E; ++I) {
612 if (Entry *E = parseEntry(&*I))
613 FS->Roots.push_back(E);
614 else
615 return false;
616 }
617 } else if (Key == "version") {
618 StringRef VersionString;
619 SmallString<4> Storage;
620 if (!parseScalarString(I->getValue(), VersionString, Storage))
621 return false;
622 int Version;
623 if (VersionString.getAsInteger<int>(10, Version)) {
624 error(I->getValue(), "expected integer");
625 return false;
626 }
627 if (Version < 0) {
628 error(I->getValue(), "invalid version number");
629 return false;
630 }
631 if (Version != 0) {
632 error(I->getValue(), "version mismatch, expected 0");
633 return false;
634 }
635 } else if (Key == "case-sensitive") {
636 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
637 return false;
638 } else {
639 llvm_unreachable("key missing from Keys");
640 }
641 }
642
643 if (Stream.failed())
644 return false;
645
646 if (!checkMissingKeys(Top, Keys))
647 return false;
648 return true;
649 }
650};
651} // end of anonymous namespace
652
653Entry::~Entry() {}
654DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
655
656VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
657
658VFSFromYAML *VFSFromYAML::create(MemoryBuffer *Buffer,
659 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000660 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000661 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
662
663 SourceMgr SM;
664 yaml::Stream Stream(Buffer, SM);
665
Ben Langmuir97882e72014-02-24 20:56:37 +0000666 SM.setDiagHandler(DiagHandler, DiagContext);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000667 yaml::document_iterator DI = Stream.begin();
668 yaml::Node *Root = DI->getRoot();
669 if (DI == Stream.end() || !Root) {
670 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
671 return NULL;
672 }
673
674 VFSFromYAMLParser P(Stream);
675
676 OwningPtr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
677 if (!P.parse(Root, FS.get()))
678 return NULL;
679
680 return FS.take();
681}
682
683ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
684 SmallVector<char, 256> Storage;
685 StringRef Path = Path_.toNullTerminatedStringRef(Storage);
686
687 if (Path.empty())
688 return error_code(errc::invalid_argument, system_category());
689
690 sys::path::const_iterator Start = sys::path::begin(Path);
691 sys::path::const_iterator End = sys::path::end(Path);
692 for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
693 I != E; ++I) {
694 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
695 if (Result || Result.getError() != errc::no_such_file_or_directory)
696 return Result;
697 }
698 return error_code(errc::no_such_file_or_directory, system_category());
699}
700
701ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
702 sys::path::const_iterator End,
703 Entry *From) {
704 // FIXME: handle . and ..
705 if (CaseSensitive ? !Start->equals(From->getName())
706 : !Start->equals_lower(From->getName()))
707 // failure to match
708 return error_code(errc::no_such_file_or_directory, system_category());
709
710 ++Start;
711
712 if (Start == End) {
713 // Match!
714 return From;
715 }
716
717 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
718 if (!DE)
719 return error_code(errc::not_a_directory, system_category());
720
721 for (DirectoryEntry::iterator I = DE->contents_begin(),
722 E = DE->contents_end();
723 I != E; ++I) {
724 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
725 if (Result || Result.getError() != errc::no_such_file_or_directory)
726 return Result;
727 }
728 return error_code(errc::no_such_file_or_directory, system_category());
729}
730
731ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
732 ErrorOr<Entry *> Result = lookupPath(Path);
733 if (!Result)
734 return Result.getError();
735
736 std::string PathStr(Path.str());
737 if (FileEntry *F = dyn_cast<FileEntry>(*Result)) {
738 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
739 if (S) {
740 assert(S->getName() == S->getExternalName() &&
741 S->getName() == F->getExternalContentsPath());
742 S->setName(PathStr);
743 }
744 return S;
745 } else { // directory
746 DirectoryEntry *DE = cast<DirectoryEntry>(*Result);
747 Status S = DE->getStatus();
748 S.setName(PathStr);
749 S.setExternalName(PathStr);
750 return S;
751 }
752}
753
754error_code VFSFromYAML::openFileForRead(const Twine &Path,
755 OwningPtr<vfs::File> &Result) {
756 ErrorOr<Entry *> E = lookupPath(Path);
757 if (!E)
758 return E.getError();
759
760 FileEntry *F = dyn_cast<FileEntry>(*E);
761 if (!F) // FIXME: errc::not_a_file?
762 return error_code(errc::invalid_argument, system_category());
763
Ben Langmuir801272a2014-02-25 18:23:47 +0000764 return ExternalFS->openFileForRead(F->getExternalContentsPath(), Result);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000765}
766
767IntrusiveRefCntPtr<FileSystem>
768vfs::getVFSFromYAML(MemoryBuffer *Buffer, SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000769 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000770 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Ben Langmuir97882e72014-02-24 20:56:37 +0000771 return VFSFromYAML::create(Buffer, DiagHandler, DiagContext, ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000772}
773
774UniqueID vfs::getNextVirtualUniqueID() {
775 static volatile sys::cas_flag UID = 0;
776 sys::cas_flag ID = llvm::sys::AtomicIncrement(&UID);
777 // The following assumes that uint64_t max will never collide with a real
778 // dev_t value from the OS.
779 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
780}