/* * Copyright 1998-2023 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html * * Uses zstd compression library from https://github.com/facebook/zstd * Requires version 1.4.x (latest as of this writing is 1.4.5) * Using custom free functions require static linking, so that is disabled when * using the shared library. */ #include #include #include #include #include "internal/comp.h" #include #include "crypto/cryptlib.h" #include "internal/bio.h" #include "internal/thread_once.h" #include "comp_local.h" COMP_METHOD *COMP_zstd(void); #ifdef OPENSSL_NO_ZSTD # undef ZSTD_SHARED #else # ifndef ZSTD_SHARED # define ZSTD_STATIC_LINKING_ONLY # endif # include /* Note: There is also a linux zstd.h file in the kernel source */ # ifndef ZSTD_H_235446 # error Wrong (i.e. linux) zstd.h included. # endif # if ZSTD_VERSION_MAJOR != 1 && ZSTD_VERSION_MINOR < 4 # error Expecting version 1.4 or greater of ZSTD # endif # ifndef ZSTD_SHARED /* memory allocations functions for zstd initialisation */ static void *zstd_alloc(void *opaque, size_t size) { return OPENSSL_zalloc(size); } static void zstd_free(void *opaque, void *address) { OPENSSL_free(address); } static ZSTD_customMem zstd_mem_funcs = { zstd_alloc, zstd_free, NULL }; # endif /* * When OpenSSL is built on Windows, we do not want to require that * the LIBZSTD.DLL be available in order for the OpenSSL DLLs to * work. Therefore, all ZSTD routines are loaded at run time * and we do not link to a .LIB file when ZSTD_SHARED is set. */ # if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) # include # endif # ifdef ZSTD_SHARED # include "internal/dso.h" /* Function pointers */ typedef ZSTD_CStream* (*createCStream_ft)(void); typedef size_t (*initCStream_ft)(ZSTD_CStream*, int); typedef size_t (*freeCStream_ft)(ZSTD_CStream*); typedef size_t (*compressStream2_ft)(ZSTD_CCtx*, ZSTD_outBuffer*, ZSTD_inBuffer*, ZSTD_EndDirective); typedef size_t (*flushStream_ft)(ZSTD_CStream*, ZSTD_outBuffer*); typedef size_t (*endStream_ft)(ZSTD_CStream*, ZSTD_outBuffer*); typedef size_t (*compress_ft)(void*, size_t, const void*, size_t, int); typedef ZSTD_DStream* (*createDStream_ft)(void); typedef size_t (*initDStream_ft)(ZSTD_DStream*); typedef size_t (*freeDStream_ft)(ZSTD_DStream*); typedef size_t (*decompressStream_ft)(ZSTD_DStream*, ZSTD_outBuffer*, ZSTD_inBuffer*); typedef size_t (*decompress_ft)(void*, size_t, const void*, size_t); typedef unsigned (*isError_ft)(size_t); typedef const char* (*getErrorName_ft)(size_t); typedef size_t (*DStreamInSize_ft)(void); typedef size_t (*CStreamInSize_ft)(void); static createCStream_ft p_createCStream = NULL; static initCStream_ft p_initCStream = NULL; static freeCStream_ft p_freeCStream = NULL; static compressStream2_ft p_compressStream2 = NULL; static flushStream_ft p_flushStream = NULL; static endStream_ft p_endStream = NULL; static compress_ft p_compress = NULL; static createDStream_ft p_createDStream = NULL; static initDStream_ft p_initDStream = NULL; static freeDStream_ft p_freeDStream = NULL; static decompressStream_ft p_decompressStream = NULL; static decompress_ft p_decompress = NULL; static isError_ft p_isError = NULL; static getErrorName_ft p_getErrorName = NULL; static DStreamInSize_ft p_DStreamInSize = NULL; static CStreamInSize_ft p_CStreamInSize = NULL; static DSO *zstd_dso = NULL; # define ZSTD_createCStream p_createCStream # define ZSTD_initCStream p_initCStream # define ZSTD_freeCStream p_freeCStream # define ZSTD_compressStream2 p_compressStream2 # define ZSTD_flushStream p_flushStream # define ZSTD_endStream p_endStream # define ZSTD_compress p_compress # define ZSTD_createDStream p_createDStream # define ZSTD_initDStream p_initDStream # define ZSTD_freeDStream p_freeDStream # define ZSTD_decompressStream p_decompressStream # define ZSTD_decompress p_decompress # define ZSTD_isError p_isError # define ZSTD_getErrorName p_getErrorName # define ZSTD_DStreamInSize p_DStreamInSize # define ZSTD_CStreamInSize p_CStreamInSize # endif /* ifdef ZSTD_SHARED */ struct zstd_state { ZSTD_CStream *compressor; ZSTD_DStream *decompressor; }; static int zstd_stateful_init(COMP_CTX *ctx) { struct zstd_state *state = OPENSSL_zalloc(sizeof(*state)); if (state == NULL) return 0; # ifdef ZSTD_SHARED state->compressor = ZSTD_createCStream(); # else state->compressor = ZSTD_createCStream_advanced(zstd_mem_funcs); # endif if (state->compressor == NULL) goto err; ZSTD_initCStream(state->compressor, ZSTD_CLEVEL_DEFAULT); # ifdef ZSTD_SHARED state->decompressor = ZSTD_createDStream(); # else state->decompressor = ZSTD_createDStream_advanced(zstd_mem_funcs); # endif if (state->decompressor == NULL) goto err; ZSTD_initDStream(state->decompressor); ctx->data = state; return 1; err: ZSTD_freeCStream(state->compressor); ZSTD_freeDStream(state->decompressor); OPENSSL_free(state); return 0; } static void zstd_stateful_finish(COMP_CTX *ctx) { struct zstd_state *state = ctx->data; if (state != NULL) { ZSTD_freeCStream(state->compressor); ZSTD_freeDStream(state->decompressor); OPENSSL_free(state); ctx->data = NULL; } } static ossl_ssize_t zstd_stateful_compress_block(COMP_CTX *ctx, unsigned char *out, size_t olen, unsigned char *in, size_t ilen) { ZSTD_inBuffer inbuf; ZSTD_outBuffer outbuf; size_t ret; ossl_ssize_t fret; struct zstd_state *state = ctx->data; inbuf.src = in; inbuf.size = ilen; inbuf.pos = 0; outbuf.dst = out; outbuf.size = olen; outbuf.pos = 0; if (state == NULL) return -1; /* If input length is zero, end the stream/frame ? */ if (ilen == 0) { ret = ZSTD_endStream(state->compressor, &outbuf); if (ZSTD_isError(ret)) return -1; goto end; } /* * The finish API does not provide a final output buffer, * so each compress operation has to be ended, if all * the input data can't be accepted, or there is more output, * this has to be considered an error, since there is no more * output buffer space. */ do { ret = ZSTD_compressStream2(state->compressor, &outbuf, &inbuf, ZSTD_e_continue); if (ZSTD_isError(ret)) return -1; /* do I need to check for ret == 0 ? */ } while (inbuf.pos < inbuf.size); /* Did not consume all the data */ if (inbuf.pos < inbuf.size) return -1; ret = ZSTD_flushStream(state->compressor, &outbuf); if (ZSTD_isError(ret)) return -1; end: if (outbuf.pos > OSSL_SSIZE_MAX) return -1; fret = (ossl_ssize_t)outbuf.pos; if (fret < 0) return -1; return fret; } static ossl_ssize_t zstd_stateful_expand_block(COMP_CTX *ctx, unsigned char *out, size_t olen, unsigned char *in, size_t ilen) { ZSTD_inBuffer inbuf; ZSTD_outBuffer outbuf; size_t ret; ossl_ssize_t fret; struct zstd_state *state = ctx->data; inbuf.src = in; inbuf.size = ilen; inbuf.pos = 0; outbuf.dst = out; outbuf.size = olen; outbuf.pos = 0; if (state == NULL) return -1; if (ilen == 0) return 0; do { ret = ZSTD_decompressStream(state->decompressor, &outbuf, &inbuf); if (ZSTD_isError(ret)) return -1; /* If we completed a frame, and there's more data, try again */ } while (ret == 0 && inbuf.pos < inbuf.size); /* Did not consume all the data */ if (inbuf.pos < inbuf.size) return -1; if (outbuf.pos > OSSL_SSIZE_MAX) return -1; fret = (ossl_ssize_t)outbuf.pos; if (fret < 0) return -1; return fret; } static COMP_METHOD zstd_stateful_method = { NID_zstd, LN_zstd, zstd_stateful_init, zstd_stateful_finish, zstd_stateful_compress_block, zstd_stateful_expand_block }; static int zstd_oneshot_init(COMP_CTX *ctx) { return 1; } static void zstd_oneshot_finish(COMP_CTX *ctx) { } static ossl_ssize_t zstd_oneshot_compress_block(COMP_CTX *ctx, unsigned char *out, size_t olen, unsigned char *in, size_t ilen) { size_t out_size; ossl_ssize_t ret; if (ilen == 0) return 0; /* Note: uses STDLIB memory allocators */ out_size = ZSTD_compress(out, olen, in, ilen, ZSTD_CLEVEL_DEFAULT); if (ZSTD_isError(out_size)) return -1; if (out_size > OSSL_SSIZE_MAX) return -1; ret = (ossl_ssize_t)out_size; if (ret < 0) return -1; return ret; } static ossl_ssize_t zstd_oneshot_expand_block(COMP_CTX *ctx, unsigned char *out, size_t olen, unsigned char *in, size_t ilen) { size_t out_size; ossl_ssize_t ret; if (ilen == 0) return 0; /* Note: uses STDLIB memory allocators */ out_size = ZSTD_decompress(out, olen, in, ilen); if (ZSTD_isError(out_size)) return -1; if (out_size > OSSL_SSIZE_MAX) return -1; ret = (ossl_ssize_t)out_size; if (ret < 0) return -1; return ret; } static COMP_METHOD zstd_oneshot_method = { NID_zstd, LN_zstd, zstd_oneshot_init, zstd_oneshot_finish, zstd_oneshot_compress_block, zstd_oneshot_expand_block }; static CRYPTO_ONCE zstd_once = CRYPTO_ONCE_STATIC_INIT; DEFINE_RUN_ONCE_STATIC(ossl_comp_zstd_init) { # ifdef ZSTD_SHARED # if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) # define LIBZSTD "LIBZSTD" # else # define LIBZSTD "zstd" # endif zstd_dso = DSO_load(NULL, LIBZSTD, NULL, 0); if (zstd_dso != NULL) { p_createCStream = (createCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_createCStream"); p_initCStream = (initCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_initCStream"); p_freeCStream = (freeCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_freeCStream"); p_compressStream2 = (compressStream2_ft)DSO_bind_func(zstd_dso, "ZSTD_compressStream2"); p_flushStream = (flushStream_ft)DSO_bind_func(zstd_dso, "ZSTD_flushStream"); p_endStream = (endStream_ft)DSO_bind_func(zstd_dso, "ZSTD_endStream"); p_compress = (compress_ft)DSO_bind_func(zstd_dso, "ZSTD_compress"); p_createDStream = (createDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_createDStream"); p_initDStream = (initDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_initDStream"); p_freeDStream = (freeDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_freeDStream"); p_decompressStream = (decompressStream_ft)DSO_bind_func(zstd_dso, "ZSTD_decompressStream"); p_decompress = (decompress_ft)DSO_bind_func(zstd_dso, "ZSTD_decompress"); p_isError = (isError_ft)DSO_bind_func(zstd_dso, "ZSTD_isError"); p_getErrorName = (getErrorName_ft)DSO_bind_func(zstd_dso, "ZSTD_getErrorName"); p_DStreamInSize = (DStreamInSize_ft)DSO_bind_func(zstd_dso, "ZSTD_DStreamInSize"); p_CStreamInSize = (CStreamInSize_ft)DSO_bind_func(zstd_dso, "ZSTD_CStreamInSize"); } if (p_createCStream == NULL || p_initCStream == NULL || p_freeCStream == NULL || p_compressStream2 == NULL || p_flushStream == NULL || p_endStream == NULL || p_compress == NULL || p_createDStream == NULL || p_initDStream == NULL || p_freeDStream == NULL || p_decompressStream == NULL || p_decompress == NULL || p_isError == NULL || p_getErrorName == NULL || p_DStreamInSize == NULL || p_CStreamInSize == NULL) { ossl_comp_zstd_cleanup(); return 0; } # endif return 1; } #endif /* ifndef ZSTD / else */ COMP_METHOD *COMP_zstd(void) { COMP_METHOD *meth = NULL; #ifndef OPENSSL_NO_ZSTD if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) meth = &zstd_stateful_method; #endif return meth; } COMP_METHOD *COMP_zstd_oneshot(void) { COMP_METHOD *meth = NULL; #ifndef OPENSSL_NO_ZSTD if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) meth = &zstd_oneshot_method; #endif return meth; } /* Also called from OPENSSL_cleanup() */ void ossl_comp_zstd_cleanup(void) { #ifdef ZSTD_SHARED DSO_free(zstd_dso); zstd_dso = NULL; p_createCStream = NULL; p_initCStream = NULL; p_freeCStream = NULL; p_compressStream2 = NULL; p_flushStream = NULL; p_endStream = NULL; p_compress = NULL; p_createDStream = NULL; p_initDStream = NULL; p_freeDStream = NULL; p_decompressStream = NULL; p_decompress = NULL; p_isError = NULL; p_getErrorName = NULL; p_DStreamInSize = NULL; p_CStreamInSize = NULL; #endif } #ifndef OPENSSL_NO_ZSTD /* Zstd-based compression/decompression filter BIO */ typedef struct { struct { /* input structure */ ZSTD_DStream *state; ZSTD_inBuffer inbuf; /* has const src */ size_t bufsize; void* buffer; } decompress; struct { /* output structure */ ZSTD_CStream *state; ZSTD_outBuffer outbuf; size_t bufsize; size_t write_pos; } compress; } BIO_ZSTD_CTX; # define ZSTD_DEFAULT_BUFSIZE 1024 static int bio_zstd_new(BIO *bi); static int bio_zstd_free(BIO *bi); static int bio_zstd_read(BIO *b, char *out, int outl); static int bio_zstd_write(BIO *b, const char *in, int inl); static long bio_zstd_ctrl(BIO *b, int cmd, long num, void *ptr); static long bio_zstd_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp); static const BIO_METHOD bio_meth_zstd = { BIO_TYPE_COMP, "zstd", /* TODO: Convert to new style write function */ bwrite_conv, bio_zstd_write, /* TODO: Convert to new style read function */ bread_conv, bio_zstd_read, NULL, /* bio_zstd_puts, */ NULL, /* bio_zstd_gets, */ bio_zstd_ctrl, bio_zstd_new, bio_zstd_free, bio_zstd_callback_ctrl }; #endif const BIO_METHOD *BIO_f_zstd(void) { #ifndef OPENSSL_NO_ZSTD if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) return &bio_meth_zstd; #endif return NULL; } #ifndef OPENSSL_NO_ZSTD static int bio_zstd_new(BIO *bi) { BIO_ZSTD_CTX *ctx; # ifdef ZSTD_SHARED (void)COMP_zstd(); if (zstd_dso == NULL) { ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_NOT_SUPPORTED); return 0; } # endif ctx = OPENSSL_zalloc(sizeof(*ctx)); if (ctx == NULL) { ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); return 0; } # ifdef ZSTD_SHARED ctx->decompress.state = ZSTD_createDStream(); # else ctx->decompress.state = ZSTD_createDStream_advanced(zstd_mem_funcs); # endif if (ctx->decompress.state == NULL) goto err; ZSTD_initDStream(ctx->decompress.state); ctx->decompress.bufsize = ZSTD_DStreamInSize(); # ifdef ZSTD_SHARED ctx->compress.state = ZSTD_createCStream(); # else ctx->compress.state = ZSTD_createCStream_advanced(zstd_mem_funcs); # endif if (ctx->compress.state == NULL) goto err; ZSTD_initCStream(ctx->compress.state, ZSTD_CLEVEL_DEFAULT); ctx->compress.bufsize = ZSTD_CStreamInSize(); BIO_set_init(bi, 1); BIO_set_data(bi, ctx); return 1; err: ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); ZSTD_freeDStream(ctx->decompress.state); ZSTD_freeCStream(ctx->compress.state); OPENSSL_free(ctx); return 0; } static int bio_zstd_free(BIO *bi) { BIO_ZSTD_CTX *ctx; if (bi == NULL) return 0; ctx = BIO_get_data(bi); if (ctx != NULL) { ZSTD_freeDStream(ctx->decompress.state); OPENSSL_free(ctx->decompress.buffer); ZSTD_freeCStream(ctx->compress.state); OPENSSL_free(ctx->compress.outbuf.dst); OPENSSL_free(ctx); } BIO_set_data(bi, NULL); BIO_set_init(bi, 0); return 1; } static int bio_zstd_read(BIO *b, char *out, int outl) { BIO_ZSTD_CTX *ctx; size_t zret; int ret; ZSTD_outBuffer outBuf; BIO *next = BIO_next(b); if (out == NULL || outl <= 0) return 0; ctx = BIO_get_data(b); BIO_clear_retry_flags(b); if (ctx->decompress.buffer == NULL) { ctx->decompress.buffer = OPENSSL_malloc(ctx->decompress.bufsize); if (ctx->decompress.buffer == NULL) { ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); return 0; } ctx->decompress.inbuf.src = ctx->decompress.buffer; ctx->decompress.inbuf.size = 0; ctx->decompress.inbuf.pos = 0; } /* Copy output data directly to supplied buffer */ outBuf.dst = out; outBuf.size = (size_t)outl; outBuf.pos = 0; for (;;) { /* Decompress while data available */ do { zret = ZSTD_decompressStream(ctx->decompress.state, &outBuf, &ctx->decompress.inbuf); if (ZSTD_isError(zret)) { ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_DECOMPRESS_ERROR); ERR_add_error_data(1, ZSTD_getErrorName(zret)); return -1; } /* No more output space */ if (outBuf.pos == outBuf.size) return outBuf.pos; } while (ctx->decompress.inbuf.pos < ctx->decompress.inbuf.size); /* * No data in input buffer try to read some in, if an error then * return the total data read. */ ret = BIO_read(next, ctx->decompress.buffer, ctx->decompress.bufsize); if (ret <= 0) { BIO_copy_next_retry(b); if (ret < 0 && outBuf.pos == 0) return ret; return outBuf.pos; } ctx->decompress.inbuf.size = ret; ctx->decompress.inbuf.pos = 0; } } static int bio_zstd_write(BIO *b, const char *in, int inl) { BIO_ZSTD_CTX *ctx; size_t zret; ZSTD_inBuffer inBuf; int ret; int done = 0; BIO *next = BIO_next(b); if (in == NULL || inl <= 0) return 0; ctx = BIO_get_data(b); BIO_clear_retry_flags(b); if (ctx->compress.outbuf.dst == NULL) { ctx->compress.outbuf.dst = OPENSSL_malloc(ctx->compress.bufsize); if (ctx->compress.outbuf.dst == NULL) { ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); return 0; } ctx->compress.outbuf.size = ctx->compress.bufsize; ctx->compress.outbuf.pos = 0; ctx->compress.write_pos = 0; } /* Obtain input data directly from supplied buffer */ inBuf.src = in; inBuf.size = inl; inBuf.pos = 0; for (;;) { /* If data in output buffer write it first */ while (ctx->compress.write_pos < ctx->compress.outbuf.pos) { ret = BIO_write(next, (unsigned char*)ctx->compress.outbuf.dst + ctx->compress.write_pos, ctx->compress.outbuf.pos - ctx->compress.write_pos); if (ret <= 0) { BIO_copy_next_retry(b); if (ret < 0 && inBuf.pos == 0) return ret; return inBuf.pos; } ctx->compress.write_pos += ret; } /* Have we consumed all supplied data? */ if (done) return inBuf.pos; /* Reset buffer */ ctx->compress.outbuf.pos = 0; ctx->compress.outbuf.size = ctx->compress.bufsize; ctx->compress.write_pos = 0; /* Compress some more */ zret = ZSTD_compressStream2(ctx->compress.state, &ctx->compress.outbuf, &inBuf, ZSTD_e_end); if (ZSTD_isError(zret)) { ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_COMPRESS_ERROR); ERR_add_error_data(1, ZSTD_getErrorName(zret)); return 0; } else if (zret == 0) { done = 1; } } } static int bio_zstd_flush(BIO *b) { BIO_ZSTD_CTX *ctx; size_t zret; int ret; BIO *next = BIO_next(b); ctx = BIO_get_data(b); /* If no data written or already flush show success */ if (ctx->compress.outbuf.dst == NULL) return 1; BIO_clear_retry_flags(b); /* No more input data */ ctx->compress.outbuf.pos = 0; ctx->compress.outbuf.size = ctx->compress.bufsize; ctx->compress.write_pos = 0; for (;;) { /* If data in output buffer write it first */ while (ctx->compress.write_pos < ctx->compress.outbuf.pos) { ret = BIO_write(next, (unsigned char*)ctx->compress.outbuf.dst + ctx->compress.write_pos, ctx->compress.outbuf.pos - ctx->compress.write_pos); if (ret <= 0) { BIO_copy_next_retry(b); return ret; } ctx->compress.write_pos += ret; } /* Reset buffer */ ctx->compress.outbuf.pos = 0; ctx->compress.outbuf.size = ctx->compress.bufsize; ctx->compress.write_pos = 0; /* Compress some more */ zret = ZSTD_flushStream(ctx->compress.state, &ctx->compress.outbuf); if (ZSTD_isError(zret)) { ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_DECODE_ERROR); ERR_add_error_data(1, ZSTD_getErrorName(zret)); return 0; } if (zret == 0) return 1; } } static long bio_zstd_ctrl(BIO *b, int cmd, long num, void *ptr) { BIO_ZSTD_CTX *ctx; int ret = 0, *ip; size_t ibs, obs; unsigned char *tmp; BIO *next = BIO_next(b); if (next == NULL) return 0; ctx = BIO_get_data(b); switch (cmd) { case BIO_CTRL_RESET: ctx->compress.write_pos = 0; ctx->compress.bufsize = 0; ret = 1; break; case BIO_CTRL_FLUSH: ret = bio_zstd_flush(b); if (ret > 0) { ret = BIO_flush(next); BIO_copy_next_retry(b); } break; case BIO_C_SET_BUFF_SIZE: ibs = ctx->decompress.bufsize; obs = ctx->compress.bufsize; if (ptr != NULL) { ip = ptr; if (*ip == 0) ibs = (size_t)num; else obs = (size_t)num; } else { obs = ibs = (size_t)num; } if (ibs > 0 && ibs != ctx->decompress.bufsize) { if (ctx->decompress.buffer != NULL) { tmp = OPENSSL_realloc(ctx->decompress.buffer, ibs); if (tmp == NULL) return 0; if (ctx->decompress.inbuf.src == ctx->decompress.buffer) ctx->decompress.inbuf.src = tmp; ctx->decompress.buffer = tmp; } ctx->decompress.bufsize = ibs; } if (obs > 0 && obs != ctx->compress.bufsize) { if (ctx->compress.outbuf.dst != NULL) { tmp = OPENSSL_realloc(ctx->compress.outbuf.dst, obs); if (tmp == NULL) return 0; ctx->compress.outbuf.dst = tmp; } ctx->compress.bufsize = obs; } ret = 1; break; case BIO_C_DO_STATE_MACHINE: BIO_clear_retry_flags(b); ret = BIO_ctrl(next, cmd, num, ptr); BIO_copy_next_retry(b); break; case BIO_CTRL_WPENDING: if (ctx->compress.outbuf.pos < ctx->compress.outbuf.size) ret = 1; else ret = BIO_ctrl(next, cmd, num, ptr); break; case BIO_CTRL_PENDING: if (ctx->decompress.inbuf.pos < ctx->decompress.inbuf.size) ret = 1; else ret = BIO_ctrl(next, cmd, num, ptr); break; default: ret = BIO_ctrl(next, cmd, num, ptr); break; } return ret; } static long bio_zstd_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp) { BIO *next = BIO_next(b); if (next == NULL) return 0; return BIO_callback_ctrl(next, cmd, fp); } #endif