blob: 24454b0192c16562fbac4a58ea637978b2669db7 [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"
Justin Bogner9c785292014-05-20 21:43:27 +000014#include "llvm/ADT/iterator_range.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000015#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/StringExtras.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000017#include "llvm/Support/MemoryBuffer.h"
Ben Langmuirc8130a72014-02-20 21:59:23 +000018#include "llvm/Support/Path.h"
Ben Langmuird51ba0b2014-02-21 23:39:37 +000019#include "llvm/Support/YAMLParser.h"
Benjamin Kramer4527fb22014-03-02 17:08:31 +000020#include <atomic>
Ahmed Charlesdfca6f92014-03-09 11:36:40 +000021#include <memory>
Ben Langmuirc8130a72014-02-20 21:59:23 +000022
23using namespace clang;
24using namespace clang::vfs;
25using namespace llvm;
26using llvm::sys::fs::file_status;
27using llvm::sys::fs::file_type;
28using llvm::sys::fs::perms;
29using llvm::sys::fs::UniqueID;
30
31Status::Status(const file_status &Status)
32 : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
33 User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
34 Type(Status.type()), Perms(Status.permissions()) {}
35
36Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
37 sys::TimeValue MTime, uint32_t User, uint32_t Group,
38 uint64_t Size, file_type Type, perms Perms)
Ben Langmuirb59cf672014-02-27 00:25:12 +000039 : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
40 Type(Type), Perms(Perms) {}
Ben Langmuirc8130a72014-02-20 21:59:23 +000041
42bool Status::equivalent(const Status &Other) const {
43 return getUniqueID() == Other.getUniqueID();
44}
45bool Status::isDirectory() const {
46 return Type == file_type::directory_file;
47}
48bool Status::isRegularFile() const {
49 return Type == file_type::regular_file;
50}
51bool Status::isOther() const {
52 return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
53}
54bool Status::isSymlink() const {
55 return Type == file_type::symlink_file;
56}
57bool Status::isStatusKnown() const {
58 return Type != file_type::status_error;
59}
60bool Status::exists() const {
61 return isStatusKnown() && Type != file_type::file_not_found;
62}
63
64File::~File() {}
65
66FileSystem::~FileSystem() {}
67
68error_code FileSystem::getBufferForFile(const llvm::Twine &Name,
Ahmed Charlesb8984322014-03-07 20:03:18 +000069 std::unique_ptr<MemoryBuffer> &Result,
Ben Langmuirc8130a72014-02-20 21:59:23 +000070 int64_t FileSize,
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +000071 bool RequiresNullTerminator,
72 bool IsVolatile) {
Ahmed Charlesb8984322014-03-07 20:03:18 +000073 std::unique_ptr<File> F;
Ben Langmuirc8130a72014-02-20 21:59:23 +000074 if (error_code EC = openFileForRead(Name, F))
75 return EC;
76
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +000077 error_code EC = F->getBuffer(Name, Result, FileSize, RequiresNullTerminator,
78 IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +000079 return EC;
80}
81
82//===-----------------------------------------------------------------------===/
83// RealFileSystem implementation
84//===-----------------------------------------------------------------------===/
85
Benjamin Kramer3d6220d2014-03-01 17:21:22 +000086namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +000087/// \brief Wrapper around a raw file descriptor.
88class RealFile : public File {
89 int FD;
Ben Langmuird066d4c2014-02-28 21:16:07 +000090 Status S;
Ben Langmuirc8130a72014-02-20 21:59:23 +000091 friend class RealFileSystem;
92 RealFile(int FD) : FD(FD) {
93 assert(FD >= 0 && "Invalid or inactive file descriptor");
94 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +000095
Ben Langmuirc8130a72014-02-20 21:59:23 +000096public:
97 ~RealFile();
Craig Toppera798a9d2014-03-02 09:32:10 +000098 ErrorOr<Status> status() override;
Ahmed Charlesb8984322014-03-07 20:03:18 +000099 error_code getBuffer(const Twine &Name, std::unique_ptr<MemoryBuffer> &Result,
Ben Langmuirc8130a72014-02-20 21:59:23 +0000100 int64_t FileSize = -1,
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +0000101 bool RequiresNullTerminator = true,
102 bool IsVolatile = false) override;
Craig Toppera798a9d2014-03-02 09:32:10 +0000103 error_code close() override;
104 void setName(StringRef Name) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000105};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000106} // end anonymous namespace
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000107RealFile::~RealFile() { close(); }
Ben Langmuirc8130a72014-02-20 21:59:23 +0000108
109ErrorOr<Status> RealFile::status() {
110 assert(FD != -1 && "cannot stat closed file");
Ben Langmuird066d4c2014-02-28 21:16:07 +0000111 if (!S.isStatusKnown()) {
112 file_status RealStatus;
113 if (error_code EC = sys::fs::status(FD, RealStatus))
114 return EC;
115 Status NewS(RealStatus);
116 NewS.setName(S.getName());
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000117 S = std::move(NewS);
Ben Langmuird066d4c2014-02-28 21:16:07 +0000118 }
119 return S;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000120}
121
122error_code RealFile::getBuffer(const Twine &Name,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000123 std::unique_ptr<MemoryBuffer> &Result,
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +0000124 int64_t FileSize, bool RequiresNullTerminator,
125 bool IsVolatile) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000126 assert(FD != -1 && "cannot get buffer for closed file");
127 return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
Argyrios Kyrtzidis26d56392014-05-05 21:57:46 +0000128 RequiresNullTerminator, IsVolatile);
Ben Langmuirc8130a72014-02-20 21:59:23 +0000129}
130
131// FIXME: This is terrible, we need this for ::close.
132#if !defined(_MSC_VER) && !defined(__MINGW32__)
133#include <unistd.h>
134#include <sys/uio.h>
135#else
136#include <io.h>
137#ifndef S_ISFIFO
138#define S_ISFIFO(x) (0)
139#endif
140#endif
141error_code RealFile::close() {
142 if (::close(FD))
143 return error_code(errno, system_category());
144 FD = -1;
145 return error_code::success();
146}
147
Ben Langmuird066d4c2014-02-28 21:16:07 +0000148void RealFile::setName(StringRef Name) {
149 S.setName(Name);
150}
151
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000152namespace {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000153/// \brief The file system according to your operating system.
154class RealFileSystem : public FileSystem {
155public:
Craig Toppera798a9d2014-03-02 09:32:10 +0000156 ErrorOr<Status> status(const Twine &Path) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000157 error_code openFileForRead(const Twine &Path,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000158 std::unique_ptr<File> &Result) override;
Ben Langmuirc8130a72014-02-20 21:59:23 +0000159};
Benjamin Kramer3d6220d2014-03-01 17:21:22 +0000160} // end anonymous namespace
Ben Langmuirc8130a72014-02-20 21:59:23 +0000161
162ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
163 sys::fs::file_status RealStatus;
164 if (error_code EC = sys::fs::status(Path, RealStatus))
165 return EC;
166 Status Result(RealStatus);
167 Result.setName(Path.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000168 return Result;
169}
170
171error_code RealFileSystem::openFileForRead(const Twine &Name,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000172 std::unique_ptr<File> &Result) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000173 int FD;
174 if (error_code EC = sys::fs::openFileForRead(Name, FD))
175 return EC;
176 Result.reset(new RealFile(FD));
Ben Langmuird066d4c2014-02-28 21:16:07 +0000177 Result->setName(Name.str());
Ben Langmuirc8130a72014-02-20 21:59:23 +0000178 return error_code::success();
179}
180
181IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
182 static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
183 return FS;
184}
185
186//===-----------------------------------------------------------------------===/
187// OverlayFileSystem implementation
188//===-----------------------------------------------------------------------===/
189OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
190 pushOverlay(BaseFS);
191}
192
193void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
194 FSList.push_back(FS);
195}
196
197ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
198 // FIXME: handle symlinks that cross file systems
199 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
200 ErrorOr<Status> Status = (*I)->status(Path);
201 if (Status || Status.getError() != errc::no_such_file_or_directory)
202 return Status;
203 }
204 return error_code(errc::no_such_file_or_directory, system_category());
205}
206
207error_code OverlayFileSystem::openFileForRead(const llvm::Twine &Path,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000208 std::unique_ptr<File> &Result) {
Ben Langmuirc8130a72014-02-20 21:59:23 +0000209 // FIXME: handle symlinks that cross file systems
210 for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
211 error_code EC = (*I)->openFileForRead(Path, Result);
212 if (!EC || EC != errc::no_such_file_or_directory)
213 return EC;
214 }
215 return error_code(errc::no_such_file_or_directory, system_category());
216}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000217
218//===-----------------------------------------------------------------------===/
219// VFSFromYAML implementation
220//===-----------------------------------------------------------------------===/
221
222// Allow DenseMap<StringRef, ...>. This is useful below because we know all the
223// strings are literals and will outlive the map, and there is no reason to
224// store them.
225namespace llvm {
226 template<>
227 struct DenseMapInfo<StringRef> {
228 // This assumes that "" will never be a valid key.
229 static inline StringRef getEmptyKey() { return StringRef(""); }
230 static inline StringRef getTombstoneKey() { return StringRef(); }
231 static unsigned getHashValue(StringRef Val) { return HashString(Val); }
232 static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
233 };
234}
235
236namespace {
237
238enum EntryKind {
239 EK_Directory,
240 EK_File
241};
242
243/// \brief A single file or directory in the VFS.
244class Entry {
245 EntryKind Kind;
246 std::string Name;
247
248public:
249 virtual ~Entry();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000250 Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
251 StringRef getName() const { return Name; }
252 EntryKind getKind() const { return Kind; }
253};
254
255class DirectoryEntry : public Entry {
256 std::vector<Entry *> Contents;
257 Status S;
258
259public:
260 virtual ~DirectoryEntry();
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000261 DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
262 : Entry(EK_Directory, Name), Contents(std::move(Contents)),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000263 S(std::move(S)) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000264 Status getStatus() { return S; }
265 typedef std::vector<Entry *>::iterator iterator;
266 iterator contents_begin() { return Contents.begin(); }
267 iterator contents_end() { return Contents.end(); }
268 static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
269};
270
271class FileEntry : public Entry {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000272public:
Ben Langmuirb59cf672014-02-27 00:25:12 +0000273 enum NameKind {
274 NK_NotSet,
275 NK_External,
276 NK_Virtual
277 };
278private:
279 std::string ExternalContentsPath;
280 NameKind UseName;
281public:
282 FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
283 : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
284 UseName(UseName) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000285 StringRef getExternalContentsPath() const { return ExternalContentsPath; }
Ben Langmuirb59cf672014-02-27 00:25:12 +0000286 /// \brief whether to use the external path as the name for this file.
Ben Langmuird066d4c2014-02-28 21:16:07 +0000287 bool useExternalName(bool GlobalUseExternalName) const {
288 return UseName == NK_NotSet ? GlobalUseExternalName
289 : (UseName == NK_External);
290 }
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000291 static bool classof(const Entry *E) { return E->getKind() == EK_File; }
292};
293
294/// \brief A virtual file system parsed from a YAML file.
295///
296/// Currently, this class allows creating virtual directories and mapping
297/// virtual file paths to existing external files, available in \c ExternalFS.
298///
299/// The basic structure of the parsed file is:
300/// \verbatim
301/// {
302/// 'version': <version number>,
303/// <optional configuration>
304/// 'roots': [
305/// <directory entries>
306/// ]
307/// }
308/// \endverbatim
309///
310/// All configuration options are optional.
311/// 'case-sensitive': <boolean, default=true>
Ben Langmuirb59cf672014-02-27 00:25:12 +0000312/// 'use-external-names': <boolean, default=true>
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000313///
314/// Virtual directories are represented as
315/// \verbatim
316/// {
317/// 'type': 'directory',
318/// 'name': <string>,
319/// 'contents': [ <file or directory entries> ]
320/// }
321/// \endverbatim
322///
323/// The default attributes for virtual directories are:
324/// \verbatim
325/// MTime = now() when created
326/// Perms = 0777
327/// User = Group = 0
328/// Size = 0
329/// UniqueID = unspecified unique value
330/// \endverbatim
331///
332/// Re-mapped files are represented as
333/// \verbatim
334/// {
335/// 'type': 'file',
336/// 'name': <string>,
Ben Langmuirb59cf672014-02-27 00:25:12 +0000337/// 'use-external-name': <boolean> # Optional
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000338/// 'external-contents': <path to external file>)
339/// }
340/// \endverbatim
341///
342/// and inherit their attributes from the external contents.
343///
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000344/// In both cases, the 'name' field may contain multiple path components (e.g.
345/// /path/to/file). However, any directory that contains more than one child
346/// must be uniquely represented by a directory entry.
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000347class VFSFromYAML : public vfs::FileSystem {
348 std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
349 /// \brief The file system to use for external references.
350 IntrusiveRefCntPtr<FileSystem> ExternalFS;
351
352 /// @name Configuration
353 /// @{
354
355 /// \brief Whether to perform case-sensitive comparisons.
356 ///
357 /// Currently, case-insensitive matching only works correctly with ASCII.
Ben Langmuirb59cf672014-02-27 00:25:12 +0000358 bool CaseSensitive;
359
360 /// \brief Whether to use to use the value of 'external-contents' for the
361 /// names of files. This global value is overridable on a per-file basis.
362 bool UseExternalNames;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000363 /// @}
364
365 friend class VFSFromYAMLParser;
366
367private:
368 VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
Ben Langmuirb59cf672014-02-27 00:25:12 +0000369 : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000370
371 /// \brief Looks up \p Path in \c Roots.
372 ErrorOr<Entry *> lookupPath(const Twine &Path);
373
374 /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
375 /// recursing into the contents of \p From if it is a directory.
376 ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
377 sys::path::const_iterator End, Entry *From);
378
379public:
380 ~VFSFromYAML();
381
382 /// \brief Parses \p Buffer, which is expected to be in YAML format and
383 /// returns a virtual file system representing its contents.
384 ///
385 /// Takes ownership of \p Buffer.
386 static VFSFromYAML *create(MemoryBuffer *Buffer,
387 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000388 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000389 IntrusiveRefCntPtr<FileSystem> ExternalFS);
390
Craig Toppera798a9d2014-03-02 09:32:10 +0000391 ErrorOr<Status> status(const Twine &Path) override;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000392 error_code openFileForRead(const Twine &Path,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000393 std::unique_ptr<File> &Result) override;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000394};
395
396/// \brief A helper class to hold the common YAML parsing state.
397class VFSFromYAMLParser {
398 yaml::Stream &Stream;
399
400 void error(yaml::Node *N, const Twine &Msg) {
401 Stream.printError(N, Msg);
402 }
403
404 // false on error
405 bool parseScalarString(yaml::Node *N, StringRef &Result,
406 SmallVectorImpl<char> &Storage) {
407 yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
408 if (!S) {
409 error(N, "expected string");
410 return false;
411 }
412 Result = S->getValue(Storage);
413 return true;
414 }
415
416 // false on error
417 bool parseScalarBool(yaml::Node *N, bool &Result) {
418 SmallString<5> Storage;
419 StringRef Value;
420 if (!parseScalarString(N, Value, Storage))
421 return false;
422
423 if (Value.equals_lower("true") || Value.equals_lower("on") ||
424 Value.equals_lower("yes") || Value == "1") {
425 Result = true;
426 return true;
427 } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
428 Value.equals_lower("no") || Value == "0") {
429 Result = false;
430 return true;
431 }
432
433 error(N, "expected boolean value");
434 return false;
435 }
436
437 struct KeyStatus {
438 KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
439 bool Required;
440 bool Seen;
441 };
442 typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
443
444 // false on error
445 bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
446 DenseMap<StringRef, KeyStatus> &Keys) {
447 if (!Keys.count(Key)) {
448 error(KeyNode, "unknown key");
449 return false;
450 }
451 KeyStatus &S = Keys[Key];
452 if (S.Seen) {
453 error(KeyNode, Twine("duplicate key '") + Key + "'");
454 return false;
455 }
456 S.Seen = true;
457 return true;
458 }
459
460 // false on error
461 bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
462 for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
463 E = Keys.end();
464 I != E; ++I) {
465 if (I->second.Required && !I->second.Seen) {
466 error(Obj, Twine("missing key '") + I->first + "'");
467 return false;
468 }
469 }
470 return true;
471 }
472
473 Entry *parseEntry(yaml::Node *N) {
474 yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
475 if (!M) {
476 error(N, "expected mapping node for file or directory entry");
Craig Topperf1186c52014-05-08 06:41:40 +0000477 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000478 }
479
480 KeyStatusPair Fields[] = {
481 KeyStatusPair("name", true),
482 KeyStatusPair("type", true),
483 KeyStatusPair("contents", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000484 KeyStatusPair("external-contents", false),
485 KeyStatusPair("use-external-name", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000486 };
487
488 DenseMap<StringRef, KeyStatus> Keys(
489 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
490
491 bool HasContents = false; // external or otherwise
492 std::vector<Entry *> EntryArrayContents;
493 std::string ExternalContentsPath;
494 std::string Name;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000495 FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000496 EntryKind Kind;
497
498 for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
499 ++I) {
500 StringRef Key;
501 // Reuse the buffer for key and value, since we don't look at key after
502 // parsing value.
503 SmallString<256> Buffer;
504 if (!parseScalarString(I->getKey(), Key, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000505 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000506
507 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000508 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000509
510 StringRef Value;
511 if (Key == "name") {
512 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000513 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000514 Name = Value;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000515 } else if (Key == "type") {
516 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000517 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000518 if (Value == "file")
519 Kind = EK_File;
520 else if (Value == "directory")
521 Kind = EK_Directory;
522 else {
523 error(I->getValue(), "unknown value for 'type'");
Craig Topperf1186c52014-05-08 06:41:40 +0000524 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000525 }
526 } else if (Key == "contents") {
527 if (HasContents) {
528 error(I->getKey(),
529 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000530 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000531 }
532 HasContents = true;
533 yaml::SequenceNode *Contents =
534 dyn_cast<yaml::SequenceNode>(I->getValue());
535 if (!Contents) {
536 // FIXME: this is only for directories, what about files?
537 error(I->getValue(), "expected array");
Craig Topperf1186c52014-05-08 06:41:40 +0000538 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000539 }
540
541 for (yaml::SequenceNode::iterator I = Contents->begin(),
542 E = Contents->end();
543 I != E; ++I) {
544 if (Entry *E = parseEntry(&*I))
545 EntryArrayContents.push_back(E);
546 else
Craig Topperf1186c52014-05-08 06:41:40 +0000547 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000548 }
549 } else if (Key == "external-contents") {
550 if (HasContents) {
551 error(I->getKey(),
552 "entry already has 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000553 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000554 }
555 HasContents = true;
556 if (!parseScalarString(I->getValue(), Value, Buffer))
Craig Topperf1186c52014-05-08 06:41:40 +0000557 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000558 ExternalContentsPath = Value;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000559 } else if (Key == "use-external-name") {
560 bool Val;
561 if (!parseScalarBool(I->getValue(), Val))
Craig Topperf1186c52014-05-08 06:41:40 +0000562 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000563 UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000564 } else {
565 llvm_unreachable("key missing from Keys");
566 }
567 }
568
569 if (Stream.failed())
Craig Topperf1186c52014-05-08 06:41:40 +0000570 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000571
572 // check for missing keys
573 if (!HasContents) {
574 error(N, "missing key 'contents' or 'external-contents'");
Craig Topperf1186c52014-05-08 06:41:40 +0000575 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000576 }
577 if (!checkMissingKeys(N, Keys))
Craig Topperf1186c52014-05-08 06:41:40 +0000578 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000579
Ben Langmuirb59cf672014-02-27 00:25:12 +0000580 // check invalid configuration
581 if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
582 error(N, "'use-external-name' is not supported for directories");
Craig Topperf1186c52014-05-08 06:41:40 +0000583 return nullptr;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000584 }
585
Ben Langmuir93853232014-03-05 21:32:20 +0000586 // Remove trailing slash(es), being careful not to remove the root path
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000587 StringRef Trimmed(Name);
Ben Langmuir93853232014-03-05 21:32:20 +0000588 size_t RootPathLen = sys::path::root_path(Trimmed).size();
589 while (Trimmed.size() > RootPathLen &&
590 sys::path::is_separator(Trimmed.back()))
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000591 Trimmed = Trimmed.slice(0, Trimmed.size()-1);
592 // Get the last component
593 StringRef LastComponent = sys::path::filename(Trimmed);
594
Craig Topperf1186c52014-05-08 06:41:40 +0000595 Entry *Result = nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000596 switch (Kind) {
597 case EK_File:
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000598 Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000599 UseExternalName);
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000600 break;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000601 case EK_Directory:
Chandler Carruthc72d9b32014-03-02 04:02:40 +0000602 Result = new DirectoryEntry(LastComponent, std::move(EntryArrayContents),
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000603 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
604 0, file_type::directory_file, sys::fs::all_all));
605 break;
606 }
607
608 StringRef Parent = sys::path::parent_path(Trimmed);
609 if (Parent.empty())
610 return Result;
611
612 // if 'name' contains multiple components, create implicit directory entries
613 for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
614 E = sys::path::rend(Parent);
615 I != E; ++I) {
Benjamin Kramer3f755aa2014-03-10 17:55:02 +0000616 Result = new DirectoryEntry(*I, llvm::makeArrayRef(Result),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000617 Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
618 0, file_type::directory_file, sys::fs::all_all));
619 }
Ben Langmuir47ff9ab2014-02-25 04:34:14 +0000620 return Result;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000621 }
622
623public:
624 VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
625
626 // false on error
627 bool parse(yaml::Node *Root, VFSFromYAML *FS) {
628 yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
629 if (!Top) {
630 error(Root, "expected mapping node");
631 return false;
632 }
633
634 KeyStatusPair Fields[] = {
635 KeyStatusPair("version", true),
636 KeyStatusPair("case-sensitive", false),
Ben Langmuirb59cf672014-02-27 00:25:12 +0000637 KeyStatusPair("use-external-names", false),
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000638 KeyStatusPair("roots", true),
639 };
640
641 DenseMap<StringRef, KeyStatus> Keys(
642 &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
643
644 // Parse configuration and 'roots'
645 for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
646 ++I) {
647 SmallString<10> KeyBuffer;
648 StringRef Key;
649 if (!parseScalarString(I->getKey(), Key, KeyBuffer))
650 return false;
651
652 if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
653 return false;
654
655 if (Key == "roots") {
656 yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
657 if (!Roots) {
658 error(I->getValue(), "expected array");
659 return false;
660 }
661
662 for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
663 I != E; ++I) {
664 if (Entry *E = parseEntry(&*I))
665 FS->Roots.push_back(E);
666 else
667 return false;
668 }
669 } else if (Key == "version") {
670 StringRef VersionString;
671 SmallString<4> Storage;
672 if (!parseScalarString(I->getValue(), VersionString, Storage))
673 return false;
674 int Version;
675 if (VersionString.getAsInteger<int>(10, Version)) {
676 error(I->getValue(), "expected integer");
677 return false;
678 }
679 if (Version < 0) {
680 error(I->getValue(), "invalid version number");
681 return false;
682 }
683 if (Version != 0) {
684 error(I->getValue(), "version mismatch, expected 0");
685 return false;
686 }
687 } else if (Key == "case-sensitive") {
688 if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
689 return false;
Ben Langmuirb59cf672014-02-27 00:25:12 +0000690 } else if (Key == "use-external-names") {
691 if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
692 return false;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000693 } else {
694 llvm_unreachable("key missing from Keys");
695 }
696 }
697
698 if (Stream.failed())
699 return false;
700
701 if (!checkMissingKeys(Top, Keys))
702 return false;
703 return true;
704 }
705};
706} // end of anonymous namespace
707
708Entry::~Entry() {}
709DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
710
711VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
712
713VFSFromYAML *VFSFromYAML::create(MemoryBuffer *Buffer,
714 SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000715 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000716 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
717
718 SourceMgr SM;
719 yaml::Stream Stream(Buffer, SM);
720
Ben Langmuir97882e72014-02-24 20:56:37 +0000721 SM.setDiagHandler(DiagHandler, DiagContext);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000722 yaml::document_iterator DI = Stream.begin();
723 yaml::Node *Root = DI->getRoot();
724 if (DI == Stream.end() || !Root) {
725 SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
Craig Topperf1186c52014-05-08 06:41:40 +0000726 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000727 }
728
729 VFSFromYAMLParser P(Stream);
730
Ahmed Charlesb8984322014-03-07 20:03:18 +0000731 std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000732 if (!P.parse(Root, FS.get()))
Craig Topperf1186c52014-05-08 06:41:40 +0000733 return nullptr;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000734
Ahmed Charles9a16beb2014-03-07 19:33:25 +0000735 return FS.release();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000736}
737
738ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +0000739 SmallString<256> Path;
740 Path_.toVector(Path);
741
742 // Handle relative paths
743 if (error_code EC = sys::fs::make_absolute(Path))
744 return EC;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000745
746 if (Path.empty())
747 return error_code(errc::invalid_argument, system_category());
748
749 sys::path::const_iterator Start = sys::path::begin(Path);
750 sys::path::const_iterator End = sys::path::end(Path);
751 for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
752 I != E; ++I) {
753 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
754 if (Result || Result.getError() != errc::no_such_file_or_directory)
755 return Result;
756 }
757 return error_code(errc::no_such_file_or_directory, system_category());
758}
759
760ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
761 sys::path::const_iterator End,
762 Entry *From) {
Ben Langmuira6f8ca82014-03-04 22:34:50 +0000763 if (Start->equals("."))
764 ++Start;
765
766 // FIXME: handle ..
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000767 if (CaseSensitive ? !Start->equals(From->getName())
768 : !Start->equals_lower(From->getName()))
769 // failure to match
770 return error_code(errc::no_such_file_or_directory, system_category());
771
772 ++Start;
773
774 if (Start == End) {
775 // Match!
776 return From;
777 }
778
779 DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
780 if (!DE)
781 return error_code(errc::not_a_directory, system_category());
782
783 for (DirectoryEntry::iterator I = DE->contents_begin(),
784 E = DE->contents_end();
785 I != E; ++I) {
786 ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
787 if (Result || Result.getError() != errc::no_such_file_or_directory)
788 return Result;
789 }
790 return error_code(errc::no_such_file_or_directory, system_category());
791}
792
793ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
794 ErrorOr<Entry *> Result = lookupPath(Path);
795 if (!Result)
796 return Result.getError();
797
798 std::string PathStr(Path.str());
799 if (FileEntry *F = dyn_cast<FileEntry>(*Result)) {
800 ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
Ben Langmuirb59cf672014-02-27 00:25:12 +0000801 assert(!S || S->getName() == F->getExternalContentsPath());
Ben Langmuird066d4c2014-02-28 21:16:07 +0000802 if (S && !F->useExternalName(UseExternalNames))
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000803 S->setName(PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000804 return S;
805 } else { // directory
806 DirectoryEntry *DE = cast<DirectoryEntry>(*Result);
807 Status S = DE->getStatus();
808 S.setName(PathStr);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000809 return S;
810 }
811}
812
813error_code VFSFromYAML::openFileForRead(const Twine &Path,
Ahmed Charlesb8984322014-03-07 20:03:18 +0000814 std::unique_ptr<vfs::File> &Result) {
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000815 ErrorOr<Entry *> E = lookupPath(Path);
816 if (!E)
817 return E.getError();
818
819 FileEntry *F = dyn_cast<FileEntry>(*E);
820 if (!F) // FIXME: errc::not_a_file?
821 return error_code(errc::invalid_argument, system_category());
822
Ben Langmuird066d4c2014-02-28 21:16:07 +0000823 if (error_code EC = ExternalFS->openFileForRead(F->getExternalContentsPath(),
824 Result))
825 return EC;
826
827 if (!F->useExternalName(UseExternalNames))
828 Result->setName(Path.str());
829
830 return error_code::success();
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000831}
832
833IntrusiveRefCntPtr<FileSystem>
834vfs::getVFSFromYAML(MemoryBuffer *Buffer, SourceMgr::DiagHandlerTy DiagHandler,
Ben Langmuir97882e72014-02-24 20:56:37 +0000835 void *DiagContext,
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000836 IntrusiveRefCntPtr<FileSystem> ExternalFS) {
Ben Langmuir97882e72014-02-24 20:56:37 +0000837 return VFSFromYAML::create(Buffer, DiagHandler, DiagContext, ExternalFS);
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000838}
839
840UniqueID vfs::getNextVirtualUniqueID() {
Benjamin Kramer4527fb22014-03-02 17:08:31 +0000841 static std::atomic<unsigned> UID;
842 unsigned ID = ++UID;
Ben Langmuird51ba0b2014-02-21 23:39:37 +0000843 // The following assumes that uint64_t max will never collide with a real
844 // dev_t value from the OS.
845 return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
846}
Justin Bogner9c785292014-05-20 21:43:27 +0000847
848#ifndef NDEBUG
849static bool pathHasTraversal(StringRef Path) {
850 using namespace llvm::sys;
851 for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path)))
852 if (Comp == "." || Comp == "..")
853 return true;
854 return false;
855}
856#endif
857
858void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) {
859 assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute");
860 assert(sys::path::is_absolute(RealPath) && "real path not absolute");
861 assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported");
862 Mappings.emplace_back(VirtualPath, RealPath);
863}
864
865ArrayRef<YAMLVFSWriter::MapEntry>
866YAMLVFSWriter::printDirNodes(llvm::raw_ostream &OS, ArrayRef<MapEntry> Entries,
867 StringRef ParentPath, unsigned Indent) {
868 while (!Entries.empty()) {
869 const MapEntry &Entry = Entries.front();
870 OS.indent(Indent) << "{\n";
871 Indent += 2;
872 OS.indent(Indent) << "'type': 'directory',\n";
873 StringRef DirName =
874 containedPart(ParentPath, sys::path::parent_path(Entry.VPath));
875 OS.indent(Indent)
876 << "'name': \"" << llvm::yaml::escape(DirName) << "\",\n";
877 OS.indent(Indent) << "'contents': [\n";
878 Entries = printContents(OS, Entries, Indent + 2);
879 OS.indent(Indent) << "]\n";
880 Indent -= 2;
881 OS.indent(Indent) << '}';
882 if (Entries.empty()) {
883 OS << '\n';
884 break;
885 }
886 StringRef NextVPath = Entries.front().VPath;
887 if (!containedIn(ParentPath, NextVPath)) {
888 OS << '\n';
889 break;
890 }
891 OS << ",\n";
892 }
893 return Entries;
894}
895
896ArrayRef<YAMLVFSWriter::MapEntry>
897YAMLVFSWriter::printContents(llvm::raw_ostream &OS, ArrayRef<MapEntry> Entries,
898 unsigned Indent) {
899 using namespace llvm::sys;
900 while (!Entries.empty()) {
901 const MapEntry &Entry = Entries.front();
902 Entries = Entries.slice(1);
903 StringRef ParentPath = path::parent_path(Entry.VPath);
904 StringRef VName = path::filename(Entry.VPath);
905 OS.indent(Indent) << "{\n";
906 Indent += 2;
907 OS.indent(Indent) << "'type': 'file',\n";
908 OS.indent(Indent) << "'name': \"" << llvm::yaml::escape(VName) << "\",\n";
909 OS.indent(Indent) << "'external-contents': \""
910 << llvm::yaml::escape(Entry.RPath) << "\"\n";
911 Indent -= 2;
912 OS.indent(Indent) << '}';
913 if (Entries.empty()) {
914 OS << '\n';
915 break;
916 }
917 StringRef NextVPath = Entries.front().VPath;
918 if (!containedIn(ParentPath, NextVPath)) {
919 OS << '\n';
920 break;
921 }
922 OS << ",\n";
923 if (path::parent_path(NextVPath) != ParentPath) {
924 Entries = printDirNodes(OS, Entries, ParentPath, Indent);
925 }
926 }
927 return Entries;
928}
929
930bool YAMLVFSWriter::containedIn(StringRef Parent, StringRef Path) {
931 return Path.startswith(Parent);
932}
933
934StringRef YAMLVFSWriter::containedPart(StringRef Parent, StringRef Path) {
935 assert(containedIn(Parent, Path));
936 if (Parent.empty())
937 return Path;
938 return Path.slice(Parent.size() + 1, StringRef::npos);
939}
940
941void YAMLVFSWriter::write(llvm::raw_ostream &OS) {
942 std::sort(Mappings.begin(), Mappings.end(),
943 [](const MapEntry &LHS, const MapEntry &RHS) {
944 return LHS.VPath < RHS.VPath;
945 });
946
947 OS << "{\n"
948 " 'version': 0,\n";
949 if (IsCaseSensitive.hasValue()) {
950 OS << " 'case-sensitive': '";
951 if (IsCaseSensitive.getValue())
952 OS << "true";
953 else
954 OS << "false";
955 OS << "',\n";
956 }
957 OS << " 'roots': [\n";
958 printDirNodes(OS, Mappings, "", 4);
959 OS << " ]\n"
960 << "}\n";
961}