We had a minor combinatorial explosion of ways to do things. We can get rid of those by just having the caller call `parseNarListing` intead.
176 lines
5.4 KiB
C++
176 lines
5.4 KiB
C++
#include "nix/util/nar-accessor.hh"
|
|
#include "nix/util/file-descriptor.hh"
|
|
#include "nix/util/error.hh"
|
|
#include "nix/util/signals.hh"
|
|
|
|
namespace nix {
|
|
|
|
struct NarAccessorImpl : NarAccessor
|
|
{
|
|
NarListing root;
|
|
|
|
GetNarBytes getNarBytes;
|
|
|
|
const NarListing & getListing() const override
|
|
{
|
|
return root;
|
|
}
|
|
|
|
NarAccessorImpl(std::string && nar)
|
|
: root{[&nar]() {
|
|
StringSource source(nar);
|
|
return parseNarListing(source);
|
|
}()}
|
|
, getNarBytes{[nar = std::move(nar)](uint64_t offset, uint64_t length, Sink & sink) {
|
|
if (offset > nar.size() || length > nar.size() - offset)
|
|
throw Error(
|
|
"reading invalid NAR bytes range: requested %1% bytes at offset %2%, but NAR has size %3%",
|
|
length,
|
|
offset,
|
|
nar.size());
|
|
StringSource source(std::string_view(nar.data() + offset, length));
|
|
source.drainInto(sink);
|
|
}}
|
|
{
|
|
}
|
|
|
|
NarAccessorImpl(NarListing && listing)
|
|
: root{std::move(listing)}
|
|
{
|
|
}
|
|
|
|
NarAccessorImpl(NarListing && listing, GetNarBytes getNarBytes)
|
|
: root{std::move(listing)}
|
|
, getNarBytes{std::move(getNarBytes)}
|
|
{
|
|
}
|
|
|
|
NarListing * find(const CanonPath & path)
|
|
{
|
|
NarListing * current = &root;
|
|
|
|
for (const auto & i : path) {
|
|
auto * dir = std::get_if<NarListing::Directory>(¤t->raw);
|
|
if (!dir)
|
|
return nullptr;
|
|
auto * child = nix::get(dir->entries, i);
|
|
if (!child)
|
|
return nullptr;
|
|
current = child;
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
NarListing & get(const CanonPath & path)
|
|
{
|
|
auto result = find(path);
|
|
if (!result)
|
|
throw Error("NAR file does not contain path '%1%'", path);
|
|
return *result;
|
|
}
|
|
|
|
std::optional<Stat> maybeLstat(const CanonPath & path) override
|
|
{
|
|
auto i = find(path);
|
|
if (!i)
|
|
return std::nullopt;
|
|
return std::visit(
|
|
overloaded{
|
|
[](const NarListing::Regular & r) -> Stat {
|
|
return {
|
|
.type = Type::tRegular,
|
|
.fileSize = r.contents.fileSize,
|
|
.isExecutable = r.executable,
|
|
.narOffset = r.contents.narOffset,
|
|
};
|
|
},
|
|
[](const NarListing::Directory &) -> Stat {
|
|
return {
|
|
.type = Type::tDirectory,
|
|
};
|
|
},
|
|
[](const NarListing::Symlink &) -> Stat {
|
|
return {
|
|
.type = Type::tSymlink,
|
|
};
|
|
},
|
|
},
|
|
i->raw);
|
|
}
|
|
|
|
DirEntries readDirectory(const CanonPath & path) override
|
|
{
|
|
auto & i = get(path);
|
|
|
|
auto * dir = std::get_if<NarListing::Directory>(&i.raw);
|
|
if (!dir)
|
|
throw Error("path '%1%' inside NAR file is not a directory", path);
|
|
|
|
DirEntries res;
|
|
for (const auto & [name, child] : dir->entries)
|
|
res.insert_or_assign(name, std::nullopt);
|
|
|
|
return res;
|
|
}
|
|
|
|
void readFile(const CanonPath & path, Sink & sink, std::function<void(uint64_t)> sizeCallback) override
|
|
{
|
|
auto & i = get(path);
|
|
auto * reg = std::get_if<NarListing::Regular>(&i.raw);
|
|
if (!reg)
|
|
throw Error("path '%1%' inside NAR file is not a regular file", path);
|
|
|
|
assert(getNarBytes);
|
|
sizeCallback(reg->contents.fileSize.value());
|
|
return getNarBytes(reg->contents.narOffset.value(), reg->contents.fileSize.value(), sink);
|
|
}
|
|
|
|
std::string readLink(const CanonPath & path) override
|
|
{
|
|
auto & i = get(path);
|
|
auto * sym = std::get_if<NarListing::Symlink>(&i.raw);
|
|
if (!sym)
|
|
throw Error("path '%1%' inside NAR file is not a symlink", path);
|
|
return sym->target;
|
|
}
|
|
};
|
|
|
|
ref<NarAccessor> makeNarAccessor(std::string && nar)
|
|
{
|
|
return make_ref<NarAccessorImpl>(std::move(nar));
|
|
}
|
|
|
|
ref<NarAccessor> makeNarAccessor(NarListing listing)
|
|
{
|
|
return make_ref<NarAccessorImpl>(std::move(listing));
|
|
}
|
|
|
|
ref<NarAccessor> makeLazyNarAccessor(NarListing listing, GetNarBytes getNarBytes)
|
|
{
|
|
return make_ref<NarAccessorImpl>(std::move(listing), getNarBytes);
|
|
}
|
|
|
|
GetNarBytes seekableGetNarBytes(const std::filesystem::path & path)
|
|
{
|
|
AutoCloseFD fd = openFileReadonly(path);
|
|
if (!fd)
|
|
throw NativeSysError("opening NAR cache file %s", PathFmt(path));
|
|
|
|
return [inner = seekableGetNarBytes(fd.get()), fd = make_ref<AutoCloseFD>(std::move(fd))](
|
|
uint64_t offset, uint64_t length, Sink & sink) { return inner(offset, length, sink); };
|
|
}
|
|
|
|
GetNarBytes seekableGetNarBytes(Descriptor fd)
|
|
{
|
|
return [fd](uint64_t offset, uint64_t length, Sink & sink) {
|
|
if (offset >= std::numeric_limits<off_t>::max()) /* Just in case off_t is not 64 bits. */
|
|
throw Error("can't read %1% NAR bytes from offset %2%: offset too big", length, offset);
|
|
if (length >= std::numeric_limits<size_t>::max()) /* Just in case size_t is 32 bits. */
|
|
throw Error("can't read %1% NAR bytes from offset %2%: length is too big", length, offset);
|
|
copyFdRange(fd, static_cast<off_t>(offset), static_cast<size_t>(length), sink);
|
|
};
|
|
}
|
|
|
|
} // namespace nix
|