178f57a9cSMark Johnston /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
378f57a9cSMark Johnston *
478f57a9cSMark Johnston * Copyright (c) 2014, 2017 Mark Johnston <markj@FreeBSD.org>
56026dcd7SMark Johnston * Copyright (c) 2017 Conrad Meyer <cem@FreeBSD.org>
678f57a9cSMark Johnston *
778f57a9cSMark Johnston * Redistribution and use in source and binary forms, with or without
878f57a9cSMark Johnston * modification, are permitted provided that the following conditions are
978f57a9cSMark Johnston * met:
1078f57a9cSMark Johnston * 1. Redistributions of source code must retain the above copyright
1178f57a9cSMark Johnston * notice, this list of conditions and the following disclaimer.
1278f57a9cSMark Johnston * 2. Redistributions in binary form must reproduce the above copyright
1378f57a9cSMark Johnston * notice, this list of conditions and the following disclaimer in
1478f57a9cSMark Johnston * the documentation and/or other materials provided with the
1578f57a9cSMark Johnston * distribution.
1678f57a9cSMark Johnston *
1778f57a9cSMark Johnston * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1878f57a9cSMark Johnston * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1978f57a9cSMark Johnston * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2078f57a9cSMark Johnston * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2178f57a9cSMark Johnston * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2278f57a9cSMark Johnston * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2378f57a9cSMark Johnston * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2478f57a9cSMark Johnston * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2578f57a9cSMark Johnston * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2678f57a9cSMark Johnston * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2778f57a9cSMark Johnston * SUCH DAMAGE.
2878f57a9cSMark Johnston */
2978f57a9cSMark Johnston
3078f57a9cSMark Johnston /*
3178f57a9cSMark Johnston * Subroutines used for writing compressed user process and kernel core dumps.
3278f57a9cSMark Johnston */
3378f57a9cSMark Johnston
3478f57a9cSMark Johnston #include <sys/cdefs.h>
3578f57a9cSMark Johnston #include "opt_gzio.h"
366026dcd7SMark Johnston #include "opt_zstdio.h"
3778f57a9cSMark Johnston
3878f57a9cSMark Johnston #include <sys/param.h>
396026dcd7SMark Johnston #include <sys/systm.h>
4078f57a9cSMark Johnston
4178f57a9cSMark Johnston #include <sys/compressor.h>
426db44b01SJustin Hibbits #include <sys/endian.h>
4378f57a9cSMark Johnston #include <sys/kernel.h>
4478f57a9cSMark Johnston #include <sys/linker_set.h>
4578f57a9cSMark Johnston #include <sys/malloc.h>
4678f57a9cSMark Johnston
4778f57a9cSMark Johnston MALLOC_DEFINE(M_COMPRESS, "compressor", "kernel compression subroutines");
4878f57a9cSMark Johnston
4978f57a9cSMark Johnston struct compressor_methods {
5078f57a9cSMark Johnston int format;
5178f57a9cSMark Johnston void *(* const init)(size_t, int);
5278f57a9cSMark Johnston void (* const reset)(void *);
5378f57a9cSMark Johnston int (* const write)(void *, void *, size_t, compressor_cb_t, void *);
5478f57a9cSMark Johnston void (* const fini)(void *);
5578f57a9cSMark Johnston };
5678f57a9cSMark Johnston
5778f57a9cSMark Johnston struct compressor {
5878f57a9cSMark Johnston const struct compressor_methods *methods;
5978f57a9cSMark Johnston compressor_cb_t cb;
6078f57a9cSMark Johnston void *priv;
6178f57a9cSMark Johnston void *arg;
6278f57a9cSMark Johnston };
6378f57a9cSMark Johnston
6478f57a9cSMark Johnston SET_DECLARE(compressors, struct compressor_methods);
6578f57a9cSMark Johnston
6678f57a9cSMark Johnston #ifdef GZIO
6778f57a9cSMark Johnston
684e8671ddSXin LI #include <contrib/zlib/zutil.h>
6978f57a9cSMark Johnston
7078f57a9cSMark Johnston struct gz_stream {
7178f57a9cSMark Johnston uint8_t *gz_buffer; /* output buffer */
7278f57a9cSMark Johnston size_t gz_bufsz; /* output buffer size */
7378f57a9cSMark Johnston off_t gz_off; /* offset into the output stream */
7478f57a9cSMark Johnston uint32_t gz_crc; /* stream CRC32 */
7578f57a9cSMark Johnston z_stream gz_stream; /* zlib state */
7678f57a9cSMark Johnston };
7778f57a9cSMark Johnston
7878f57a9cSMark Johnston static void *gz_init(size_t maxiosize, int level);
7978f57a9cSMark Johnston static void gz_reset(void *stream);
8078f57a9cSMark Johnston static int gz_write(void *stream, void *data, size_t len, compressor_cb_t,
8178f57a9cSMark Johnston void *);
8278f57a9cSMark Johnston static void gz_fini(void *stream);
8378f57a9cSMark Johnston
8478f57a9cSMark Johnston static void *
gz_alloc(void * arg __unused,u_int n,u_int sz)8578f57a9cSMark Johnston gz_alloc(void *arg __unused, u_int n, u_int sz)
8678f57a9cSMark Johnston {
8778f57a9cSMark Johnston
8878f57a9cSMark Johnston /*
8978f57a9cSMark Johnston * Memory for zlib state is allocated using M_NODUMP since it may be
9078f57a9cSMark Johnston * used to compress a kernel dump, and we don't want zlib to attempt to
9178f57a9cSMark Johnston * compress its own state.
9278f57a9cSMark Johnston */
9378f57a9cSMark Johnston return (malloc(n * sz, M_COMPRESS, M_WAITOK | M_ZERO | M_NODUMP));
9478f57a9cSMark Johnston }
9578f57a9cSMark Johnston
9678f57a9cSMark Johnston static void
gz_free(void * arg __unused,void * ptr)9778f57a9cSMark Johnston gz_free(void *arg __unused, void *ptr)
9878f57a9cSMark Johnston {
9978f57a9cSMark Johnston
10078f57a9cSMark Johnston free(ptr, M_COMPRESS);
10178f57a9cSMark Johnston }
10278f57a9cSMark Johnston
10378f57a9cSMark Johnston static void *
gz_init(size_t maxiosize,int level)10478f57a9cSMark Johnston gz_init(size_t maxiosize, int level)
10578f57a9cSMark Johnston {
10678f57a9cSMark Johnston struct gz_stream *s;
10778f57a9cSMark Johnston int error;
10878f57a9cSMark Johnston
10978f57a9cSMark Johnston s = gz_alloc(NULL, 1, roundup2(sizeof(*s), PAGE_SIZE));
11078f57a9cSMark Johnston s->gz_buffer = gz_alloc(NULL, 1, maxiosize);
11178f57a9cSMark Johnston s->gz_bufsz = maxiosize;
11278f57a9cSMark Johnston
11378f57a9cSMark Johnston s->gz_stream.zalloc = gz_alloc;
11478f57a9cSMark Johnston s->gz_stream.zfree = gz_free;
11578f57a9cSMark Johnston s->gz_stream.opaque = NULL;
11678f57a9cSMark Johnston s->gz_stream.next_in = Z_NULL;
11778f57a9cSMark Johnston s->gz_stream.avail_in = 0;
11878f57a9cSMark Johnston
1193cd1f28eSEric van Gyzen if (level != Z_DEFAULT_COMPRESSION) {
1203cd1f28eSEric van Gyzen if (level < Z_BEST_SPEED)
1213cd1f28eSEric van Gyzen level = Z_BEST_SPEED;
1223cd1f28eSEric van Gyzen else if (level > Z_BEST_COMPRESSION)
1233cd1f28eSEric van Gyzen level = Z_BEST_COMPRESSION;
1243cd1f28eSEric van Gyzen }
1253cd1f28eSEric van Gyzen
12678f57a9cSMark Johnston error = deflateInit2(&s->gz_stream, level, Z_DEFLATED, -MAX_WBITS,
12778f57a9cSMark Johnston DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
12878f57a9cSMark Johnston if (error != 0)
12978f57a9cSMark Johnston goto fail;
13078f57a9cSMark Johnston
13178f57a9cSMark Johnston gz_reset(s);
13278f57a9cSMark Johnston
13378f57a9cSMark Johnston return (s);
13478f57a9cSMark Johnston
13578f57a9cSMark Johnston fail:
13678f57a9cSMark Johnston gz_free(NULL, s);
13778f57a9cSMark Johnston return (NULL);
13878f57a9cSMark Johnston }
13978f57a9cSMark Johnston
14078f57a9cSMark Johnston static void
gz_reset(void * stream)14178f57a9cSMark Johnston gz_reset(void *stream)
14278f57a9cSMark Johnston {
14378f57a9cSMark Johnston struct gz_stream *s;
14478f57a9cSMark Johnston uint8_t *hdr;
14578f57a9cSMark Johnston const size_t hdrlen = 10;
14678f57a9cSMark Johnston
14778f57a9cSMark Johnston s = stream;
14878f57a9cSMark Johnston s->gz_off = 0;
1494e8671ddSXin LI s->gz_crc = crc32(0L, Z_NULL, 0);
15078f57a9cSMark Johnston
15178f57a9cSMark Johnston (void)deflateReset(&s->gz_stream);
15278f57a9cSMark Johnston s->gz_stream.avail_out = s->gz_bufsz;
15378f57a9cSMark Johnston s->gz_stream.next_out = s->gz_buffer;
15478f57a9cSMark Johnston
15578f57a9cSMark Johnston /* Write the gzip header to the output buffer. */
15678f57a9cSMark Johnston hdr = s->gz_buffer;
15778f57a9cSMark Johnston memset(hdr, 0, hdrlen);
15878f57a9cSMark Johnston hdr[0] = 0x1f;
15978f57a9cSMark Johnston hdr[1] = 0x8b;
16078f57a9cSMark Johnston hdr[2] = Z_DEFLATED;
16178f57a9cSMark Johnston hdr[9] = OS_CODE;
16278f57a9cSMark Johnston s->gz_stream.next_out += hdrlen;
16378f57a9cSMark Johnston s->gz_stream.avail_out -= hdrlen;
16478f57a9cSMark Johnston }
16578f57a9cSMark Johnston
16678f57a9cSMark Johnston static int
gz_write(void * stream,void * data,size_t len,compressor_cb_t cb,void * arg)16778f57a9cSMark Johnston gz_write(void *stream, void *data, size_t len, compressor_cb_t cb,
16878f57a9cSMark Johnston void *arg)
16978f57a9cSMark Johnston {
17078f57a9cSMark Johnston struct gz_stream *s;
17178f57a9cSMark Johnston uint8_t trailer[8];
17278f57a9cSMark Johnston size_t room;
17378f57a9cSMark Johnston int error, zerror, zflag;
17478f57a9cSMark Johnston
17578f57a9cSMark Johnston s = stream;
17678f57a9cSMark Johnston zflag = data == NULL ? Z_FINISH : Z_NO_FLUSH;
17778f57a9cSMark Johnston
17878f57a9cSMark Johnston if (len > 0) {
17978f57a9cSMark Johnston s->gz_stream.avail_in = len;
18078f57a9cSMark Johnston s->gz_stream.next_in = data;
1814e8671ddSXin LI s->gz_crc = crc32(s->gz_crc, data, len);
1824e8671ddSXin LI }
18378f57a9cSMark Johnston
18478f57a9cSMark Johnston error = 0;
18578f57a9cSMark Johnston do {
18678f57a9cSMark Johnston zerror = deflate(&s->gz_stream, zflag);
18778f57a9cSMark Johnston if (zerror != Z_OK && zerror != Z_STREAM_END) {
18878f57a9cSMark Johnston error = EIO;
18978f57a9cSMark Johnston break;
19078f57a9cSMark Johnston }
19178f57a9cSMark Johnston
19278f57a9cSMark Johnston if (s->gz_stream.avail_out == 0 || zerror == Z_STREAM_END) {
19378f57a9cSMark Johnston /*
19478f57a9cSMark Johnston * Our output buffer is full or there's nothing left
19578f57a9cSMark Johnston * to produce, so we're flushing the buffer.
19678f57a9cSMark Johnston */
19778f57a9cSMark Johnston len = s->gz_bufsz - s->gz_stream.avail_out;
19878f57a9cSMark Johnston if (zerror == Z_STREAM_END) {
19978f57a9cSMark Johnston /*
20078f57a9cSMark Johnston * Try to pack as much of the trailer into the
20178f57a9cSMark Johnston * output buffer as we can.
20278f57a9cSMark Johnston */
2036db44b01SJustin Hibbits ((uint32_t *)trailer)[0] = htole32(s->gz_crc);
20478f57a9cSMark Johnston ((uint32_t *)trailer)[1] =
2056db44b01SJustin Hibbits htole32(s->gz_stream.total_in);
20678f57a9cSMark Johnston room = MIN(sizeof(trailer),
20778f57a9cSMark Johnston s->gz_bufsz - len);
20878f57a9cSMark Johnston memcpy(s->gz_buffer + len, trailer, room);
20978f57a9cSMark Johnston len += room;
21078f57a9cSMark Johnston }
21178f57a9cSMark Johnston
21278f57a9cSMark Johnston error = cb(s->gz_buffer, len, s->gz_off, arg);
21378f57a9cSMark Johnston if (error != 0)
21478f57a9cSMark Johnston break;
21578f57a9cSMark Johnston
21678f57a9cSMark Johnston s->gz_off += len;
21778f57a9cSMark Johnston s->gz_stream.next_out = s->gz_buffer;
21878f57a9cSMark Johnston s->gz_stream.avail_out = s->gz_bufsz;
21978f57a9cSMark Johnston
22078f57a9cSMark Johnston /*
22178f57a9cSMark Johnston * If we couldn't pack the trailer into the output
22278f57a9cSMark Johnston * buffer, write it out now.
22378f57a9cSMark Johnston */
22478f57a9cSMark Johnston if (zerror == Z_STREAM_END && room < sizeof(trailer))
22578f57a9cSMark Johnston error = cb(trailer + room,
22678f57a9cSMark Johnston sizeof(trailer) - room, s->gz_off, arg);
22778f57a9cSMark Johnston }
22878f57a9cSMark Johnston } while (zerror != Z_STREAM_END &&
22978f57a9cSMark Johnston (zflag == Z_FINISH || s->gz_stream.avail_in > 0));
23078f57a9cSMark Johnston
23178f57a9cSMark Johnston return (error);
23278f57a9cSMark Johnston }
23378f57a9cSMark Johnston
23478f57a9cSMark Johnston static void
gz_fini(void * stream)23578f57a9cSMark Johnston gz_fini(void *stream)
23678f57a9cSMark Johnston {
23778f57a9cSMark Johnston struct gz_stream *s;
23878f57a9cSMark Johnston
23978f57a9cSMark Johnston s = stream;
24078f57a9cSMark Johnston (void)deflateEnd(&s->gz_stream);
24178f57a9cSMark Johnston gz_free(NULL, s->gz_buffer);
24278f57a9cSMark Johnston gz_free(NULL, s);
24378f57a9cSMark Johnston }
24478f57a9cSMark Johnston
24578f57a9cSMark Johnston struct compressor_methods gzip_methods = {
24678f57a9cSMark Johnston .format = COMPRESS_GZIP,
24778f57a9cSMark Johnston .init = gz_init,
24878f57a9cSMark Johnston .reset = gz_reset,
24978f57a9cSMark Johnston .write = gz_write,
25078f57a9cSMark Johnston .fini = gz_fini,
25178f57a9cSMark Johnston };
25278f57a9cSMark Johnston DATA_SET(compressors, gzip_methods);
25378f57a9cSMark Johnston
25478f57a9cSMark Johnston #endif /* GZIO */
25578f57a9cSMark Johnston
2566026dcd7SMark Johnston #ifdef ZSTDIO
2576026dcd7SMark Johnston
2586026dcd7SMark Johnston #define ZSTD_STATIC_LINKING_ONLY
2596026dcd7SMark Johnston #include <contrib/zstd/lib/zstd.h>
2606026dcd7SMark Johnston
2616026dcd7SMark Johnston struct zstdio_stream {
2626026dcd7SMark Johnston ZSTD_CCtx *zst_stream;
2636026dcd7SMark Johnston ZSTD_inBuffer zst_inbuffer;
2646026dcd7SMark Johnston ZSTD_outBuffer zst_outbuffer;
2656026dcd7SMark Johnston uint8_t * zst_buffer; /* output buffer */
2666026dcd7SMark Johnston size_t zst_maxiosz; /* Max output IO size */
2676026dcd7SMark Johnston off_t zst_off; /* offset into the output stream */
2686026dcd7SMark Johnston void * zst_static_wkspc;
2696026dcd7SMark Johnston };
2706026dcd7SMark Johnston
2716026dcd7SMark Johnston static void *zstdio_init(size_t maxiosize, int level);
2726026dcd7SMark Johnston static void zstdio_reset(void *stream);
2736026dcd7SMark Johnston static int zstdio_write(void *stream, void *data, size_t len,
2746026dcd7SMark Johnston compressor_cb_t, void *);
2756026dcd7SMark Johnston static void zstdio_fini(void *stream);
2766026dcd7SMark Johnston
2776026dcd7SMark Johnston static void *
zstdio_init(size_t maxiosize,int level)2786026dcd7SMark Johnston zstdio_init(size_t maxiosize, int level)
2796026dcd7SMark Johnston {
2806026dcd7SMark Johnston ZSTD_CCtx *dump_compressor;
2816026dcd7SMark Johnston struct zstdio_stream *s;
2826026dcd7SMark Johnston void *wkspc, *owkspc, *buffer;
283767bc248SConrad Meyer size_t wkspc_size, buf_size, rc;
2846026dcd7SMark Johnston
285767bc248SConrad Meyer s = NULL;
2866026dcd7SMark Johnston wkspc_size = ZSTD_estimateCStreamSize(level);
2876026dcd7SMark Johnston owkspc = wkspc = malloc(wkspc_size + 8, M_COMPRESS,
2886026dcd7SMark Johnston M_WAITOK | M_NODUMP);
2896026dcd7SMark Johnston /* Zstd API requires 8-byte alignment. */
2906026dcd7SMark Johnston if ((uintptr_t)wkspc % 8 != 0)
2916026dcd7SMark Johnston wkspc = (void *)roundup2((uintptr_t)wkspc, 8);
2926026dcd7SMark Johnston
2936026dcd7SMark Johnston dump_compressor = ZSTD_initStaticCCtx(wkspc, wkspc_size);
2946026dcd7SMark Johnston if (dump_compressor == NULL) {
2956026dcd7SMark Johnston printf("%s: workspace too small.\n", __func__);
296767bc248SConrad Meyer goto out;
2976026dcd7SMark Johnston }
2986026dcd7SMark Johnston
299a0483764SConrad Meyer rc = ZSTD_CCtx_setParameter(dump_compressor, ZSTD_c_checksumFlag, 1);
300767bc248SConrad Meyer if (ZSTD_isError(rc)) {
301767bc248SConrad Meyer printf("%s: error setting checksumFlag: %s\n", __func__,
302767bc248SConrad Meyer ZSTD_getErrorName(rc));
303767bc248SConrad Meyer goto out;
304767bc248SConrad Meyer }
305a0483764SConrad Meyer rc = ZSTD_CCtx_setParameter(dump_compressor, ZSTD_c_compressionLevel,
306767bc248SConrad Meyer level);
307767bc248SConrad Meyer if (ZSTD_isError(rc)) {
308767bc248SConrad Meyer printf("%s: error setting compressLevel: %s\n", __func__,
309767bc248SConrad Meyer ZSTD_getErrorName(rc));
310767bc248SConrad Meyer goto out;
311767bc248SConrad Meyer }
3126026dcd7SMark Johnston
3136026dcd7SMark Johnston buf_size = ZSTD_CStreamOutSize() * 2;
3146026dcd7SMark Johnston buffer = malloc(buf_size, M_COMPRESS, M_WAITOK | M_NODUMP);
3156026dcd7SMark Johnston
3166026dcd7SMark Johnston s = malloc(sizeof(*s), M_COMPRESS, M_NODUMP | M_WAITOK);
3176026dcd7SMark Johnston s->zst_buffer = buffer;
3186026dcd7SMark Johnston s->zst_outbuffer.dst = buffer;
3196026dcd7SMark Johnston s->zst_outbuffer.size = buf_size;
3206026dcd7SMark Johnston s->zst_maxiosz = maxiosize;
3216026dcd7SMark Johnston s->zst_stream = dump_compressor;
3226026dcd7SMark Johnston s->zst_static_wkspc = owkspc;
3236026dcd7SMark Johnston
3246026dcd7SMark Johnston zstdio_reset(s);
3256026dcd7SMark Johnston
326767bc248SConrad Meyer out:
327767bc248SConrad Meyer if (s == NULL)
328767bc248SConrad Meyer free(owkspc, M_COMPRESS);
3296026dcd7SMark Johnston return (s);
3306026dcd7SMark Johnston }
3316026dcd7SMark Johnston
3326026dcd7SMark Johnston static void
zstdio_reset(void * stream)3336026dcd7SMark Johnston zstdio_reset(void *stream)
3346026dcd7SMark Johnston {
3356026dcd7SMark Johnston struct zstdio_stream *s;
3366026dcd7SMark Johnston size_t res;
3376026dcd7SMark Johnston
3386026dcd7SMark Johnston s = stream;
3395ff13fbcSAllan Jude res = ZSTD_CCtx_reset(s->zst_stream, ZSTD_reset_session_only);
3406026dcd7SMark Johnston if (ZSTD_isError(res))
3416026dcd7SMark Johnston panic("%s: could not reset stream %p: %s\n", __func__, s,
3426026dcd7SMark Johnston ZSTD_getErrorName(res));
3435ff13fbcSAllan Jude res = ZSTD_CCtx_setPledgedSrcSize(s->zst_stream,
3445ff13fbcSAllan Jude ZSTD_CONTENTSIZE_UNKNOWN);
3455ff13fbcSAllan Jude if (ZSTD_isError(res))
3465ff13fbcSAllan Jude panic("%s: could not set src size on %p: %s\n", __func__, s,
3475ff13fbcSAllan Jude ZSTD_getErrorName(res));
3486026dcd7SMark Johnston
3496026dcd7SMark Johnston s->zst_off = 0;
3506026dcd7SMark Johnston s->zst_inbuffer.src = NULL;
3516026dcd7SMark Johnston s->zst_inbuffer.size = 0;
3526026dcd7SMark Johnston s->zst_inbuffer.pos = 0;
3536026dcd7SMark Johnston s->zst_outbuffer.pos = 0;
3546026dcd7SMark Johnston }
3556026dcd7SMark Johnston
3566026dcd7SMark Johnston static int
zst_flush_intermediate(struct zstdio_stream * s,compressor_cb_t cb,void * arg)3576026dcd7SMark Johnston zst_flush_intermediate(struct zstdio_stream *s, compressor_cb_t cb, void *arg)
3586026dcd7SMark Johnston {
3596026dcd7SMark Johnston size_t bytes_to_dump;
3606026dcd7SMark Johnston int error;
3616026dcd7SMark Johnston
3626026dcd7SMark Johnston /* Flush as many full output blocks as possible. */
3636026dcd7SMark Johnston /* XXX: 4096 is arbitrary safe HDD block size for kernel dumps */
3646026dcd7SMark Johnston while (s->zst_outbuffer.pos >= 4096) {
3656026dcd7SMark Johnston bytes_to_dump = rounddown(s->zst_outbuffer.pos, 4096);
3666026dcd7SMark Johnston
3676026dcd7SMark Johnston if (bytes_to_dump > s->zst_maxiosz)
3686026dcd7SMark Johnston bytes_to_dump = s->zst_maxiosz;
3696026dcd7SMark Johnston
3706026dcd7SMark Johnston error = cb(s->zst_buffer, bytes_to_dump, s->zst_off, arg);
3716026dcd7SMark Johnston if (error != 0)
3726026dcd7SMark Johnston return (error);
3736026dcd7SMark Johnston
3746026dcd7SMark Johnston /*
3756026dcd7SMark Johnston * Shift any non-full blocks up to the front of the output
3766026dcd7SMark Johnston * buffer.
3776026dcd7SMark Johnston */
3786026dcd7SMark Johnston s->zst_outbuffer.pos -= bytes_to_dump;
3796026dcd7SMark Johnston memmove(s->zst_outbuffer.dst,
3806026dcd7SMark Johnston (char *)s->zst_outbuffer.dst + bytes_to_dump,
3816026dcd7SMark Johnston s->zst_outbuffer.pos);
3826026dcd7SMark Johnston s->zst_off += bytes_to_dump;
3836026dcd7SMark Johnston }
3846026dcd7SMark Johnston return (0);
3856026dcd7SMark Johnston }
3866026dcd7SMark Johnston
3876026dcd7SMark Johnston static int
zstdio_flush(struct zstdio_stream * s,compressor_cb_t cb,void * arg)3886026dcd7SMark Johnston zstdio_flush(struct zstdio_stream *s, compressor_cb_t cb, void *arg)
3896026dcd7SMark Johnston {
3906026dcd7SMark Johnston size_t rc, lastpos;
3916026dcd7SMark Johnston int error;
3926026dcd7SMark Johnston
3936026dcd7SMark Johnston /*
3946026dcd7SMark Johnston * Positive return indicates unflushed data remaining; need to call
3956026dcd7SMark Johnston * endStream again after clearing out room in output buffer.
3966026dcd7SMark Johnston */
3976026dcd7SMark Johnston rc = 1;
3986026dcd7SMark Johnston lastpos = s->zst_outbuffer.pos;
3996026dcd7SMark Johnston while (rc > 0) {
4006026dcd7SMark Johnston rc = ZSTD_endStream(s->zst_stream, &s->zst_outbuffer);
4016026dcd7SMark Johnston if (ZSTD_isError(rc)) {
4026026dcd7SMark Johnston printf("%s: ZSTD_endStream failed (%s)\n", __func__,
4036026dcd7SMark Johnston ZSTD_getErrorName(rc));
4046026dcd7SMark Johnston return (EIO);
4056026dcd7SMark Johnston }
4066026dcd7SMark Johnston if (lastpos == s->zst_outbuffer.pos) {
4076026dcd7SMark Johnston printf("%s: did not make forward progress endStream %zu\n",
4086026dcd7SMark Johnston __func__, lastpos);
4096026dcd7SMark Johnston return (EIO);
4106026dcd7SMark Johnston }
4116026dcd7SMark Johnston
4126026dcd7SMark Johnston error = zst_flush_intermediate(s, cb, arg);
4136026dcd7SMark Johnston if (error != 0)
4146026dcd7SMark Johnston return (error);
4156026dcd7SMark Johnston
4166026dcd7SMark Johnston lastpos = s->zst_outbuffer.pos;
4176026dcd7SMark Johnston }
4186026dcd7SMark Johnston
4196026dcd7SMark Johnston /*
4206026dcd7SMark Johnston * We've already done an intermediate flush, so all full blocks have
4216026dcd7SMark Johnston * been written. Only a partial block remains. Padding happens in a
4226026dcd7SMark Johnston * higher layer.
4236026dcd7SMark Johnston */
4246026dcd7SMark Johnston if (s->zst_outbuffer.pos != 0) {
4256026dcd7SMark Johnston error = cb(s->zst_buffer, s->zst_outbuffer.pos, s->zst_off,
4266026dcd7SMark Johnston arg);
4276026dcd7SMark Johnston if (error != 0)
4286026dcd7SMark Johnston return (error);
4296026dcd7SMark Johnston }
4306026dcd7SMark Johnston
4316026dcd7SMark Johnston return (0);
4326026dcd7SMark Johnston }
4336026dcd7SMark Johnston
4346026dcd7SMark Johnston static int
zstdio_write(void * stream,void * data,size_t len,compressor_cb_t cb,void * arg)4356026dcd7SMark Johnston zstdio_write(void *stream, void *data, size_t len, compressor_cb_t cb,
4366026dcd7SMark Johnston void *arg)
4376026dcd7SMark Johnston {
4386026dcd7SMark Johnston struct zstdio_stream *s;
4396026dcd7SMark Johnston size_t lastpos, rc;
4406026dcd7SMark Johnston int error;
4416026dcd7SMark Johnston
4426026dcd7SMark Johnston s = stream;
4436026dcd7SMark Johnston if (data == NULL)
4446026dcd7SMark Johnston return (zstdio_flush(s, cb, arg));
4456026dcd7SMark Johnston
4466026dcd7SMark Johnston s->zst_inbuffer.src = data;
4476026dcd7SMark Johnston s->zst_inbuffer.size = len;
4486026dcd7SMark Johnston s->zst_inbuffer.pos = 0;
4496026dcd7SMark Johnston lastpos = 0;
4506026dcd7SMark Johnston
4516026dcd7SMark Johnston while (s->zst_inbuffer.pos < s->zst_inbuffer.size) {
4526026dcd7SMark Johnston rc = ZSTD_compressStream(s->zst_stream, &s->zst_outbuffer,
4536026dcd7SMark Johnston &s->zst_inbuffer);
4546026dcd7SMark Johnston if (ZSTD_isError(rc)) {
4556026dcd7SMark Johnston printf("%s: Compress failed on %p! (%s)\n",
4566026dcd7SMark Johnston __func__, data, ZSTD_getErrorName(rc));
4576026dcd7SMark Johnston return (EIO);
4586026dcd7SMark Johnston }
4596026dcd7SMark Johnston
4606026dcd7SMark Johnston if (lastpos == s->zst_inbuffer.pos) {
4616026dcd7SMark Johnston /*
4626026dcd7SMark Johnston * XXX: May need flushStream to make forward progress
4636026dcd7SMark Johnston */
4646026dcd7SMark Johnston printf("ZSTD: did not make forward progress @pos %zu\n",
4656026dcd7SMark Johnston lastpos);
4666026dcd7SMark Johnston return (EIO);
4676026dcd7SMark Johnston }
4686026dcd7SMark Johnston lastpos = s->zst_inbuffer.pos;
4696026dcd7SMark Johnston
4706026dcd7SMark Johnston error = zst_flush_intermediate(s, cb, arg);
4716026dcd7SMark Johnston if (error != 0)
4726026dcd7SMark Johnston return (error);
4736026dcd7SMark Johnston }
4746026dcd7SMark Johnston return (0);
4756026dcd7SMark Johnston }
4766026dcd7SMark Johnston
4776026dcd7SMark Johnston static void
zstdio_fini(void * stream)4786026dcd7SMark Johnston zstdio_fini(void *stream)
4796026dcd7SMark Johnston {
4806026dcd7SMark Johnston struct zstdio_stream *s;
4816026dcd7SMark Johnston
4826026dcd7SMark Johnston s = stream;
4836026dcd7SMark Johnston if (s->zst_static_wkspc != NULL)
4846026dcd7SMark Johnston free(s->zst_static_wkspc, M_COMPRESS);
4856026dcd7SMark Johnston else
4866026dcd7SMark Johnston ZSTD_freeCCtx(s->zst_stream);
4876026dcd7SMark Johnston free(s->zst_buffer, M_COMPRESS);
4886026dcd7SMark Johnston free(s, M_COMPRESS);
4896026dcd7SMark Johnston }
4906026dcd7SMark Johnston
4916026dcd7SMark Johnston static struct compressor_methods zstd_methods = {
4926026dcd7SMark Johnston .format = COMPRESS_ZSTD,
4936026dcd7SMark Johnston .init = zstdio_init,
4946026dcd7SMark Johnston .reset = zstdio_reset,
4956026dcd7SMark Johnston .write = zstdio_write,
4966026dcd7SMark Johnston .fini = zstdio_fini,
4976026dcd7SMark Johnston };
4986026dcd7SMark Johnston DATA_SET(compressors, zstd_methods);
4996026dcd7SMark Johnston
5006026dcd7SMark Johnston #endif /* ZSTDIO */
5016026dcd7SMark Johnston
50278f57a9cSMark Johnston bool
compressor_avail(int format)50378f57a9cSMark Johnston compressor_avail(int format)
50478f57a9cSMark Johnston {
50578f57a9cSMark Johnston struct compressor_methods **iter;
50678f57a9cSMark Johnston
50778f57a9cSMark Johnston SET_FOREACH(iter, compressors) {
50878f57a9cSMark Johnston if ((*iter)->format == format)
50978f57a9cSMark Johnston return (true);
51078f57a9cSMark Johnston }
51178f57a9cSMark Johnston return (false);
51278f57a9cSMark Johnston }
51378f57a9cSMark Johnston
51478f57a9cSMark Johnston struct compressor *
compressor_init(compressor_cb_t cb,int format,size_t maxiosize,int level,void * arg)51578f57a9cSMark Johnston compressor_init(compressor_cb_t cb, int format, size_t maxiosize, int level,
51678f57a9cSMark Johnston void *arg)
51778f57a9cSMark Johnston {
51878f57a9cSMark Johnston struct compressor_methods **iter;
51978f57a9cSMark Johnston struct compressor *s;
52078f57a9cSMark Johnston void *priv;
52178f57a9cSMark Johnston
52278f57a9cSMark Johnston SET_FOREACH(iter, compressors) {
52378f57a9cSMark Johnston if ((*iter)->format == format)
52478f57a9cSMark Johnston break;
52578f57a9cSMark Johnston }
52605f0f0e9SMark Johnston if (iter == SET_LIMIT(compressors))
52778f57a9cSMark Johnston return (NULL);
52878f57a9cSMark Johnston
52978f57a9cSMark Johnston priv = (*iter)->init(maxiosize, level);
53078f57a9cSMark Johnston if (priv == NULL)
53178f57a9cSMark Johnston return (NULL);
53278f57a9cSMark Johnston
53378f57a9cSMark Johnston s = malloc(sizeof(*s), M_COMPRESS, M_WAITOK | M_ZERO);
53478f57a9cSMark Johnston s->methods = (*iter);
53578f57a9cSMark Johnston s->priv = priv;
53678f57a9cSMark Johnston s->cb = cb;
53778f57a9cSMark Johnston s->arg = arg;
53878f57a9cSMark Johnston return (s);
53978f57a9cSMark Johnston }
54078f57a9cSMark Johnston
54178f57a9cSMark Johnston void
compressor_reset(struct compressor * stream)54278f57a9cSMark Johnston compressor_reset(struct compressor *stream)
54378f57a9cSMark Johnston {
54478f57a9cSMark Johnston
54578f57a9cSMark Johnston stream->methods->reset(stream->priv);
54678f57a9cSMark Johnston }
54778f57a9cSMark Johnston
54878f57a9cSMark Johnston int
compressor_write(struct compressor * stream,void * data,size_t len)54978f57a9cSMark Johnston compressor_write(struct compressor *stream, void *data, size_t len)
55078f57a9cSMark Johnston {
55178f57a9cSMark Johnston
55278f57a9cSMark Johnston return (stream->methods->write(stream->priv, data, len, stream->cb,
55378f57a9cSMark Johnston stream->arg));
55478f57a9cSMark Johnston }
55578f57a9cSMark Johnston
55678f57a9cSMark Johnston int
compressor_flush(struct compressor * stream)55778f57a9cSMark Johnston compressor_flush(struct compressor *stream)
55878f57a9cSMark Johnston {
55978f57a9cSMark Johnston
56078f57a9cSMark Johnston return (stream->methods->write(stream->priv, NULL, 0, stream->cb,
56178f57a9cSMark Johnston stream->arg));
56278f57a9cSMark Johnston }
56378f57a9cSMark Johnston
56478f57a9cSMark Johnston void
compressor_fini(struct compressor * stream)56578f57a9cSMark Johnston compressor_fini(struct compressor *stream)
56678f57a9cSMark Johnston {
56778f57a9cSMark Johnston
56878f57a9cSMark Johnston stream->methods->fini(stream->priv);
56978f57a9cSMark Johnston }
570