libutil: add a nar indexer
we'll use this in NarAccessor to provide actually safe indexing of archives. NarAccessor currently is not fully correct: it relies on the parser not buffering anything to produce correct file offsets, but only the nar implementation itself can reasonably expect that. Change-Id: I64f300b86d8844b876a3ace723546ea7d7b4628b
This commit is contained in:
@@ -583,6 +583,69 @@ Generator<Entry> parse(Source & source)
|
||||
|
||||
}
|
||||
|
||||
namespace nar_index {
|
||||
|
||||
namespace {
|
||||
struct NarSource : Source
|
||||
{
|
||||
Source & source;
|
||||
uint64_t pos = 0;
|
||||
|
||||
NarSource(Source & source) : source(source) {}
|
||||
|
||||
size_t read(char * data, size_t len) override
|
||||
{
|
||||
auto n = source.read(data, len);
|
||||
pos += n;
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
struct Indexer : NARParseVisitor
|
||||
{
|
||||
NarSource & source;
|
||||
Directory & parent;
|
||||
|
||||
public:
|
||||
Indexer(NarSource & source, Directory & parent) : source(source), parent(parent) {}
|
||||
|
||||
box_ptr<NARParseVisitor> createDirectory(const std::string & name) override
|
||||
{
|
||||
auto & dir = std::get<Directory>(parent.contents[name] = Directory{});
|
||||
return make_box_ptr<Indexer>(source, dir);
|
||||
}
|
||||
|
||||
box_ptr<FileHandle>
|
||||
createRegularFile(const std::string & name, uint64_t size, bool executable) override
|
||||
{
|
||||
struct IgnoringFileHandle : FileHandle
|
||||
{
|
||||
void close() override {}
|
||||
void receiveContents(std::string_view data) override {}
|
||||
};
|
||||
|
||||
parent.contents[name] = File{executable, source.pos, size};
|
||||
return make_box_ptr<IgnoringFileHandle>();
|
||||
}
|
||||
|
||||
void createSymlink(const std::string & name, const std::string & target) override
|
||||
{
|
||||
parent.contents[name] = Symlink{target};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Entry create(Source & source)
|
||||
{
|
||||
Directory root;
|
||||
NarSource wrapper{source};
|
||||
Indexer index{wrapper, root};
|
||||
parseDump(index, wrapper);
|
||||
return root.contents.at("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void restore(NARParseVisitor & sink, nar::Entry entry, const Path & path)
|
||||
{
|
||||
return std::visit(
|
||||
|
||||
@@ -174,6 +174,33 @@ WireFormatGenerator dump(Entry nar);
|
||||
Generator<Entry> parse(Source & source);
|
||||
}
|
||||
|
||||
namespace nar_index {
|
||||
|
||||
struct File;
|
||||
struct Symlink;
|
||||
struct Directory;
|
||||
using Entry = std::variant<File, Symlink, Directory>;
|
||||
|
||||
struct File
|
||||
{
|
||||
bool executable;
|
||||
uint64_t offset, size;
|
||||
};
|
||||
|
||||
struct Symlink
|
||||
{
|
||||
Path target;
|
||||
};
|
||||
|
||||
struct Directory
|
||||
{
|
||||
std::map<std::string, Entry> contents;
|
||||
};
|
||||
|
||||
Entry create(Source & source);
|
||||
|
||||
}
|
||||
|
||||
void parseDump(NARParseVisitor & sink, Source & source);
|
||||
|
||||
void restorePath(const Path & path, Source & source);
|
||||
|
||||
@@ -179,7 +179,34 @@ void assert_eq(Entry & a, Entry & b)
|
||||
}
|
||||
|
||||
class NarTest : public testing::TestWithParam<Fragment>
|
||||
{};
|
||||
{
|
||||
public:
|
||||
static Entries fromIndex(std::string_view raw, nar_index::Entry e)
|
||||
{
|
||||
auto handlers = overloaded{
|
||||
[&](const nar_index::File & f) -> Entry {
|
||||
std::span<const char> block{raw.substr(f.offset, f.size)};
|
||||
return File{
|
||||
f.executable,
|
||||
f.size,
|
||||
[](auto block) -> Generator<Bytes> { co_yield block; }(block),
|
||||
};
|
||||
},
|
||||
[&](const nar_index::Symlink & s) -> Entry { return Symlink{s.target}; },
|
||||
[&](const nar_index::Directory & d) -> Entry {
|
||||
return Directory{
|
||||
[](std::string_view raw, const nar_index::Directory & d
|
||||
) -> Generator<std::pair<const std::string &, Entry>> {
|
||||
for (auto & [name, entry] : d.contents) {
|
||||
co_yield std::pair{std::cref(name), *fromIndex(raw, entry).next()};
|
||||
}
|
||||
}(raw, d)
|
||||
};
|
||||
},
|
||||
};
|
||||
co_yield std::visit(handlers, e);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(NarTest, parse)
|
||||
{
|
||||
@@ -208,6 +235,24 @@ TEST_P(NarTest, copy)
|
||||
ASSERT_EQ(raw, copied);
|
||||
}
|
||||
|
||||
TEST_P(NarTest, index)
|
||||
{
|
||||
auto & [raw, entriesF] = GetParam();
|
||||
StringSource source(raw);
|
||||
|
||||
auto entries = entriesF();
|
||||
auto indexed = fromIndex(raw, nar_index::create(source));
|
||||
while (true) {
|
||||
auto e = entries.next();
|
||||
auto p = indexed.next();
|
||||
ASSERT_EQ(e.has_value(), p.has_value());
|
||||
if (!e) {
|
||||
break;
|
||||
}
|
||||
assert_eq(*e, *p);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
,
|
||||
NarTest,
|
||||
|
||||
Reference in New Issue
Block a user