diff --git a/src/libutil/include/nix/util/fs-sink.hh b/src/libutil/include/nix/util/fs-sink.hh index 60e8441dd..bd9c7205f 100644 --- a/src/libutil/include/nix/util/fs-sink.hh +++ b/src/libutil/include/nix/util/fs-sink.hh @@ -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 diff --git a/src/nix/cat.cc b/src/nix/cat.cc index f0dfc3ee6..c1d73f2a2 100644 --- a/src/nix/cat.cc +++ b/src/nix/cat.cc @@ -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,7 +80,40 @@ struct CmdCatNar : StoreCommand, MixCat if (!fd) throw SysError("opening NAR file '%s'", narPath); auto source = FdSource{fd.get()}; - cat(makeLazyNarAccessor(source, seekableGetNarBytes(fd.get())), CanonPath{path}); + + struct CatRegularFileSink : NullFileSystemObjectSink + { + CanonPath neededPath = CanonPath::root; + bool found = false; + + void createRegularFile(const CanonPath & path, std::function 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); } }; diff --git a/tests/functional/nar-access.sh b/tests/functional/nar-access.sh index 2b0a6a329..cd419b4ee 100755 --- a/tests/functional/nar-access.sh +++ b/tests/functional/nar-access.sh @@ -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"