123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841 |
- //===- VirtualFileSystem.cpp - Virtual File System Layer --------*- C++ -*-===//
- //
- // The LLVM Compiler Infrastructure
- //
- // This file is distributed under the University of Illinois Open Source
- // License. See LICENSE.TXT for details.
- //
- //===----------------------------------------------------------------------===//
- // This file implements the VirtualFileSystem interface.
- //===----------------------------------------------------------------------===//
- #include "clang/Basic/VirtualFileSystem.h"
- #include "llvm/ADT/DenseMap.h"
- #include "llvm/ADT/STLExtras.h"
- #include "llvm/ADT/StringExtras.h"
- #include "llvm/Support/MemoryBuffer.h"
- #include "llvm/Support/Path.h"
- #include "llvm/Support/YAMLParser.h"
- #include <atomic>
- #include <memory>
- using namespace clang;
- using namespace clang::vfs;
- using namespace llvm;
- using llvm::sys::fs::file_status;
- using llvm::sys::fs::file_type;
- using llvm::sys::fs::perms;
- using llvm::sys::fs::UniqueID;
- Status::Status(const file_status &Status)
- : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
- User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
- Type(Status.type()), Perms(Status.permissions()) {}
- Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID,
- sys::TimeValue MTime, uint32_t User, uint32_t Group,
- uint64_t Size, file_type Type, perms Perms)
- : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),
- Type(Type), Perms(Perms) {}
- bool Status::equivalent(const Status &Other) const {
- return getUniqueID() == Other.getUniqueID();
- }
- bool Status::isDirectory() const {
- return Type == file_type::directory_file;
- }
- bool Status::isRegularFile() const {
- return Type == file_type::regular_file;
- }
- bool Status::isOther() const {
- return exists() && !isRegularFile() && !isDirectory() && !isSymlink();
- }
- bool Status::isSymlink() const {
- return Type == file_type::symlink_file;
- }
- bool Status::isStatusKnown() const {
- return Type != file_type::status_error;
- }
- bool Status::exists() const {
- return isStatusKnown() && Type != file_type::file_not_found;
- }
- File::~File() {}
- FileSystem::~FileSystem() {}
- error_code FileSystem::getBufferForFile(const llvm::Twine &Name,
- std::unique_ptr<MemoryBuffer> &Result,
- int64_t FileSize,
- bool RequiresNullTerminator) {
- std::unique_ptr<File> F;
- if (error_code EC = openFileForRead(Name, F))
- return EC;
- error_code EC = F->getBuffer(Name, Result, FileSize, RequiresNullTerminator);
- return EC;
- }
- //===-----------------------------------------------------------------------===/
- // RealFileSystem implementation
- //===-----------------------------------------------------------------------===/
- namespace {
- /// \brief Wrapper around a raw file descriptor.
- class RealFile : public File {
- int FD;
- Status S;
- friend class RealFileSystem;
- RealFile(int FD) : FD(FD) {
- assert(FD >= 0 && "Invalid or inactive file descriptor");
- }
- public:
- ~RealFile();
- ErrorOr<Status> status() override;
- error_code getBuffer(const Twine &Name, std::unique_ptr<MemoryBuffer> &Result,
- int64_t FileSize = -1,
- bool RequiresNullTerminator = true) override;
- error_code close() override;
- void setName(StringRef Name) override;
- };
- } // end anonymous namespace
- RealFile::~RealFile() { close(); }
- ErrorOr<Status> RealFile::status() {
- assert(FD != -1 && "cannot stat closed file");
- if (!S.isStatusKnown()) {
- file_status RealStatus;
- if (error_code EC = sys::fs::status(FD, RealStatus))
- return EC;
- Status NewS(RealStatus);
- NewS.setName(S.getName());
- S = std::move(NewS);
- }
- return S;
- }
- error_code RealFile::getBuffer(const Twine &Name,
- std::unique_ptr<MemoryBuffer> &Result,
- int64_t FileSize, bool RequiresNullTerminator) {
- assert(FD != -1 && "cannot get buffer for closed file");
- return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
- RequiresNullTerminator);
- }
- // FIXME: This is terrible, we need this for ::close.
- #if !defined(_MSC_VER) && !defined(__MINGW32__)
- #include <unistd.h>
- #include <sys/uio.h>
- #else
- #include <io.h>
- #ifndef S_ISFIFO
- #define S_ISFIFO(x) (0)
- #endif
- #endif
- error_code RealFile::close() {
- if (::close(FD))
- return error_code(errno, system_category());
- FD = -1;
- return error_code::success();
- }
- void RealFile::setName(StringRef Name) {
- S.setName(Name);
- }
- namespace {
- /// \brief The file system according to your operating system.
- class RealFileSystem : public FileSystem {
- public:
- ErrorOr<Status> status(const Twine &Path) override;
- error_code openFileForRead(const Twine &Path,
- std::unique_ptr<File> &Result) override;
- };
- } // end anonymous namespace
- ErrorOr<Status> RealFileSystem::status(const Twine &Path) {
- sys::fs::file_status RealStatus;
- if (error_code EC = sys::fs::status(Path, RealStatus))
- return EC;
- Status Result(RealStatus);
- Result.setName(Path.str());
- return Result;
- }
- error_code RealFileSystem::openFileForRead(const Twine &Name,
- std::unique_ptr<File> &Result) {
- int FD;
- if (error_code EC = sys::fs::openFileForRead(Name, FD))
- return EC;
- Result.reset(new RealFile(FD));
- Result->setName(Name.str());
- return error_code::success();
- }
- IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
- static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
- return FS;
- }
- //===-----------------------------------------------------------------------===/
- // OverlayFileSystem implementation
- //===-----------------------------------------------------------------------===/
- OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
- pushOverlay(BaseFS);
- }
- void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
- FSList.push_back(FS);
- }
- ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
- // FIXME: handle symlinks that cross file systems
- for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
- ErrorOr<Status> Status = (*I)->status(Path);
- if (Status || Status.getError() != errc::no_such_file_or_directory)
- return Status;
- }
- return error_code(errc::no_such_file_or_directory, system_category());
- }
- error_code OverlayFileSystem::openFileForRead(const llvm::Twine &Path,
- std::unique_ptr<File> &Result) {
- // FIXME: handle symlinks that cross file systems
- for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) {
- error_code EC = (*I)->openFileForRead(Path, Result);
- if (!EC || EC != errc::no_such_file_or_directory)
- return EC;
- }
- return error_code(errc::no_such_file_or_directory, system_category());
- }
- //===-----------------------------------------------------------------------===/
- // VFSFromYAML implementation
- //===-----------------------------------------------------------------------===/
- // Allow DenseMap<StringRef, ...>. This is useful below because we know all the
- // strings are literals and will outlive the map, and there is no reason to
- // store them.
- namespace llvm {
- template<>
- struct DenseMapInfo<StringRef> {
- // This assumes that "" will never be a valid key.
- static inline StringRef getEmptyKey() { return StringRef(""); }
- static inline StringRef getTombstoneKey() { return StringRef(); }
- static unsigned getHashValue(StringRef Val) { return HashString(Val); }
- static bool isEqual(StringRef LHS, StringRef RHS) { return LHS == RHS; }
- };
- }
- namespace {
- enum EntryKind {
- EK_Directory,
- EK_File
- };
- /// \brief A single file or directory in the VFS.
- class Entry {
- EntryKind Kind;
- std::string Name;
- public:
- virtual ~Entry();
- Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
- StringRef getName() const { return Name; }
- EntryKind getKind() const { return Kind; }
- };
- class DirectoryEntry : public Entry {
- std::vector<Entry *> Contents;
- Status S;
- public:
- virtual ~DirectoryEntry();
- DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
- : Entry(EK_Directory, Name), Contents(std::move(Contents)),
- S(std::move(S)) {}
- Status getStatus() { return S; }
- typedef std::vector<Entry *>::iterator iterator;
- iterator contents_begin() { return Contents.begin(); }
- iterator contents_end() { return Contents.end(); }
- static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }
- };
- class FileEntry : public Entry {
- public:
- enum NameKind {
- NK_NotSet,
- NK_External,
- NK_Virtual
- };
- private:
- std::string ExternalContentsPath;
- NameKind UseName;
- public:
- FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName)
- : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),
- UseName(UseName) {}
- StringRef getExternalContentsPath() const { return ExternalContentsPath; }
- /// \brief whether to use the external path as the name for this file.
- bool useExternalName(bool GlobalUseExternalName) const {
- return UseName == NK_NotSet ? GlobalUseExternalName
- : (UseName == NK_External);
- }
- static bool classof(const Entry *E) { return E->getKind() == EK_File; }
- };
- /// \brief A virtual file system parsed from a YAML file.
- ///
- /// Currently, this class allows creating virtual directories and mapping
- /// virtual file paths to existing external files, available in \c ExternalFS.
- ///
- /// The basic structure of the parsed file is:
- /// \verbatim
- /// {
- /// 'version': <version number>,
- /// <optional configuration>
- /// 'roots': [
- /// <directory entries>
- /// ]
- /// }
- /// \endverbatim
- ///
- /// All configuration options are optional.
- /// 'case-sensitive': <boolean, default=true>
- /// 'use-external-names': <boolean, default=true>
- ///
- /// Virtual directories are represented as
- /// \verbatim
- /// {
- /// 'type': 'directory',
- /// 'name': <string>,
- /// 'contents': [ <file or directory entries> ]
- /// }
- /// \endverbatim
- ///
- /// The default attributes for virtual directories are:
- /// \verbatim
- /// MTime = now() when created
- /// Perms = 0777
- /// User = Group = 0
- /// Size = 0
- /// UniqueID = unspecified unique value
- /// \endverbatim
- ///
- /// Re-mapped files are represented as
- /// \verbatim
- /// {
- /// 'type': 'file',
- /// 'name': <string>,
- /// 'use-external-name': <boolean> # Optional
- /// 'external-contents': <path to external file>)
- /// }
- /// \endverbatim
- ///
- /// and inherit their attributes from the external contents.
- ///
- /// In both cases, the 'name' field may contain multiple path components (e.g.
- /// /path/to/file). However, any directory that contains more than one child
- /// must be uniquely represented by a directory entry.
- class VFSFromYAML : public vfs::FileSystem {
- std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
- /// \brief The file system to use for external references.
- IntrusiveRefCntPtr<FileSystem> ExternalFS;
- /// @name Configuration
- /// @{
- /// \brief Whether to perform case-sensitive comparisons.
- ///
- /// Currently, case-insensitive matching only works correctly with ASCII.
- bool CaseSensitive;
- /// \brief Whether to use to use the value of 'external-contents' for the
- /// names of files. This global value is overridable on a per-file basis.
- bool UseExternalNames;
- /// @}
- friend class VFSFromYAMLParser;
- private:
- VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS)
- : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}
- /// \brief Looks up \p Path in \c Roots.
- ErrorOr<Entry *> lookupPath(const Twine &Path);
- /// \brief Looks up the path <tt>[Start, End)</tt> in \p From, possibly
- /// recursing into the contents of \p From if it is a directory.
- ErrorOr<Entry *> lookupPath(sys::path::const_iterator Start,
- sys::path::const_iterator End, Entry *From);
- public:
- ~VFSFromYAML();
- /// \brief Parses \p Buffer, which is expected to be in YAML format and
- /// returns a virtual file system representing its contents.
- ///
- /// Takes ownership of \p Buffer.
- static VFSFromYAML *create(MemoryBuffer *Buffer,
- SourceMgr::DiagHandlerTy DiagHandler,
- void *DiagContext,
- IntrusiveRefCntPtr<FileSystem> ExternalFS);
- ErrorOr<Status> status(const Twine &Path) override;
- error_code openFileForRead(const Twine &Path,
- std::unique_ptr<File> &Result) override;
- };
- /// \brief A helper class to hold the common YAML parsing state.
- class VFSFromYAMLParser {
- yaml::Stream &Stream;
- void error(yaml::Node *N, const Twine &Msg) {
- Stream.printError(N, Msg);
- }
- // false on error
- bool parseScalarString(yaml::Node *N, StringRef &Result,
- SmallVectorImpl<char> &Storage) {
- yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
- if (!S) {
- error(N, "expected string");
- return false;
- }
- Result = S->getValue(Storage);
- return true;
- }
- // false on error
- bool parseScalarBool(yaml::Node *N, bool &Result) {
- SmallString<5> Storage;
- StringRef Value;
- if (!parseScalarString(N, Value, Storage))
- return false;
- if (Value.equals_lower("true") || Value.equals_lower("on") ||
- Value.equals_lower("yes") || Value == "1") {
- Result = true;
- return true;
- } else if (Value.equals_lower("false") || Value.equals_lower("off") ||
- Value.equals_lower("no") || Value == "0") {
- Result = false;
- return true;
- }
- error(N, "expected boolean value");
- return false;
- }
- struct KeyStatus {
- KeyStatus(bool Required=false) : Required(Required), Seen(false) {}
- bool Required;
- bool Seen;
- };
- typedef std::pair<StringRef, KeyStatus> KeyStatusPair;
- // false on error
- bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key,
- DenseMap<StringRef, KeyStatus> &Keys) {
- if (!Keys.count(Key)) {
- error(KeyNode, "unknown key");
- return false;
- }
- KeyStatus &S = Keys[Key];
- if (S.Seen) {
- error(KeyNode, Twine("duplicate key '") + Key + "'");
- return false;
- }
- S.Seen = true;
- return true;
- }
- // false on error
- bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) {
- for (DenseMap<StringRef, KeyStatus>::iterator I = Keys.begin(),
- E = Keys.end();
- I != E; ++I) {
- if (I->second.Required && !I->second.Seen) {
- error(Obj, Twine("missing key '") + I->first + "'");
- return false;
- }
- }
- return true;
- }
- Entry *parseEntry(yaml::Node *N) {
- yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
- if (!M) {
- error(N, "expected mapping node for file or directory entry");
- return NULL;
- }
- KeyStatusPair Fields[] = {
- KeyStatusPair("name", true),
- KeyStatusPair("type", true),
- KeyStatusPair("contents", false),
- KeyStatusPair("external-contents", false),
- KeyStatusPair("use-external-name", false),
- };
- DenseMap<StringRef, KeyStatus> Keys(
- &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
- bool HasContents = false; // external or otherwise
- std::vector<Entry *> EntryArrayContents;
- std::string ExternalContentsPath;
- std::string Name;
- FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet;
- EntryKind Kind;
- for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E;
- ++I) {
- StringRef Key;
- // Reuse the buffer for key and value, since we don't look at key after
- // parsing value.
- SmallString<256> Buffer;
- if (!parseScalarString(I->getKey(), Key, Buffer))
- return NULL;
- if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
- return NULL;
- StringRef Value;
- if (Key == "name") {
- if (!parseScalarString(I->getValue(), Value, Buffer))
- return NULL;
- Name = Value;
- } else if (Key == "type") {
- if (!parseScalarString(I->getValue(), Value, Buffer))
- return NULL;
- if (Value == "file")
- Kind = EK_File;
- else if (Value == "directory")
- Kind = EK_Directory;
- else {
- error(I->getValue(), "unknown value for 'type'");
- return NULL;
- }
- } else if (Key == "contents") {
- if (HasContents) {
- error(I->getKey(),
- "entry already has 'contents' or 'external-contents'");
- return NULL;
- }
- HasContents = true;
- yaml::SequenceNode *Contents =
- dyn_cast<yaml::SequenceNode>(I->getValue());
- if (!Contents) {
- // FIXME: this is only for directories, what about files?
- error(I->getValue(), "expected array");
- return NULL;
- }
- for (yaml::SequenceNode::iterator I = Contents->begin(),
- E = Contents->end();
- I != E; ++I) {
- if (Entry *E = parseEntry(&*I))
- EntryArrayContents.push_back(E);
- else
- return NULL;
- }
- } else if (Key == "external-contents") {
- if (HasContents) {
- error(I->getKey(),
- "entry already has 'contents' or 'external-contents'");
- return NULL;
- }
- HasContents = true;
- if (!parseScalarString(I->getValue(), Value, Buffer))
- return NULL;
- ExternalContentsPath = Value;
- } else if (Key == "use-external-name") {
- bool Val;
- if (!parseScalarBool(I->getValue(), Val))
- return NULL;
- UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual;
- } else {
- llvm_unreachable("key missing from Keys");
- }
- }
- if (Stream.failed())
- return NULL;
- // check for missing keys
- if (!HasContents) {
- error(N, "missing key 'contents' or 'external-contents'");
- return NULL;
- }
- if (!checkMissingKeys(N, Keys))
- return NULL;
- // check invalid configuration
- if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) {
- error(N, "'use-external-name' is not supported for directories");
- return NULL;
- }
- // Remove trailing slash(es), being careful not to remove the root path
- StringRef Trimmed(Name);
- size_t RootPathLen = sys::path::root_path(Trimmed).size();
- while (Trimmed.size() > RootPathLen &&
- sys::path::is_separator(Trimmed.back()))
- Trimmed = Trimmed.slice(0, Trimmed.size()-1);
- // Get the last component
- StringRef LastComponent = sys::path::filename(Trimmed);
- Entry *Result = 0;
- switch (Kind) {
- case EK_File:
- Result = new FileEntry(LastComponent, std::move(ExternalContentsPath),
- UseExternalName);
- break;
- case EK_Directory:
- Result = new DirectoryEntry(LastComponent, std::move(EntryArrayContents),
- Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
- 0, file_type::directory_file, sys::fs::all_all));
- break;
- }
- StringRef Parent = sys::path::parent_path(Trimmed);
- if (Parent.empty())
- return Result;
- // if 'name' contains multiple components, create implicit directory entries
- for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
- E = sys::path::rend(Parent);
- I != E; ++I) {
- Result = new DirectoryEntry(*I, llvm::makeArrayRef(Result),
- Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
- 0, file_type::directory_file, sys::fs::all_all));
- }
- return Result;
- }
- public:
- VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {}
- // false on error
- bool parse(yaml::Node *Root, VFSFromYAML *FS) {
- yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
- if (!Top) {
- error(Root, "expected mapping node");
- return false;
- }
- KeyStatusPair Fields[] = {
- KeyStatusPair("version", true),
- KeyStatusPair("case-sensitive", false),
- KeyStatusPair("use-external-names", false),
- KeyStatusPair("roots", true),
- };
- DenseMap<StringRef, KeyStatus> Keys(
- &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0]));
- // Parse configuration and 'roots'
- for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
- ++I) {
- SmallString<10> KeyBuffer;
- StringRef Key;
- if (!parseScalarString(I->getKey(), Key, KeyBuffer))
- return false;
- if (!checkDuplicateOrUnknownKey(I->getKey(), Key, Keys))
- return false;
- if (Key == "roots") {
- yaml::SequenceNode *Roots = dyn_cast<yaml::SequenceNode>(I->getValue());
- if (!Roots) {
- error(I->getValue(), "expected array");
- return false;
- }
- for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
- I != E; ++I) {
- if (Entry *E = parseEntry(&*I))
- FS->Roots.push_back(E);
- else
- return false;
- }
- } else if (Key == "version") {
- StringRef VersionString;
- SmallString<4> Storage;
- if (!parseScalarString(I->getValue(), VersionString, Storage))
- return false;
- int Version;
- if (VersionString.getAsInteger<int>(10, Version)) {
- error(I->getValue(), "expected integer");
- return false;
- }
- if (Version < 0) {
- error(I->getValue(), "invalid version number");
- return false;
- }
- if (Version != 0) {
- error(I->getValue(), "version mismatch, expected 0");
- return false;
- }
- } else if (Key == "case-sensitive") {
- if (!parseScalarBool(I->getValue(), FS->CaseSensitive))
- return false;
- } else if (Key == "use-external-names") {
- if (!parseScalarBool(I->getValue(), FS->UseExternalNames))
- return false;
- } else {
- llvm_unreachable("key missing from Keys");
- }
- }
- if (Stream.failed())
- return false;
- if (!checkMissingKeys(Top, Keys))
- return false;
- return true;
- }
- };
- } // end of anonymous namespace
- Entry::~Entry() {}
- DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); }
- VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); }
- VFSFromYAML *VFSFromYAML::create(MemoryBuffer *Buffer,
- SourceMgr::DiagHandlerTy DiagHandler,
- void *DiagContext,
- IntrusiveRefCntPtr<FileSystem> ExternalFS) {
- SourceMgr SM;
- yaml::Stream Stream(Buffer, SM);
- SM.setDiagHandler(DiagHandler, DiagContext);
- yaml::document_iterator DI = Stream.begin();
- yaml::Node *Root = DI->getRoot();
- if (DI == Stream.end() || !Root) {
- SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node");
- return NULL;
- }
- VFSFromYAMLParser P(Stream);
- std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS));
- if (!P.parse(Root, FS.get()))
- return NULL;
- return FS.release();
- }
- ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {
- SmallString<256> Path;
- Path_.toVector(Path);
- // Handle relative paths
- if (error_code EC = sys::fs::make_absolute(Path))
- return EC;
- if (Path.empty())
- return error_code(errc::invalid_argument, system_category());
- sys::path::const_iterator Start = sys::path::begin(Path);
- sys::path::const_iterator End = sys::path::end(Path);
- for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end();
- I != E; ++I) {
- ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
- if (Result || Result.getError() != errc::no_such_file_or_directory)
- return Result;
- }
- return error_code(errc::no_such_file_or_directory, system_category());
- }
- ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,
- sys::path::const_iterator End,
- Entry *From) {
- if (Start->equals("."))
- ++Start;
- // FIXME: handle ..
- if (CaseSensitive ? !Start->equals(From->getName())
- : !Start->equals_lower(From->getName()))
- // failure to match
- return error_code(errc::no_such_file_or_directory, system_category());
- ++Start;
- if (Start == End) {
- // Match!
- return From;
- }
- DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From);
- if (!DE)
- return error_code(errc::not_a_directory, system_category());
- for (DirectoryEntry::iterator I = DE->contents_begin(),
- E = DE->contents_end();
- I != E; ++I) {
- ErrorOr<Entry *> Result = lookupPath(Start, End, *I);
- if (Result || Result.getError() != errc::no_such_file_or_directory)
- return Result;
- }
- return error_code(errc::no_such_file_or_directory, system_category());
- }
- ErrorOr<Status> VFSFromYAML::status(const Twine &Path) {
- ErrorOr<Entry *> Result = lookupPath(Path);
- if (!Result)
- return Result.getError();
- std::string PathStr(Path.str());
- if (FileEntry *F = dyn_cast<FileEntry>(*Result)) {
- ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());
- assert(!S || S->getName() == F->getExternalContentsPath());
- if (S && !F->useExternalName(UseExternalNames))
- S->setName(PathStr);
- return S;
- } else { // directory
- DirectoryEntry *DE = cast<DirectoryEntry>(*Result);
- Status S = DE->getStatus();
- S.setName(PathStr);
- return S;
- }
- }
- error_code VFSFromYAML::openFileForRead(const Twine &Path,
- std::unique_ptr<vfs::File> &Result) {
- ErrorOr<Entry *> E = lookupPath(Path);
- if (!E)
- return E.getError();
- FileEntry *F = dyn_cast<FileEntry>(*E);
- if (!F) // FIXME: errc::not_a_file?
- return error_code(errc::invalid_argument, system_category());
- if (error_code EC = ExternalFS->openFileForRead(F->getExternalContentsPath(),
- Result))
- return EC;
- if (!F->useExternalName(UseExternalNames))
- Result->setName(Path.str());
- return error_code::success();
- }
- IntrusiveRefCntPtr<FileSystem>
- vfs::getVFSFromYAML(MemoryBuffer *Buffer, SourceMgr::DiagHandlerTy DiagHandler,
- void *DiagContext,
- IntrusiveRefCntPtr<FileSystem> ExternalFS) {
- return VFSFromYAML::create(Buffer, DiagHandler, DiagContext, ExternalFS);
- }
- UniqueID vfs::getNextVirtualUniqueID() {
- static std::atomic<unsigned> UID;
- unsigned ID = ++UID;
- // The following assumes that uint64_t max will never collide with a real
- // dev_t value from the OS.
- return UniqueID(std::numeric_limits<uint64_t>::max(), ID);
- }
|