From c6295a3afd87a605f30f1de8b09d7885eb08fedb Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Fri, 13 Dec 2019 03:29:33 -0500 Subject: [PATCH] Initial gzip support Closes #3256 --- configure.ac | 5 +++ release-common.nix | 2 +- src/libutil/compression.cc | 64 ++++++++++++++++++++++++++++++++++++++ src/libutil/tarfile.cc | 1 + tests/tarball.sh | 17 ++++++++++ 5 files changed, 88 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9dd0acd86..99248b90c 100644 --- a/configure.ac +++ b/configure.ac @@ -209,6 +209,11 @@ PKG_CHECK_MODULES([LIBLZMA], [liblzma], [CXXFLAGS="$LIBLZMA_CFLAGS $CXXFLAGS"]) AC_CHECK_LIB([lzma], [lzma_stream_encoder_mt], [AC_DEFINE([HAVE_LZMA_MT], [1], [xz multithreaded compression support])]) +# Look for zlib, a required dependency. +PKG_CHECK_MODULES([ZLIB], [zlib], [CXXFLAGS="$ZLIB_CFLAGS $CXXFLAGS"]) +AC_CHECK_HEADER([zlib.h],[:],[AC_MSG_ERROR([could not find the zlib.h header])]) +LDFLAGS="-lz $LDFLAGS" + # Look for libbrotli{enc,dec}. PKG_CHECK_MODULES([LIBBROTLI], [libbrotlienc libbrotlidec], [CXXFLAGS="$LIBBROTLI_CFLAGS $CXXFLAGS"]) diff --git a/release-common.nix b/release-common.nix index dd5f939d9..3765e61b3 100644 --- a/release-common.nix +++ b/release-common.nix @@ -47,7 +47,7 @@ rec { buildDeps = [ curl - bzip2 xz brotli editline + bzip2 xz brotli zlib editline openssl pkgconfig sqlite boehmgc boost nlohmann_json diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc index 0dd84e320..17b506d5d 100644 --- a/src/libutil/compression.cc +++ b/src/libutil/compression.cc @@ -11,6 +11,8 @@ #include #include +#include + #include namespace nix { @@ -42,6 +44,66 @@ struct NoneSink : CompressionSink void write(const unsigned char * data, size_t len) override { nextSink(data, len); } }; +struct GzipDecompressionSink : CompressionSink +{ + Sink & nextSink; + z_stream strm; + bool finished = false; + uint8_t outbuf[BUFSIZ]; + + GzipDecompressionSink(Sink & nextSink) : nextSink(nextSink) + { + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + // Enable gzip and zlib decoding (+32) with 15 windowBits + int ret = inflateInit2(&strm,15+32); + if (ret != Z_OK) + throw CompressionError("unable to initialise gzip encoder"); + } + + ~GzipDecompressionSink() + { + inflateEnd(&strm); + } + + void finish() override + { + CompressionSink::flush(); + write(nullptr, 0); + } + + void write(const unsigned char * data, size_t len) override + { + assert(len <= std::numeric_limits::max()); + + strm.next_in = (Bytef *) data; + strm.avail_in = len; + + while (!finished && (!data || strm.avail_in)) { + checkInterrupt(); + + int ret = inflate(&strm,Z_SYNC_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + throw CompressionError("error while decompressing gzip file: %d: %d: %d",ret, len, strm.avail_in); + + + finished = ret == Z_STREAM_END; + + if (strm.avail_out < sizeof(outbuf) || strm.avail_in == 0) { + nextSink(outbuf, sizeof(outbuf) - strm.avail_out); + strm.next_out = (Bytef *) outbuf; + strm.avail_out = sizeof(outbuf); + } + } + } +}; + struct XzDecompressionSink : CompressionSink { Sink & nextSink; @@ -215,6 +277,8 @@ ref makeDecompressionSink(const std::string & method, Sink & ne return make_ref(nextSink); else if (method == "bzip2") return make_ref(nextSink); + else if (method == "gzip") + return make_ref(nextSink); else if (method == "br") return make_ref(nextSink); else diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index 262bc655f..c00673182 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -24,6 +24,7 @@ void unpackTarfile(const Path & tarFile, const Path & destDir, auto decompressor = // FIXME: add .gz support hasSuffix(*baseName, ".bz2") ? makeDecompressionSink("bzip2", sink) : + hasSuffix(*baseName, ".gz") ? makeDecompressionSink("gzip", sink) : hasSuffix(*baseName, ".xz") ? makeDecompressionSink("xz", sink) : makeDecompressionSink("none", sink); readFile(tarFile, *decompressor); diff --git a/tests/tarball.sh b/tests/tarball.sh index ba534c626..ec810b4d7 100644 --- a/tests/tarball.sh +++ b/tests/tarball.sh @@ -26,3 +26,20 @@ nix-instantiate --eval -E 'with ; 1 + 2' -I fnord=file://no-such-ta (! nix-instantiate --eval -E ' 1' -I fnord=file://no-such-tarball.tar.xz) nix-instantiate --eval -E '' -I fnord=file://no-such-tarball.tar.xz -I fnord=. + +tarball=$TEST_ROOT/tarball.tar.gz +(cd $TEST_ROOT && tar c tarball) | gzip > $tarball + +nix-env -f file://$tarball -qa --out-path | grep -q dependencies + +nix-build -o $TEST_ROOT/result file://$tarball + +nix-build -o $TEST_ROOT/result '' -I foo=file://$tarball + +nix-build -o $TEST_ROOT/result -E "import (fetchTarball file://$tarball)" + +nix-instantiate --eval -E '1 + 2' -I fnord=file://no-such-tarball.tar.gz +nix-instantiate --eval -E 'with ; 1 + 2' -I fnord=file://no-such-tarball.tar.gz +(! nix-instantiate --eval -E ' 1' -I fnord=file://no-such-tarball.tar.gz) + +nix-instantiate --eval -E '' -I fnord=file://no-such-tarball.tar.gz -I fnord=.