libutil: mirror nar structure in nar visitor structure

this is an unfortunate but necessary intermediate state in which we can
mirror nar structure from the visitor side, but not yet from the parser
side. to move from paths to entry names we must first relocate names of
entries out of the entries themselves, and into their parent directory.

Change-Id: I2ea898230952a4f631eea67c66ac8dcbdfb19636
This commit is contained in:
eldritch horrors
2025-02-27 23:51:06 +00:00
parent ab9f6e311d
commit 52db1ecb3a
4 changed files with 64 additions and 49 deletions
+20 -21
View File
@@ -54,37 +54,30 @@ struct NarAccessor : public FSAccessor
NarAccessor & acc;
NarSource & source;
std::stack<NarMember *> parents;
NarMember * parent;
public:
NarIndexer(NarAccessor & acc, NarSource & source)
: acc(acc), source(source)
NarIndexer(NarAccessor & acc, NarSource & source, NarMember & parent)
: acc(acc), source(source), parent(&parent)
{ }
NarMember & createMember(const Path & path, NarMember member)
{
size_t level = std::count(path.begin(), path.end(), '/');
while (parents.size() > level) parents.pop();
if (parents.empty()) {
acc.root = std::move(member);
parents.push(&acc.root);
if (parent->type == FSAccessor::Type::tMissing) {
*parent = std::move(member);
return *parent;
} else {
if (parents.top()->type != FSAccessor::Type::tDirectory)
throw Error("NAR file missing parent directory of path '%s'", path);
auto result = parents.top()->children.emplace(baseNameOf(path), std::move(member));
parents.push(&result.first->second);
return parent->children.emplace(baseNameOf(path), std::move(member)).first->second;
}
return *parents.top();
}
void createDirectory(const Path & path) override
box_ptr<NARParseVisitor> createDirectory(const Path & path) override
{
createMember(path, {FSAccessor::Type::tDirectory, false, 0, 0});
auto & dir = createMember(path, {FSAccessor::Type::tDirectory, false, 0, 0});
return make_box_ptr<NarIndexer>(acc, source, dir);
}
std::unique_ptr<FileHandle> createRegularFile(const Path & path, uint64_t size, bool executable) override
box_ptr<FileHandle> createRegularFile(const Path & path, uint64_t size, bool executable) override
{
auto & memb = createMember(path, {FSAccessor::Type::tRegular, false, 0, 0});
@@ -93,7 +86,13 @@ struct NarAccessor : public FSAccessor
memb.start = source.pos;
memb.isExecutable = executable;
return std::make_unique<FileHandle>();
struct IgnoringFileHandle : FileHandle
{
void close() override {}
void receiveContents(std::string_view data) override {}
};
return make_box_ptr<IgnoringFileHandle>();
}
void createSymlink(const Path & path, const std::string & target) override
@@ -107,14 +106,14 @@ struct NarAccessor : public FSAccessor
{
StringSource source(*nar);
NarSource posSource(source);
NarIndexer indexer(*this, posSource);
NarIndexer indexer(*this, posSource, root);
parseDump(indexer, posSource);
}
NarAccessor(Source & source)
{
NarSource posSource(source);
NarIndexer indexer(*this, posSource);
NarIndexer indexer(*this, posSource, root);
parseDump(indexer, posSource);
}
+5 -6
View File
@@ -385,22 +385,21 @@ struct RetrieveRegularNARVisitor : NARParseVisitor
sink(data);
}
private:
MyFileHandle(Sink & sink) : sink(sink) {}
void close() override {}
friend struct RetrieveRegularNARVisitor;
MyFileHandle(Sink & sink) : sink(sink) {}
};
Sink & sink;
RetrieveRegularNARVisitor(Sink & sink) : sink(sink) { }
std::unique_ptr<FileHandle> createRegularFile(const Path & path, uint64_t size, bool executable) override
box_ptr<FileHandle> createRegularFile(const Path & path, uint64_t size, bool executable) override
{
return std::unique_ptr<MyFileHandle>(new MyFileHandle{sink});
return make_box_ptr<MyFileHandle>(sink);
}
void createDirectory(const Path & path) override
box_ptr<NARParseVisitor> createDirectory(const Path & path) override
{
assert(false && "RetrieveRegularNARVisitor::createDirectory must not be called");
}
+32 -13
View File
@@ -350,8 +350,8 @@ static WireFormatGenerator restore(NARParseVisitor & sink, Generator<nar::Entry>
},
[&](nar::Directory d) {
return [](auto d, auto & sink) -> WireFormatGenerator {
sink.createDirectory(d.path);
return restore(sink, std::move(d.contents));
auto dir = sink.createDirectory(d.path);
co_yield restore(*dir, std::move(d.contents));
}(std::move(d), sink);
},
},
@@ -407,7 +407,7 @@ struct NARRestoreVisitor : NARParseVisitor
Path dstPath;
private:
class MyFileHandle : public FileHandle
struct MyFileHandle : public FileHandle
{
AutoCloseFD fd;
@@ -447,8 +447,6 @@ private:
#endif
}
public:
~MyFileHandle() = default;
virtual void close() override
@@ -461,25 +459,26 @@ private:
{
writeFull(fd.get(), data);
}
friend struct NARRestoreVisitor;
};
public:
void createDirectory(const Path & path) override
NARRestoreVisitor(Path dstPath): dstPath(std::move(dstPath)) {}
box_ptr<NARParseVisitor> createDirectory(const Path & path) override
{
Path p = dstPath + path;
if (mkdir(p.c_str(), 0777) == -1)
throw SysError("creating directory '%1%'", p);
return make_box_ptr<NARRestoreVisitor>(dstPath);
};
std::unique_ptr<FileHandle> createRegularFile(const Path & path, uint64_t size, bool executable) override
box_ptr<FileHandle> createRegularFile(const Path & path, uint64_t size, bool executable) override
{
Path p = dstPath + path;
AutoCloseFD fd = AutoCloseFD{open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666)};
if (!fd) throw SysError("creating file '%1%'", p);
return std::unique_ptr<MyFileHandle>(new MyFileHandle(std::move(fd), size, executable));
return make_box_ptr<MyFileHandle>(std::move(fd), size, executable);
}
void createSymlink(const Path & path, const std::string & target) override
@@ -492,8 +491,7 @@ public:
void restorePath(const Path & path, Source & source)
{
NARRestoreVisitor sink;
sink.dstPath = path;
NARRestoreVisitor sink(path);
parseDump(sink, source);
}
@@ -503,7 +501,28 @@ WireFormatGenerator copyNAR(Source & source)
// FIXME: if 'source' is the output of dumpPath() followed by EOF,
// we should just forward all data directly without parsing.
static NARParseVisitor parseSink; /* null sink; just parse the NAR */
struct DiscardVisitor : NARParseVisitor
{
struct MyFileHandle : FileHandle
{
void close() override {}
void receiveContents(std::string_view data) override {}
};
box_ptr<NARParseVisitor> createDirectory(const Path & path) override
{
return make_box_ptr<DiscardVisitor>();
}
box_ptr<FileHandle> createRegularFile(const Path & path, uint64_t size, bool executable) override
{
return make_box_ptr<MyFileHandle>();
}
void createSymlink(const Path & path, const std::string & target) override {}
};
static DiscardVisitor parseSink; /* null sink; just parse the NAR */
return parseAndCopyDump(parseSink, source);
}
+7 -9
View File
@@ -93,30 +93,28 @@ struct NARParseVisitor
FileHandle & operator=(FileHandle &) = delete;
/** Puts one block of data into the file */
virtual void receiveContents(std::string_view data) { }
virtual void receiveContents(std::string_view data) = 0;
/**
* Explicitly closes the file. Further operations may throw an assert.
* This exists so that closing can fail and throw an exception without doing so in a destructor.
*/
virtual void close() { }
virtual void close() = 0;
virtual ~FileHandle() = default;
};
virtual void createDirectory(const Path & path) { }
virtual ~NARParseVisitor() = default;
virtual box_ptr<NARParseVisitor> createDirectory(const Path & path) = 0;
/**
* Creates a regular file in the extraction output with the given size and executable flag.
* The size is guaranteed to be the true size of the file.
*/
[[nodiscard]]
virtual std::unique_ptr<FileHandle> createRegularFile(const Path & path, uint64_t size, bool executable)
{
return std::make_unique<FileHandle>();
}
virtual box_ptr<FileHandle> createRegularFile(const Path & path, uint64_t size, bool executable) = 0;
virtual void createSymlink(const Path & path, const std::string & target) { }
virtual void createSymlink(const Path & path, const std::string & target) = 0;
};
namespace nar {