blob: d4845e6f363b746b80a4d70c341400a69c964f4d [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;
Ben Langmuir09e0d5c2014-02-27 23:27:54 +000086 Status S;
Ben Langmuirc8130a72014-02-20 21:59:23 +000087 friend class RealFileSystem;
88 RealFile(int FD) : FD(FD) {
89 assert(FD >= 0 && "Invalid or inactive file descriptor");
90 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +000091
Ben Langmuirc8130a72014-02-20 21:59:23 +000092public:
93 ~RealFile();
94 ErrorOr<Status> status() LLVM_OVERRIDE;
95 error_code getBuffer(const Twine &Name, OwningPtr<MemoryBuffer> &Result,
96 int64_t FileSize = -1,
97 bool RequiresNullTerminator = true) LLVM_OVERRIDE;
98 error_code close() LLVM_OVERRIDE;
Ben Langmuir09e0d5c2014-02-27 23:27:54 +000099 void setName(StringRef Name) LLVM_OVERRIDE;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000100};
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000101RealFile::~RealFile() { close(); }
Ben Langmuirc8130a72014-02-20 21:59:23 +0000102
103ErrorOr<Status> RealFile::status() {
104 assert(FD != -1 && "cannot stat closed file");
Ben Langmuir09e0d5c2014-02-27 23:27:54 +0000105 if (!S.isStatusKnown()) {
106 file_status RealStatus;
107 if (error_code EC = sys::fs::status(FD, RealStatus))
108 return EC;
109 Status NewS(RealStatus);
110 NewS.setName(S.getName());
111 S = llvm_move(NewS);
112 }
113 return S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000114}
115
116error_code RealFile::getBuffer(const Twine &Name,
117 OwningPtr<MemoryBuffer> &Result,
118 int64_t FileSize, bool RequiresNullTerminator) {
119 assert(FD != -1 && "cannot get buffer for closed file");
120 return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
121 RequiresNullTerminator);
122}
123
124// FIXME: This is terrible, we need this for ::close.
125#if !defined(_MSC_VER) && !defined(__MINGW32__)
126#include <unistd.h>
127#include <sys/uio.h>
128#else
129#include <io.h>
130#ifndef S_ISFIFO
131#define S_ISFIFO(x) (0)
132#endif
133#endif
134error_code RealFile::close() {
135 if (::close(FD))
136 return error_code(errno, system_category());
137 FD = -1;
138 return error_code::success();
139}
140
Ben Langmuir09e0d5c2014-02-27 23:27:54 +0000141void RealFile::setName(StringRef Name) {
142 S.setName(Name);
143}
144
Ben Langmuirc8130a72014-02-20 21:59:23 +0000145/// \brief The file system according to your operating system.
146class RealFileSystem : public FileSystem {
147public:
148 ErrorOr<Status> status(const Twine &Path) LLVM_OVERRIDE;
149 error_code openFileForRead(const Twine &Path,
150 OwningPtr<File> &Result) LLVM_OVERRIDE;
151};
152
153ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
154 sys::fs::file_status RealStatus;
155 if (error_code EC = sys::fs::status(Path, RealStatus))
156 return EC;
157 Status Result(RealStatus);
158 Result.setName(Path.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000159 return Result;
160}
161
162error_code RealFileSystem::openFileForRead(const Twine &Name,
163 OwningPtr<File> &Result) {
164 int FD;
165 if (error_code EC = sys::fs::openFileForRead(Name, FD))
166 return EC;
167 Result.reset(new RealFile(FD));
Ben Langmuir09e0d5c2014-02-27 23:27:54 +0000168 Result->setName(Name.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000169 return error_code::success();
170}
171
172IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
173 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
174 return FS;
175}
176
177//===-----------------------------------------------------------------------===/
178// OverlayFileSystem implementation
179//===-----------------------------------------------------------------------===/
180OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
181 pushOverlay(BaseFS);
182}
183
184void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
185 FSList.push_back(FS);
186}
187
188ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
189 // FIXME: handle symlinks that cross file systems
190 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
191 ErrorOr<Status> Status = (*I)->status(Path);
192 if (Status || Status.getError() != errc::no_such_file_or_directory)
193 return Status;
194 }
195 return error_code(errc::no_such_file_or_directory, system_category());
196}
197
198error_code OverlayFileSystem::openFileForRead(const llvm::Twine &Path,
199 OwningPtr<File> &Result) {
200 // FIXME: handle symlinks that cross file systems
201 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
202 error_code EC = (*I)->openFileForRead(Path, Result);
203 if (!EC || EC != errc::no_such_file_or_directory)
204 return EC;
205 }
206 return error_code(errc::no_such_file_or_directory, system_category());
207}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000208
209//===-----------------------------------------------------------------------===/
210// VFSFromYAML implementation
211//===-----------------------------------------------------------------------===/
212
213// Allow DenseMap<StringRef, ...>. This is useful below because we know all the
214// strings are literals and will outlive the map, and there is no reason to
215// store them.
216namespace llvm {
217 template<>
218 struct DenseMapInfo<StringRef> {
219 // This assumes that "" will never be a valid key.
220 static inline StringRef getEmptyKey() { return StringRef(""); }
221 static inline StringRef getTombstoneKey() { return StringRef(); }
222 static unsigned getHashValue(StringRef Val) { return HashString(Val); }
223 static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
224 };
225}
226
227namespace {
228
229enum EntryKind {
230 EK_Directory,
231 EK_File
232};
233
234/// \brief A single file or directory in the VFS.
235class Entry {
236 EntryKind Kind;
237 std::string Name;
238
239public:
240 virtual ~Entry();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000241 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
242 StringRef getName() const { return Name; }
243 EntryKind getKind() const { return Kind; }
244};
245
246class DirectoryEntry : public Entry {
247 std::vector<Entry *> Contents;
248 Status S;
249
250public:
251 virtual ~DirectoryEntry();
252#if LLVM_HAS_RVALUE_REFERENCES
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000253 DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
254 : Entry(EK_Directory, Name), Contents(std::move(Contents)),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000255 S(std::move(S)) {}
256#endif
257 DirectoryEntry(StringRef Name, ArrayRef<Entry *> Contents, const Status &S)
258 : Entry(EK_Directory, Name), Contents(Contents), S(S) {}
259 Status getStatus() { return S; }
260 typedef std::vector<Entry *>::iterator iterator;
261 iterator contents_begin() { return Contents.begin(); }
262 iterator contents_end() { return Contents.end(); }
263 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
264};
265
266class FileEntry : public Entry {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000267public:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000268 enum NameKind {
269 NK_NotSet,
270 NK_External,
271 NK_Virtual
272 };
273private:
274 std::string ExternalContentsPath;
275 NameKind UseName;
276public:
277 FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
278 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
279 UseName(UseName) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000280 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
Ben Langmuirb59cf672014-02-27 00:25:12 +0000281 /// \brief whether to use the external path as the name for this file.
Ben Langmuir09e0d5c2014-02-27 23:27:54 +0000282 bool useExternalName(bool GlobalUseExternalName) const {
283 return UseName == NK_NotSet ? GlobalUseExternalName
284 : (UseName == NK_External);
285 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000286 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
287};
288
289/// \brief A virtual file system parsed from a YAML file.
290///
291/// Currently, this class allows creating virtual directories and mapping
292/// virtual file paths to existing external files, available in \c ExternalFS.
293///
294/// The basic structure of the parsed file is:
295/// \verbatim
296/// {
297/// 'version': <version number>,
298/// <optional configuration>
299/// 'roots': [
300/// <directory entries>
301/// ]
302/// }
303/// \endverbatim
304///
305/// All configuration options are optional.
306/// 'case-sensitive': <boolean, default=true>
Ben Langmuirb59cf672014-02-27 00:25:12 +0000307/// 'use-external-names': <boolean, default=true>
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000308///
309/// Virtual directories are represented as
310/// \verbatim
311/// {
312/// 'type': 'directory',
313/// 'name': <string>,
314/// 'contents': [ <file or directory entries> ]
315/// }
316/// \endverbatim
317///
318/// The default attributes for virtual directories are:
319/// \verbatim
320/// MTime = now() when created
321/// Perms = 0777
322/// User = Group = 0
323/// Size = 0
324/// UniqueID = unspecified unique value
325/// \endverbatim
326///
327/// Re-mapped files are represented as
328/// \verbatim
329/// {
330/// 'type': 'file',
331/// 'name': <string>,
Ben Langmuirb59cf672014-02-27 00:25:12 +0000332/// 'use-external-name': <boolean> # Optional
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000333/// 'external-contents': <path to external file>)
334/// }
335/// \endverbatim
336///
337/// and inherit their attributes from the external contents.
338///
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000339/// In both cases, the 'name' field may contain multiple path components (e.g.
340/// /path/to/file). However, any directory that contains more than one child
341/// must be uniquely represented by a directory entry.
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000342class VFSFromYAML : public vfs::FileSystem {
343 std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
344 /// \brief The file system to use for external references.
345 IntrusiveRefCntPtr<FileSystem> ExternalFS;
346
347 /// @name Configuration
348 /// @{
349
350 /// \brief Whether to perform case-sensitive comparisons.
351 ///
352 /// Currently, case-insensitive matching only works correctly with ASCII.
Ben Langmuirb59cf672014-02-27 00:25:12 +0000353 bool CaseSensitive;
354
355 /// \brief Whether to use to use the value of 'external-contents' for the
356 /// names of files. This global value is overridable on a per-file basis.
357 bool UseExternalNames;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000358 /// @}
359
360 friend class VFSFromYAMLParser;
361
362private:
363 VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
Ben Langmuirb59cf672014-02-27 00:25:12 +0000364 : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000365
366 /// \brief Looks up \p Path in \c Roots.
367 ErrorOr<Entry *> lookupPath(const Twine &Path);
368
369 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
370 /// recursing into the contents of \p From if it is a directory.
371 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
372 sys::path::const_iterator End, Entry *From);
373
374public:
375 ~VFSFromYAML();
376
377 /// \brief Parses \p Buffer, which is expected to be in YAML format and
378 /// returns a virtual file system representing its contents.
379 ///
380 /// Takes ownership of \p Buffer.
381 static VFSFromYAML *create(MemoryBuffer *Buffer,
382 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000383 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000384 IntrusiveRefCntPtr<FileSystem> ExternalFS);
385
386 ErrorOr<Status> status(const Twine &Path) LLVM_OVERRIDE;
387 error_code openFileForRead(const Twine &Path,
388 OwningPtr<File> &Result) LLVM_OVERRIDE;
389};
390
391/// \brief A helper class to hold the common YAML parsing state.
392class VFSFromYAMLParser {
393 yaml::Stream &Stream;
394
395 void error(yaml::Node *N, const Twine &Msg) {
396 Stream.printError(N, Msg);
397 }
398
399 // false on error
400 bool parseScalarString(yaml::Node *N, StringRef &Result,
401 SmallVectorImpl<char> &Storage) {
402 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
403 if (!S) {
404 error(N, "expected string");
405 return false;
406 }
407 Result = S->getValue(Storage);
408 return true;
409 }
410
411 // false on error
412 bool parseScalarBool(yaml::Node *N, bool &Result) {
413 SmallString<5> Storage;
414 StringRef Value;
415 if (!parseScalarString(N, Value, Storage))
416 return false;
417
418 if (Value.equals_lower("true") || Value.equals_lower("on") ||
419 Value.equals_lower("yes") || Value == "1") {
420 Result = true;
421 return true;
422 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
423 Value.equals_lower("no") || Value == "0") {
424 Result = false;
425 return true;
426 }
427
428 error(N, "expected boolean value");
429 return false;
430 }
431
432 struct KeyStatus {
433 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
434 bool Required;
435 bool Seen;
436 };
437 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
438
439 // false on error
440 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
441 DenseMap<StringRef, KeyStatus> &Keys) {
442 if (!Keys.count(Key)) {
443 error(KeyNode, "unknown key");
444 return false;
445 }
446 KeyStatus &S = Keys[Key];
447 if (S.Seen) {
448 error(KeyNode, Twine("duplicate key '") + Key + "'");
449 return false;
450 }
451 S.Seen = true;
452 return true;
453 }
454
455 // false on error
456 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
457 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
458 E = Keys.end();
459 I != E; ++I) {
460 if (I->second.Required && !I->second.Seen) {
461 error(Obj, Twine("missing key '") + I->first + "'");
462 return false;
463 }
464 }
465 return true;
466 }
467
468 Entry *parseEntry(yaml::Node *N) {
469 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
470 if (!M) {
471 error(N, "expected mapping node for file or directory entry");
472 return NULL;
473 }
474
475 KeyStatusPair Fields[] = {
476 KeyStatusPair("name", true),
477 KeyStatusPair("type", true),
478 KeyStatusPair("contents", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000479 KeyStatusPair("external-contents", false),
480 KeyStatusPair("use-external-name", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000481 };
482
483 DenseMap<StringRef, KeyStatus> Keys(
484 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
485
486 bool HasContents = false; // external or otherwise
487 std::vector<Entry *> EntryArrayContents;
488 std::string ExternalContentsPath;
489 std::string Name;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000490 FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000491 EntryKind Kind;
492
493 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
494 ++I) {
495 StringRef Key;
496 // Reuse the buffer for key and value, since we don't look at key after
497 // parsing value.
498 SmallString<256> Buffer;
499 if (!parseScalarString(I->getKey(), Key, Buffer))
500 return NULL;
501
502 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
503 return NULL;
504
505 StringRef Value;
506 if (Key == "name") {
507 if (!parseScalarString(I->getValue(), Value, Buffer))
508 return NULL;
509 Name = Value;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000510 } else if (Key == "type") {
511 if (!parseScalarString(I->getValue(), Value, Buffer))
512 return NULL;
513 if (Value == "file")
514 Kind = EK_File;
515 else if (Value == "directory")
516 Kind = EK_Directory;
517 else {
518 error(I->getValue(), "unknown value for 'type'");
519 return NULL;
520 }
521 } else if (Key == "contents") {
522 if (HasContents) {
523 error(I->getKey(),
524 "entry already has 'contents' or 'external-contents'");
525 return NULL;
526 }
527 HasContents = true;
528 yaml::SequenceNode *Contents =
529 dyn_cast<yaml::SequenceNode>(I->getValue());
530 if (!Contents) {
531 // FIXME: this is only for directories, what about files?
532 error(I->getValue(), "expected array");
533 return NULL;
534 }
535
536 for (yaml::SequenceNode::iterator I = Contents->begin(),
537 E = Contents->end();
538 I != E; ++I) {
539 if (Entry *E = parseEntry(&*I))
540 EntryArrayContents.push_back(E);
541 else
542 return NULL;
543 }
544 } else if (Key == "external-contents") {
545 if (HasContents) {
546 error(I->getKey(),
547 "entry already has 'contents' or 'external-contents'");
548 return NULL;
549 }
550 HasContents = true;
551 if (!parseScalarString(I->getValue(), Value, Buffer))
552 return NULL;
553 ExternalContentsPath = Value;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000554 } else if (Key == "use-external-name") {
555 bool Val;
556 if (!parseScalarBool(I->getValue(), Val))
557 return NULL;
558 UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000559 } else {
560 llvm_unreachable("key missing from Keys");
561 }
562 }
563
564 if (Stream.failed())
565 return NULL;
566
567 // check for missing keys
568 if (!HasContents) {
569 error(N, "missing key 'contents' or 'external-contents'");
570 return NULL;
571 }
572 if (!checkMissingKeys(N, Keys))
573 return NULL;
574
Ben Langmuirb59cf672014-02-27 00:25:12 +0000575 // check invalid configuration
576 if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
577 error(N, "'use-external-name' is not supported for directories");
578 return NULL;
579 }
580
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000581 // Remove trailing slash(es)
582 StringRef Trimmed(Name);
583 while (Trimmed.size() > 1 && sys::path::is_separator(Trimmed.back()))
584 Trimmed = Trimmed.slice(0, Trimmed.size()-1);
585 // Get the last component
586 StringRef LastComponent = sys::path::filename(Trimmed);
587
588 Entry *Result = 0;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000589 switch (Kind) {
590 case EK_File:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000591 Result = new FileEntry(LastComponent, llvm_move(ExternalContentsPath),
592 UseExternalName);
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000593 break;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000594 case EK_Directory:
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000595 Result = new DirectoryEntry(LastComponent, llvm_move(EntryArrayContents),
596 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
597 0, file_type::directory_file, sys::fs::all_all));
598 break;
599 }
600
601 StringRef Parent = sys::path::parent_path(Trimmed);
602 if (Parent.empty())
603 return Result;
604
605 // if 'name' contains multiple components, create implicit directory entries
606 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
607 E = sys::path::rend(Parent);
608 I != E; ++I) {
609 Result = new DirectoryEntry(*I, Result,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000610 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
611 0, file_type::directory_file, sys::fs::all_all));
612 }
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000613 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000614 }
615
616public:
617 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
618
619 // false on error
620 bool parse(yaml::Node *Root, VFSFromYAML *FS) {
621 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
622 if (!Top) {
623 error(Root, "expected mapping node");
624 return false;
625 }
626
627 KeyStatusPair Fields[] = {
628 KeyStatusPair("version", true),
629 KeyStatusPair("case-sensitive", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000630 KeyStatusPair("use-external-names", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000631 KeyStatusPair("roots", true),
632 };
633
634 DenseMap<StringRef, KeyStatus> Keys(
635 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
636
637 // Parse configuration and 'roots'
638 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
639 ++I) {
640 SmallString<10> KeyBuffer;
641 StringRef Key;
642 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
643 return false;
644
645 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
646 return false;
647
648 if (Key == "roots") {
649 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
650 if (!Roots) {
651 error(I->getValue(), "expected array");
652 return false;
653 }
654
655 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
656 I != E; ++I) {
657 if (Entry *E = parseEntry(&*I))
658 FS->Roots.push_back(E);
659 else
660 return false;
661 }
662 } else if (Key == "version") {
663 StringRef VersionString;
664 SmallString<4> Storage;
665 if (!parseScalarString(I->getValue(), VersionString, Storage))
666 return false;
667 int Version;
668 if (VersionString.getAsInteger<int>(10, Version)) {
669 error(I->getValue(), "expected integer");
670 return false;
671 }
672 if (Version < 0) {
673 error(I->getValue(), "invalid version number");
674 return false;
675 }
676 if (Version != 0) {
677 error(I->getValue(), "version mismatch, expected 0");
678 return false;
679 }
680 } else if (Key == "case-sensitive") {
681 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
682 return false;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000683 } else if (Key == "use-external-names") {
684 if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
685 return false;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000686 } else {
687 llvm_unreachable("key missing from Keys");
688 }
689 }
690
691 if (Stream.failed())
692 return false;
693
694 if (!checkMissingKeys(Top, Keys))
695 return false;
696 return true;
697 }
698};
699} // end of anonymous namespace
700
701Entry::~Entry() {}
702DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
703
704VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
705
706VFSFromYAML *VFSFromYAML::create(MemoryBuffer *Buffer,
707 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000708 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000709 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
710
711 SourceMgr SM;
712 yaml::Stream Stream(Buffer, SM);
713
Ben Langmuir97882e72014-02-24 20:56:37 +0000714 SM.setDiagHandler(DiagHandler, DiagContext);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000715 yaml::document_iterator DI = Stream.begin();
716 yaml::Node *Root = DI->getRoot();
717 if (DI == Stream.end() || !Root) {
718 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
719 return NULL;
720 }
721
722 VFSFromYAMLParser P(Stream);
723
724 OwningPtr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
725 if (!P.parse(Root, FS.get()))
726 return NULL;
727
728 return FS.take();
729}
730
731ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
732 SmallVector<char, 256> Storage;
733 StringRef Path = Path_.toNullTerminatedStringRef(Storage);
734
735 if (Path.empty())
736 return error_code(errc::invalid_argument, system_category());
737
738 sys::path::const_iterator Start = sys::path::begin(Path);
739 sys::path::const_iterator End = sys::path::end(Path);
740 for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
741 I != E; ++I) {
742 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
743 if (Result || Result.getError() != errc::no_such_file_or_directory)
744 return Result;
745 }
746 return error_code(errc::no_such_file_or_directory, system_category());
747}
748
749ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
750 sys::path::const_iterator End,
751 Entry *From) {
752 // FIXME: handle . and ..
753 if (CaseSensitive ? !Start->equals(From->getName())
754 : !Start->equals_lower(From->getName()))
755 // failure to match
756 return error_code(errc::no_such_file_or_directory, system_category());
757
758 ++Start;
759
760 if (Start == End) {
761 // Match!
762 return From;
763 }
764
765 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
766 if (!DE)
767 return error_code(errc::not_a_directory, system_category());
768
769 for (DirectoryEntry::iterator I = DE->contents_begin(),
770 E = DE->contents_end();
771 I != E; ++I) {
772 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
773 if (Result || Result.getError() != errc::no_such_file_or_directory)
774 return Result;
775 }
776 return error_code(errc::no_such_file_or_directory, system_category());
777}
778
779ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
780 ErrorOr<Entry *> Result = lookupPath(Path);
781 if (!Result)
782 return Result.getError();
783
784 std::string PathStr(Path.str());
785 if (FileEntry *F = dyn_cast<FileEntry>(*Result)) {
786 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
Ben Langmuirb59cf672014-02-27 00:25:12 +0000787 assert(!S || S->getName() == F->getExternalContentsPath());
Ben Langmuir09e0d5c2014-02-27 23:27:54 +0000788 if (S && !F->useExternalName(UseExternalNames))
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000789 S->setName(PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000790 return S;
791 } else { // directory
792 DirectoryEntry *DE = cast<DirectoryEntry>(*Result);
793 Status S = DE->getStatus();
794 S.setName(PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000795 return S;
796 }
797}
798
799error_code VFSFromYAML::openFileForRead(const Twine &Path,
800 OwningPtr<vfs::File> &Result) {
801 ErrorOr<Entry *> E = lookupPath(Path);
802 if (!E)
803 return E.getError();
804
805 FileEntry *F = dyn_cast<FileEntry>(*E);
806 if (!F) // FIXME: errc::not_a_file?
807 return error_code(errc::invalid_argument, system_category());
808
Ben Langmuir09e0d5c2014-02-27 23:27:54 +0000809 if (error_code EC = ExternalFS->openFileForRead(F->getExternalContentsPath(),
810 Result))
811 return EC;
812
813 if (!F->useExternalName(UseExternalNames))
814 Result->setName(Path.str());
815
816 return error_code::success();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000817}
818
819IntrusiveRefCntPtr<FileSystem>
820vfs::getVFSFromYAML(MemoryBuffer *Buffer, SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000821 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000822 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Ben Langmuir97882e72014-02-24 20:56:37 +0000823 return VFSFromYAML::create(Buffer, DiagHandler, DiagContext, ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000824}
825
826UniqueID vfs::getNextVirtualUniqueID() {
827 static volatile sys::cas_flag UID = 0;
828 sys::cas_flag ID = llvm::sys::AtomicIncrement(&UID);
829 // The following assumes that uint64_t max will never collide with a real
830 // dev_t value from the OS.
831 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
832}