blob: a107db68e441e5436a7d3a4a71801e9c293a566c [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();
230#if LLVM_HAS_RVALUE_REFERENCES
231 Entry(EntryKind K, std::string Name) : Kind(K), Name(std::move(Name)) {}
232#endif
233 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
234 StringRef getName() const { return Name; }
235 EntryKind getKind() const { return Kind; }
236};
237
238class DirectoryEntry : public Entry {
239 std::vector<Entry *> Contents;
240 Status S;
241
242public:
243 virtual ~DirectoryEntry();
244#if LLVM_HAS_RVALUE_REFERENCES
245 DirectoryEntry(std::string Name, std::vector<Entry *> Contents, Status S)
246 : Entry(EK_Directory, std::move(Name)), Contents(std::move(Contents)),
247 S(std::move(S)) {}
248#endif
249 DirectoryEntry(StringRef Name, ArrayRef<Entry *> Contents, const Status &S)
250 : Entry(EK_Directory, Name), Contents(Contents), S(S) {}
251 Status getStatus() { return S; }
252 typedef std::vector<Entry *>::iterator iterator;
253 iterator contents_begin() { return Contents.begin(); }
254 iterator contents_end() { return Contents.end(); }
255 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
256};
257
258class FileEntry : public Entry {
259 std::string ExternalContentsPath;
260
261public:
262#if LLVM_HAS_RVALUE_REFERENCES
263 FileEntry(std::string Name, std::string ExternalContentsPath)
264 : Entry(EK_File, std::move(Name)),
265 ExternalContentsPath(std::move(ExternalContentsPath)) {}
266#endif
267 FileEntry(StringRef Name, StringRef ExternalContentsPath)
268 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath) {}
269 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
270 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
271};
272
273/// \brief A virtual file system parsed from a YAML file.
274///
275/// Currently, this class allows creating virtual directories and mapping
276/// virtual file paths to existing external files, available in \c ExternalFS.
277///
278/// The basic structure of the parsed file is:
279/// \verbatim
280/// {
281/// 'version': <version number>,
282/// <optional configuration>
283/// 'roots': [
284/// <directory entries>
285/// ]
286/// }
287/// \endverbatim
288///
289/// All configuration options are optional.
290/// 'case-sensitive': <boolean, default=true>
291///
292/// Virtual directories are represented as
293/// \verbatim
294/// {
295/// 'type': 'directory',
296/// 'name': <string>,
297/// 'contents': [ <file or directory entries> ]
298/// }
299/// \endverbatim
300///
301/// The default attributes for virtual directories are:
302/// \verbatim
303/// MTime = now() when created
304/// Perms = 0777
305/// User = Group = 0
306/// Size = 0
307/// UniqueID = unspecified unique value
308/// \endverbatim
309///
310/// Re-mapped files are represented as
311/// \verbatim
312/// {
313/// 'type': 'file',
314/// 'name': <string>,
315/// 'external-contents': <path to external file>)
316/// }
317/// \endverbatim
318///
319/// and inherit their attributes from the external contents.
320///
321/// In both cases, the 'name' field must be a single path component (containing
322/// no separators).
323class VFSFromYAML : public vfs::FileSystem {
324 std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
325 /// \brief The file system to use for external references.
326 IntrusiveRefCntPtr<FileSystem> ExternalFS;
327
328 /// @name Configuration
329 /// @{
330
331 /// \brief Whether to perform case-sensitive comparisons.
332 ///
333 /// Currently, case-insensitive matching only works correctly with ASCII.
334 bool CaseSensitive; ///< Whether to perform case-sensitive comparisons.
335 /// @}
336
337 friend class VFSFromYAMLParser;
338
339private:
340 VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
341 : ExternalFS(ExternalFS), CaseSensitive(true) {}
342
343 /// \brief Looks up \p Path in \c Roots.
344 ErrorOr<Entry *> lookupPath(const Twine &Path);
345
346 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
347 /// recursing into the contents of \p From if it is a directory.
348 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
349 sys::path::const_iterator End, Entry *From);
350
351public:
352 ~VFSFromYAML();
353
354 /// \brief Parses \p Buffer, which is expected to be in YAML format and
355 /// returns a virtual file system representing its contents.
356 ///
357 /// Takes ownership of \p Buffer.
358 static VFSFromYAML *create(MemoryBuffer *Buffer,
359 SourceMgr::DiagHandlerTy DiagHandler,
360 IntrusiveRefCntPtr<FileSystem> ExternalFS);
361
362 ErrorOr<Status> status(const Twine &Path) LLVM_OVERRIDE;
363 error_code openFileForRead(const Twine &Path,
364 OwningPtr<File> &Result) LLVM_OVERRIDE;
365};
366
367/// \brief A helper class to hold the common YAML parsing state.
368class VFSFromYAMLParser {
369 yaml::Stream &Stream;
370
371 void error(yaml::Node *N, const Twine &Msg) {
372 Stream.printError(N, Msg);
373 }
374
375 // false on error
376 bool parseScalarString(yaml::Node *N, StringRef &Result,
377 SmallVectorImpl<char> &Storage) {
378 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
379 if (!S) {
380 error(N, "expected string");
381 return false;
382 }
383 Result = S->getValue(Storage);
384 return true;
385 }
386
387 // false on error
388 bool parseScalarBool(yaml::Node *N, bool &Result) {
389 SmallString<5> Storage;
390 StringRef Value;
391 if (!parseScalarString(N, Value, Storage))
392 return false;
393
394 if (Value.equals_lower("true") || Value.equals_lower("on") ||
395 Value.equals_lower("yes") || Value == "1") {
396 Result = true;
397 return true;
398 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
399 Value.equals_lower("no") || Value == "0") {
400 Result = false;
401 return true;
402 }
403
404 error(N, "expected boolean value");
405 return false;
406 }
407
408 struct KeyStatus {
409 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
410 bool Required;
411 bool Seen;
412 };
413 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
414
415 // false on error
416 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
417 DenseMap<StringRef, KeyStatus> &Keys) {
418 if (!Keys.count(Key)) {
419 error(KeyNode, "unknown key");
420 return false;
421 }
422 KeyStatus &S = Keys[Key];
423 if (S.Seen) {
424 error(KeyNode, Twine("duplicate key '") + Key + "'");
425 return false;
426 }
427 S.Seen = true;
428 return true;
429 }
430
431 // false on error
432 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
433 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
434 E = Keys.end();
435 I != E; ++I) {
436 if (I->second.Required && !I->second.Seen) {
437 error(Obj, Twine("missing key '") + I->first + "'");
438 return false;
439 }
440 }
441 return true;
442 }
443
444 Entry *parseEntry(yaml::Node *N) {
445 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
446 if (!M) {
447 error(N, "expected mapping node for file or directory entry");
448 return NULL;
449 }
450
451 KeyStatusPair Fields[] = {
452 KeyStatusPair("name", true),
453 KeyStatusPair("type", true),
454 KeyStatusPair("contents", false),
455 KeyStatusPair("external-contents", false)
456 };
457
458 DenseMap<StringRef, KeyStatus> Keys(
459 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
460
461 bool HasContents = false; // external or otherwise
462 std::vector<Entry *> EntryArrayContents;
463 std::string ExternalContentsPath;
464 std::string Name;
465 EntryKind Kind;
466
467 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
468 ++I) {
469 StringRef Key;
470 // Reuse the buffer for key and value, since we don't look at key after
471 // parsing value.
472 SmallString<256> Buffer;
473 if (!parseScalarString(I->getKey(), Key, Buffer))
474 return NULL;
475
476 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
477 return NULL;
478
479 StringRef Value;
480 if (Key == "name") {
481 if (!parseScalarString(I->getValue(), Value, Buffer))
482 return NULL;
483 Name = Value;
484 if (sys::path::has_parent_path(Name)) {
485 error(I->getValue(), "unexpected path separator in name");
486 return NULL;
487 }
488 } else if (Key == "type") {
489 if (!parseScalarString(I->getValue(), Value, Buffer))
490 return NULL;
491 if (Value == "file")
492 Kind = EK_File;
493 else if (Value == "directory")
494 Kind = EK_Directory;
495 else {
496 error(I->getValue(), "unknown value for 'type'");
497 return NULL;
498 }
499 } else if (Key == "contents") {
500 if (HasContents) {
501 error(I->getKey(),
502 "entry already has 'contents' or 'external-contents'");
503 return NULL;
504 }
505 HasContents = true;
506 yaml::SequenceNode *Contents =
507 dyn_cast<yaml::SequenceNode>(I->getValue());
508 if (!Contents) {
509 // FIXME: this is only for directories, what about files?
510 error(I->getValue(), "expected array");
511 return NULL;
512 }
513
514 for (yaml::SequenceNode::iterator I = Contents->begin(),
515 E = Contents->end();
516 I != E; ++I) {
517 if (Entry *E = parseEntry(&*I))
518 EntryArrayContents.push_back(E);
519 else
520 return NULL;
521 }
522 } else if (Key == "external-contents") {
523 if (HasContents) {
524 error(I->getKey(),
525 "entry already has 'contents' or 'external-contents'");
526 return NULL;
527 }
528 HasContents = true;
529 if (!parseScalarString(I->getValue(), Value, Buffer))
530 return NULL;
531 ExternalContentsPath = Value;
532 } else {
533 llvm_unreachable("key missing from Keys");
534 }
535 }
536
537 if (Stream.failed())
538 return NULL;
539
540 // check for missing keys
541 if (!HasContents) {
542 error(N, "missing key 'contents' or 'external-contents'");
543 return NULL;
544 }
545 if (!checkMissingKeys(N, Keys))
546 return NULL;
547
548 switch (Kind) {
549 case EK_File:
550 return new FileEntry(llvm_move(Name), llvm_move(ExternalContentsPath));
551 case EK_Directory:
552 return new DirectoryEntry(
553 llvm_move(Name), llvm_move(EntryArrayContents),
554 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
555 0, file_type::directory_file, sys::fs::all_all));
556 }
Ben Langmuir09099512014-02-22 00:44:02 +0000557 llvm_unreachable("unknown EntryKind in switch");
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000558 }
559
560public:
561 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
562
563 // false on error
564 bool parse(yaml::Node *Root, VFSFromYAML *FS) {
565 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
566 if (!Top) {
567 error(Root, "expected mapping node");
568 return false;
569 }
570
571 KeyStatusPair Fields[] = {
572 KeyStatusPair("version", true),
573 KeyStatusPair("case-sensitive", false),
574 KeyStatusPair("roots", true),
575 };
576
577 DenseMap<StringRef, KeyStatus> Keys(
578 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
579
580 // Parse configuration and 'roots'
581 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
582 ++I) {
583 SmallString<10> KeyBuffer;
584 StringRef Key;
585 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
586 return false;
587
588 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
589 return false;
590
591 if (Key == "roots") {
592 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
593 if (!Roots) {
594 error(I->getValue(), "expected array");
595 return false;
596 }
597
598 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
599 I != E; ++I) {
600 if (Entry *E = parseEntry(&*I))
601 FS->Roots.push_back(E);
602 else
603 return false;
604 }
605 } else if (Key == "version") {
606 StringRef VersionString;
607 SmallString<4> Storage;
608 if (!parseScalarString(I->getValue(), VersionString, Storage))
609 return false;
610 int Version;
611 if (VersionString.getAsInteger<int>(10, Version)) {
612 error(I->getValue(), "expected integer");
613 return false;
614 }
615 if (Version < 0) {
616 error(I->getValue(), "invalid version number");
617 return false;
618 }
619 if (Version != 0) {
620 error(I->getValue(), "version mismatch, expected 0");
621 return false;
622 }
623 } else if (Key == "case-sensitive") {
624 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
625 return false;
626 } else {
627 llvm_unreachable("key missing from Keys");
628 }
629 }
630
631 if (Stream.failed())
632 return false;
633
634 if (!checkMissingKeys(Top, Keys))
635 return false;
636 return true;
637 }
638};
639} // end of anonymous namespace
640
641Entry::~Entry() {}
642DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
643
644VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
645
646VFSFromYAML *VFSFromYAML::create(MemoryBuffer *Buffer,
647 SourceMgr::DiagHandlerTy DiagHandler,
648 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
649
650 SourceMgr SM;
651 yaml::Stream Stream(Buffer, SM);
652
653 SM.setDiagHandler(DiagHandler);
654 yaml::document_iterator DI = Stream.begin();
655 yaml::Node *Root = DI->getRoot();
656 if (DI == Stream.end() || !Root) {
657 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
658 return NULL;
659 }
660
661 VFSFromYAMLParser P(Stream);
662
663 OwningPtr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
664 if (!P.parse(Root, FS.get()))
665 return NULL;
666
667 return FS.take();
668}
669
670ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
671 SmallVector<char, 256> Storage;
672 StringRef Path = Path_.toNullTerminatedStringRef(Storage);
673
674 if (Path.empty())
675 return error_code(errc::invalid_argument, system_category());
676
677 sys::path::const_iterator Start = sys::path::begin(Path);
678 sys::path::const_iterator End = sys::path::end(Path);
679 for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
680 I != E; ++I) {
681 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
682 if (Result || Result.getError() != errc::no_such_file_or_directory)
683 return Result;
684 }
685 return error_code(errc::no_such_file_or_directory, system_category());
686}
687
688ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
689 sys::path::const_iterator End,
690 Entry *From) {
691 // FIXME: handle . and ..
692 if (CaseSensitive ? !Start->equals(From->getName())
693 : !Start->equals_lower(From->getName()))
694 // failure to match
695 return error_code(errc::no_such_file_or_directory, system_category());
696
697 ++Start;
698
699 if (Start == End) {
700 // Match!
701 return From;
702 }
703
704 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
705 if (!DE)
706 return error_code(errc::not_a_directory, system_category());
707
708 for (DirectoryEntry::iterator I = DE->contents_begin(),
709 E = DE->contents_end();
710 I != E; ++I) {
711 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
712 if (Result || Result.getError() != errc::no_such_file_or_directory)
713 return Result;
714 }
715 return error_code(errc::no_such_file_or_directory, system_category());
716}
717
718ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
719 ErrorOr<Entry *> Result = lookupPath(Path);
720 if (!Result)
721 return Result.getError();
722
723 std::string PathStr(Path.str());
724 if (FileEntry *F = dyn_cast<FileEntry>(*Result)) {
725 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
726 if (S) {
727 assert(S->getName() == S->getExternalName() &&
728 S->getName() == F->getExternalContentsPath());
729 S->setName(PathStr);
730 }
731 return S;
732 } else { // directory
733 DirectoryEntry *DE = cast<DirectoryEntry>(*Result);
734 Status S = DE->getStatus();
735 S.setName(PathStr);
736 S.setExternalName(PathStr);
737 return S;
738 }
739}
740
741error_code VFSFromYAML::openFileForRead(const Twine &Path,
742 OwningPtr<vfs::File> &Result) {
743 ErrorOr<Entry *> E = lookupPath(Path);
744 if (!E)
745 return E.getError();
746
747 FileEntry *F = dyn_cast<FileEntry>(*E);
748 if (!F) // FIXME: errc::not_a_file?
749 return error_code(errc::invalid_argument, system_category());
750
751 return ExternalFS->openFileForRead(Path, Result);
752}
753
754IntrusiveRefCntPtr<FileSystem>
755vfs::getVFSFromYAML(MemoryBuffer *Buffer, SourceMgr::DiagHandlerTy DiagHandler,
756 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
757 return VFSFromYAML::create(Buffer, DiagHandler, ExternalFS);
758}
759
760UniqueID vfs::getNextVirtualUniqueID() {
761 static volatile sys::cas_flag UID = 0;
762 sys::cas_flag ID = llvm::sys::AtomicIncrement(&UID);
763 // The following assumes that uint64_t max will never collide with a real
764 // dev_t value from the OS.
765 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
766}