mirror of
https://github.com/NixOS/nix.git
synced 2025-12-08 18:11:02 +01:00
Merge pull request #14732 from NixOS/optimize-nar-cat
nix nar {ls,cat}: Optimize, make nix nar cat work on pipes too
This commit is contained in:
commit
ffc5dffa65
7 changed files with 82 additions and 27 deletions
|
|
@ -260,8 +260,7 @@ std::string readFile(const Path & path)
|
|||
AutoCloseFD fd = toDescriptor(open(
|
||||
path.c_str(),
|
||||
O_RDONLY
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
#ifdef O_CLOEXEC
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
));
|
||||
|
|
@ -294,8 +293,7 @@ void readFile(const Path & path, Sink & sink, bool memory_map)
|
|||
AutoCloseFD fd = toDescriptor(open(
|
||||
path.c_str(),
|
||||
O_RDONLY
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
#ifdef O_CLOEXEC
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
));
|
||||
|
|
@ -309,8 +307,7 @@ void writeFile(const Path & path, std::string_view s, mode_t mode, FsSync sync)
|
|||
AutoCloseFD fd = toDescriptor(open(
|
||||
path.c_str(),
|
||||
O_WRONLY | O_TRUNC | O_CREAT
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
#ifdef O_CLOEXEC
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
,
|
||||
|
|
@ -344,8 +341,7 @@ void writeFile(const Path & path, Source & source, mode_t mode, FsSync sync)
|
|||
AutoCloseFD fd = toDescriptor(open(
|
||||
path.c_str(),
|
||||
O_WRONLY | O_TRUNC | O_CREAT
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
#ifdef O_CLOEXEC
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace nix {
|
|||
*
|
||||
* See `FileSystemObjectSink::createRegularFile`.
|
||||
*/
|
||||
struct CreateRegularFileSink : Sink
|
||||
struct CreateRegularFileSink : virtual Sink
|
||||
{
|
||||
/**
|
||||
* If set to true, the sink will not be called with the contents
|
||||
|
|
|
|||
|
|
@ -32,8 +32,16 @@ using GetNarBytes = std::function<std::string(uint64_t, uint64_t)>;
|
|||
*/
|
||||
GetNarBytes seekableGetNarBytes(const Path & path);
|
||||
|
||||
GetNarBytes seekableGetNarBytes(Descriptor fd);
|
||||
|
||||
ref<SourceAccessor> makeLazyNarAccessor(const nlohmann::json & listing, GetNarBytes getNarBytes);
|
||||
|
||||
/**
|
||||
* Creates a NAR accessor from a given stream and a GetNarBytes getter.
|
||||
* @param source Consumed eagerly. References to it are not persisted in the resulting SourceAccessor.
|
||||
*/
|
||||
ref<SourceAccessor> makeLazyNarAccessor(Source & source, GetNarBytes getNarBytes);
|
||||
|
||||
struct NarListingRegularFile
|
||||
{
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ struct NarAccessor : public SourceAccessor
|
|||
path,
|
||||
NarMember{.stat = {.type = Type::tRegular, .fileSize = 0, .isExecutable = false, .narOffset = 0}});
|
||||
NarMemberConstructor nmc{nm, pos};
|
||||
nmc.skipContents = true; /* Don't care about contents. */
|
||||
func(nmc);
|
||||
}
|
||||
|
||||
|
|
@ -141,6 +142,13 @@ struct NarAccessor : public SourceAccessor
|
|||
parseDump(indexer, indexer);
|
||||
}
|
||||
|
||||
NarAccessor(Source & source, GetNarBytes getNarBytes)
|
||||
: getNarBytes(std::move(getNarBytes))
|
||||
{
|
||||
NarIndexer indexer(*this, source);
|
||||
parseDump(indexer, indexer);
|
||||
}
|
||||
|
||||
NarAccessor(const nlohmann::json & listing, GetNarBytes getNarBytes)
|
||||
: getNarBytes(getNarBytes)
|
||||
{
|
||||
|
|
@ -249,24 +257,35 @@ ref<SourceAccessor> makeLazyNarAccessor(const nlohmann::json & listing, GetNarBy
|
|||
return make_ref<NarAccessor>(listing, getNarBytes);
|
||||
}
|
||||
|
||||
ref<SourceAccessor> makeLazyNarAccessor(Source & source, GetNarBytes getNarBytes)
|
||||
{
|
||||
return make_ref<NarAccessor>(source, getNarBytes);
|
||||
}
|
||||
|
||||
GetNarBytes seekableGetNarBytes(const Path & path)
|
||||
{
|
||||
return [path](uint64_t offset, uint64_t length) {
|
||||
AutoCloseFD fd = toDescriptor(open(
|
||||
path.c_str(),
|
||||
O_RDONLY
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
AutoCloseFD fd = toDescriptor(open(
|
||||
path.c_str(),
|
||||
O_RDONLY
|
||||
#ifdef O_CLOEXEC
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
));
|
||||
if (!fd)
|
||||
throw SysError("opening NAR cache file '%s'", path);
|
||||
));
|
||||
if (!fd)
|
||||
throw SysError("opening NAR cache file '%s'", path);
|
||||
|
||||
if (lseek(fromDescriptorReadOnly(fd.get()), offset, SEEK_SET) != (off_t) offset)
|
||||
throw SysError("seeking in '%s'", path);
|
||||
return [inner = seekableGetNarBytes(fd.get()), fd = make_ref<AutoCloseFD>(std::move(fd))](
|
||||
uint64_t offset, uint64_t length) { return inner(offset, length); };
|
||||
}
|
||||
|
||||
GetNarBytes seekableGetNarBytes(Descriptor fd)
|
||||
{
|
||||
return [fd](uint64_t offset, uint64_t length) {
|
||||
if (::lseek(fromDescriptorReadOnly(fd), offset, SEEK_SET) == -1)
|
||||
throw SysError("seeking in file");
|
||||
|
||||
std::string buf(length, 0);
|
||||
readFull(fd.get(), buf.data(), length);
|
||||
readFull(fd, buf.data(), length);
|
||||
|
||||
return buf;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "nix/cmd/command.hh"
|
||||
#include "nix/store/store-api.hh"
|
||||
#include "nix/util/archive.hh"
|
||||
#include "nix/util/nar-accessor.hh"
|
||||
#include "nix/util/serialise.hh"
|
||||
#include "nix/util/source-accessor.hh"
|
||||
|
|
@ -79,9 +80,40 @@ struct CmdCatNar : StoreCommand, MixCat
|
|||
if (!fd)
|
||||
throw SysError("opening NAR file '%s'", narPath);
|
||||
auto source = FdSource{fd.get()};
|
||||
auto narAccessor = makeNarAccessor(source);
|
||||
nlohmann::json listing = listNarDeep(*narAccessor, CanonPath::root);
|
||||
cat(makeLazyNarAccessor(listing, seekableGetNarBytes(narPath)), CanonPath{path});
|
||||
|
||||
struct CatRegularFileSink : NullFileSystemObjectSink
|
||||
{
|
||||
CanonPath neededPath = CanonPath::root;
|
||||
bool found = false;
|
||||
|
||||
void createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)> crf) override
|
||||
{
|
||||
struct : CreateRegularFileSink, FdSink
|
||||
{
|
||||
void isExecutable() override {}
|
||||
} crfSink;
|
||||
|
||||
crfSink.fd = INVALID_DESCRIPTOR;
|
||||
|
||||
if (path == neededPath) {
|
||||
logger->stop();
|
||||
crfSink.skipContents = false;
|
||||
crfSink.fd = STDOUT_FILENO;
|
||||
found = true;
|
||||
} else {
|
||||
crfSink.skipContents = true;
|
||||
}
|
||||
|
||||
crf(crfSink);
|
||||
}
|
||||
} sink;
|
||||
|
||||
sink.neededPath = CanonPath(path);
|
||||
/* NOTE: We still parse the whole file to validate that it's a correct NAR. */
|
||||
parseDump(sink, source);
|
||||
|
||||
if (!sink.found)
|
||||
throw Error("NAR does not contain regular file '%1%'", path);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -154,9 +154,7 @@ struct CmdLsNar : Command, MixLs
|
|||
if (!fd)
|
||||
throw SysError("opening NAR file '%s'", narPath);
|
||||
auto source = FdSource{fd.get()};
|
||||
auto narAccessor = makeNarAccessor(source);
|
||||
nlohmann::json listing = listNarDeep(*narAccessor, CanonPath::root);
|
||||
list(makeLazyNarAccessor(listing, seekableGetNarBytes(narPath)), CanonPath{path});
|
||||
list(makeLazyNarAccessor(source, seekableGetNarBytes(fd.get())), CanonPath{path});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ diff -u data.cat-nar "$storePath/foo/data"
|
|||
# Check that file contents of baz match.
|
||||
nix nar cat "$narFile" /foo/baz > baz.cat-nar
|
||||
diff -u baz.cat-nar "$storePath/foo/baz"
|
||||
nix nar cat /dev/stdin /foo/baz < "$narFile" > baz.cat-nar-pipe
|
||||
expect 1 nix nar cat "$narFile" /foo/baz/doesntexist 2>&1 | grep "NAR does not contain regular file '/foo/baz/doesntexist'"
|
||||
|
||||
nix store cat "$storePath/foo/baz" > baz.cat-nar
|
||||
diff -u baz.cat-nar "$storePath/foo/baz"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue