about summary refs log tree commit diff
path: root/src/libstore/nar-accessor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore/nar-accessor.cc')
-rw-r--r--src/libstore/nar-accessor.cc142
1 files changed, 142 insertions, 0 deletions
diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc
new file mode 100644
index 000000000000..88b027ff3b7d
--- /dev/null
+++ b/src/libstore/nar-accessor.cc
@@ -0,0 +1,142 @@
+#include "nar-accessor.hh"
+#include "archive.hh"
+
+#include <map>
+
+namespace nix {
+
+struct NarMember
+{
+    FSAccessor::Type type;
+
+    bool isExecutable;
+
+    /* If this is a regular file, position of the contents of this
+       file in the NAR. */
+    size_t start, size;
+
+    std::string target;
+};
+
+struct NarIndexer : ParseSink, StringSource
+{
+    // FIXME: should store this as a tree. Now we're vulnerable to
+    // O(nm) memory consumption (e.g. for x_0/.../x_n/{y_0..y_m}).
+    typedef std::map<Path, NarMember> Members;
+    Members members;
+
+    Path currentPath;
+    std::string currentStart;
+    bool isExec;
+
+    NarIndexer(const std::string & nar) : StringSource(nar)
+    {
+    }
+
+    void createDirectory(const Path & path)
+    {
+        members.emplace(path,
+            NarMember{FSAccessor::Type::tDirectory, false, 0, 0});
+    }
+
+    void createRegularFile(const Path & path) override
+    {
+        currentPath = path;
+    }
+
+    void isExecutable()
+    {
+        isExec = true;
+    }
+
+    void preallocateContents(unsigned long long size) override
+    {
+        assert(currentPath != "");
+        currentStart = string(s, pos, 16);
+        members.emplace(currentPath,
+            NarMember{FSAccessor::Type::tRegular, isExec, pos, size});
+    }
+
+    void receiveContents(unsigned char * data, unsigned int len) override
+    {
+        // Sanity check
+        if (!currentStart.empty()) {
+            assert(len < 16 || currentStart == string((char *) data, 16));
+            currentStart.clear();
+        }
+    }
+
+    void createSymlink(const Path & path, const string & target) override
+    {
+        members.emplace(path,
+            NarMember{FSAccessor::Type::tSymlink, false, 0, 0, target});
+    }
+
+    Members::iterator find(const Path & path)
+    {
+        auto i = members.find(path);
+        if (i == members.end())
+            throw Error(format("NAR file does not contain path ‘%1%’") % path);
+        return i;
+    }
+};
+
+struct NarAccessor : public FSAccessor
+{
+    ref<const std::string> nar;
+    NarIndexer indexer;
+
+    NarAccessor(ref<const std::string> nar) : nar(nar), indexer(*nar)
+    {
+        parseDump(indexer, indexer);
+    }
+
+    Stat stat(const Path & path) override
+    {
+        auto i = indexer.members.find(path);
+        if (i == indexer.members.end())
+            return {FSAccessor::Type::tMissing, 0, false};
+        return {i->second.type, i->second.size, i->second.isExecutable};
+    }
+
+    StringSet readDirectory(const Path & path) override
+    {
+        auto i = indexer.find(path);
+
+        if (i->second.type != FSAccessor::Type::tDirectory)
+            throw Error(format("path ‘%1%’ inside NAR file is not a directory") % path);
+
+        ++i;
+        StringSet res;
+        while (i != indexer.members.end() && isInDir(i->first, path)) {
+            // FIXME: really bad performance.
+            if (i->first.find('/', path.size() + 1) == std::string::npos)
+                res.insert(std::string(i->first, path.size() + 1));
+            ++i;
+        }
+        return res;
+    }
+
+    std::string readFile(const Path & path) override
+    {
+        auto i = indexer.find(path);
+        if (i->second.type != FSAccessor::Type::tRegular)
+            throw Error(format("path ‘%1%’ inside NAR file is not a regular file") % path);
+        return std::string(*nar, i->second.start, i->second.size);
+    }
+
+    std::string readLink(const Path & path) override
+    {
+        auto i = indexer.find(path);
+        if (i->second.type != FSAccessor::Type::tSymlink)
+            throw Error(format("path ‘%1%’ inside NAR file is not a symlink") % path);
+        return i->second.target;
+    }
+};
+
+ref<FSAccessor> makeNarAccessor(ref<const std::string> nar)
+{
+    return make_ref<NarAccessor>(nar);
+}
+
+}