/src/openssl/crypto/comp/c_zstd.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 1998-2025 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | | * this file except in compliance with the License. You can obtain a copy |
6 | | * in the file LICENSE in the source distribution or at |
7 | | * https://www.openssl.org/source/license.html |
8 | | * |
9 | | * Uses zstd compression library from https://github.com/facebook/zstd |
10 | | * Requires version 1.4.x (latest as of this writing is 1.4.5) |
11 | | * Using custom free functions require static linking, so that is disabled when |
12 | | * using the shared library. |
13 | | */ |
14 | | |
15 | | #include <stdio.h> |
16 | | #include <stdlib.h> |
17 | | #include <string.h> |
18 | | #include <openssl/objects.h> |
19 | | #include "internal/comp.h" |
20 | | #include <openssl/err.h> |
21 | | #include "crypto/cryptlib.h" |
22 | | #include "internal/bio.h" |
23 | | #include "internal/thread_once.h" |
24 | | #include "comp_local.h" |
25 | | |
26 | | COMP_METHOD *COMP_zstd(void); |
27 | | |
28 | | #ifdef OPENSSL_NO_ZSTD |
29 | | #undef ZSTD_SHARED |
30 | | #else |
31 | | |
32 | | #ifndef ZSTD_SHARED |
33 | | #define ZSTD_STATIC_LINKING_ONLY |
34 | | #endif |
35 | | #include <zstd.h> |
36 | | |
37 | | /* Note: There is also a linux zstd.h file in the kernel source */ |
38 | | #ifndef ZSTD_H_235446 |
39 | | #error Wrong (i.e. linux) zstd.h included. |
40 | | #endif |
41 | | |
42 | | #if ZSTD_VERSION_MAJOR != 1 || ZSTD_VERSION_MINOR < 4 |
43 | | #error Expecting version 1.4 or greater of ZSTD 1.x |
44 | | #endif |
45 | | |
46 | | #ifndef ZSTD_SHARED |
47 | | /* memory allocations functions for zstd initialisation */ |
48 | | static void *zstd_alloc(void *opaque, size_t size) |
49 | | { |
50 | | return OPENSSL_zalloc(size); |
51 | | } |
52 | | |
53 | | static void zstd_free(void *opaque, void *address) |
54 | | { |
55 | | OPENSSL_free(address); |
56 | | } |
57 | | |
58 | | static ZSTD_customMem zstd_mem_funcs = { |
59 | | zstd_alloc, |
60 | | zstd_free, |
61 | | NULL |
62 | | }; |
63 | | #endif |
64 | | |
65 | | /* |
66 | | * When OpenSSL is built on Windows, we do not want to require that |
67 | | * the LIBZSTD.DLL be available in order for the OpenSSL DLLs to |
68 | | * work. Therefore, all ZSTD routines are loaded at run time |
69 | | * and we do not link to a .LIB file when ZSTD_SHARED is set. |
70 | | */ |
71 | | #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) |
72 | | #include <windows.h> |
73 | | #endif |
74 | | |
75 | | #ifdef ZSTD_SHARED |
76 | | #include "internal/dso.h" |
77 | | |
78 | | /* Function pointers */ |
79 | | typedef ZSTD_CStream *(*createCStream_ft)(void); |
80 | | typedef size_t (*initCStream_ft)(ZSTD_CStream *, int); |
81 | | typedef size_t (*freeCStream_ft)(ZSTD_CStream *); |
82 | | typedef size_t (*compressStream2_ft)(ZSTD_CCtx *, ZSTD_outBuffer *, ZSTD_inBuffer *, ZSTD_EndDirective); |
83 | | typedef size_t (*flushStream_ft)(ZSTD_CStream *, ZSTD_outBuffer *); |
84 | | typedef size_t (*endStream_ft)(ZSTD_CStream *, ZSTD_outBuffer *); |
85 | | typedef size_t (*compress_ft)(void *, size_t, const void *, size_t, int); |
86 | | typedef ZSTD_DStream *(*createDStream_ft)(void); |
87 | | typedef size_t (*initDStream_ft)(ZSTD_DStream *); |
88 | | typedef size_t (*freeDStream_ft)(ZSTD_DStream *); |
89 | | typedef size_t (*decompressStream_ft)(ZSTD_DStream *, ZSTD_outBuffer *, ZSTD_inBuffer *); |
90 | | typedef size_t (*decompress_ft)(void *, size_t, const void *, size_t); |
91 | | typedef unsigned (*isError_ft)(size_t); |
92 | | typedef const char *(*getErrorName_ft)(size_t); |
93 | | typedef size_t (*DStreamInSize_ft)(void); |
94 | | typedef size_t (*CStreamInSize_ft)(void); |
95 | | |
96 | | static createCStream_ft p_createCStream = NULL; |
97 | | static initCStream_ft p_initCStream = NULL; |
98 | | static freeCStream_ft p_freeCStream = NULL; |
99 | | static compressStream2_ft p_compressStream2 = NULL; |
100 | | static flushStream_ft p_flushStream = NULL; |
101 | | static endStream_ft p_endStream = NULL; |
102 | | static compress_ft p_compress = NULL; |
103 | | static createDStream_ft p_createDStream = NULL; |
104 | | static initDStream_ft p_initDStream = NULL; |
105 | | static freeDStream_ft p_freeDStream = NULL; |
106 | | static decompressStream_ft p_decompressStream = NULL; |
107 | | static decompress_ft p_decompress = NULL; |
108 | | static isError_ft p_isError = NULL; |
109 | | static getErrorName_ft p_getErrorName = NULL; |
110 | | static DStreamInSize_ft p_DStreamInSize = NULL; |
111 | | static CStreamInSize_ft p_CStreamInSize = NULL; |
112 | | |
113 | | static DSO *zstd_dso = NULL; |
114 | | |
115 | | #define ZSTD_createCStream p_createCStream |
116 | | #define ZSTD_initCStream p_initCStream |
117 | | #define ZSTD_freeCStream p_freeCStream |
118 | | #define ZSTD_compressStream2 p_compressStream2 |
119 | | #define ZSTD_flushStream p_flushStream |
120 | | #define ZSTD_endStream p_endStream |
121 | | #define ZSTD_compress p_compress |
122 | | #define ZSTD_createDStream p_createDStream |
123 | | #define ZSTD_initDStream p_initDStream |
124 | | #define ZSTD_freeDStream p_freeDStream |
125 | | #define ZSTD_decompressStream p_decompressStream |
126 | | #define ZSTD_decompress p_decompress |
127 | | #define ZSTD_isError p_isError |
128 | | #define ZSTD_getErrorName p_getErrorName |
129 | | #define ZSTD_DStreamInSize p_DStreamInSize |
130 | | #define ZSTD_CStreamInSize p_CStreamInSize |
131 | | |
132 | | #endif /* ifdef ZSTD_SHARED */ |
133 | | |
134 | | struct zstd_state { |
135 | | ZSTD_CStream *compressor; |
136 | | ZSTD_DStream *decompressor; |
137 | | }; |
138 | | |
139 | | static int zstd_stateful_init(COMP_CTX *ctx) |
140 | | { |
141 | | struct zstd_state *state = OPENSSL_zalloc(sizeof(*state)); |
142 | | |
143 | | if (state == NULL) |
144 | | return 0; |
145 | | |
146 | | #ifdef ZSTD_SHARED |
147 | | state->compressor = ZSTD_createCStream(); |
148 | | #else |
149 | | state->compressor = ZSTD_createCStream_advanced(zstd_mem_funcs); |
150 | | #endif |
151 | | if (state->compressor == NULL) |
152 | | goto err; |
153 | | ZSTD_initCStream(state->compressor, ZSTD_CLEVEL_DEFAULT); |
154 | | |
155 | | #ifdef ZSTD_SHARED |
156 | | state->decompressor = ZSTD_createDStream(); |
157 | | #else |
158 | | state->decompressor = ZSTD_createDStream_advanced(zstd_mem_funcs); |
159 | | #endif |
160 | | if (state->decompressor == NULL) |
161 | | goto err; |
162 | | ZSTD_initDStream(state->decompressor); |
163 | | |
164 | | ctx->data = state; |
165 | | return 1; |
166 | | err: |
167 | | ZSTD_freeCStream(state->compressor); |
168 | | ZSTD_freeDStream(state->decompressor); |
169 | | OPENSSL_free(state); |
170 | | return 0; |
171 | | } |
172 | | |
173 | | static void zstd_stateful_finish(COMP_CTX *ctx) |
174 | | { |
175 | | struct zstd_state *state = ctx->data; |
176 | | |
177 | | if (state != NULL) { |
178 | | ZSTD_freeCStream(state->compressor); |
179 | | ZSTD_freeDStream(state->decompressor); |
180 | | OPENSSL_free(state); |
181 | | ctx->data = NULL; |
182 | | } |
183 | | } |
184 | | |
185 | | static ossl_ssize_t zstd_stateful_compress_block(COMP_CTX *ctx, unsigned char *out, |
186 | | size_t olen, unsigned char *in, |
187 | | size_t ilen) |
188 | | { |
189 | | ZSTD_inBuffer inbuf; |
190 | | ZSTD_outBuffer outbuf; |
191 | | size_t ret; |
192 | | ossl_ssize_t fret; |
193 | | struct zstd_state *state = ctx->data; |
194 | | |
195 | | inbuf.src = in; |
196 | | inbuf.size = ilen; |
197 | | inbuf.pos = 0; |
198 | | outbuf.dst = out; |
199 | | outbuf.size = olen; |
200 | | outbuf.pos = 0; |
201 | | |
202 | | if (state == NULL) |
203 | | return -1; |
204 | | |
205 | | /* If input length is zero, end the stream/frame ? */ |
206 | | if (ilen == 0) { |
207 | | ret = ZSTD_endStream(state->compressor, &outbuf); |
208 | | if (ZSTD_isError(ret)) |
209 | | return -1; |
210 | | goto end; |
211 | | } |
212 | | |
213 | | /* |
214 | | * The finish API does not provide a final output buffer, |
215 | | * so each compress operation has to be ended, if all |
216 | | * the input data can't be accepted, or there is more output, |
217 | | * this has to be considered an error, since there is no more |
218 | | * output buffer space. |
219 | | */ |
220 | | do { |
221 | | ret = ZSTD_compressStream2(state->compressor, &outbuf, &inbuf, ZSTD_e_continue); |
222 | | if (ZSTD_isError(ret)) |
223 | | return -1; |
224 | | /* do I need to check for ret == 0 ? */ |
225 | | } while (inbuf.pos < inbuf.size); |
226 | | |
227 | | /* Did not consume all the data */ |
228 | | if (inbuf.pos < inbuf.size) |
229 | | return -1; |
230 | | |
231 | | ret = ZSTD_flushStream(state->compressor, &outbuf); |
232 | | if (ZSTD_isError(ret)) |
233 | | return -1; |
234 | | |
235 | | end: |
236 | | if (outbuf.pos > OSSL_SSIZE_MAX) |
237 | | return -1; |
238 | | fret = (ossl_ssize_t)outbuf.pos; |
239 | | if (fret < 0) |
240 | | return -1; |
241 | | return fret; |
242 | | } |
243 | | |
244 | | static ossl_ssize_t zstd_stateful_expand_block(COMP_CTX *ctx, unsigned char *out, |
245 | | size_t olen, unsigned char *in, |
246 | | size_t ilen) |
247 | | { |
248 | | ZSTD_inBuffer inbuf; |
249 | | ZSTD_outBuffer outbuf; |
250 | | size_t ret; |
251 | | ossl_ssize_t fret; |
252 | | struct zstd_state *state = ctx->data; |
253 | | |
254 | | inbuf.src = in; |
255 | | inbuf.size = ilen; |
256 | | inbuf.pos = 0; |
257 | | outbuf.dst = out; |
258 | | outbuf.size = olen; |
259 | | outbuf.pos = 0; |
260 | | |
261 | | if (state == NULL) |
262 | | return -1; |
263 | | |
264 | | if (ilen == 0) |
265 | | return 0; |
266 | | |
267 | | do { |
268 | | ret = ZSTD_decompressStream(state->decompressor, &outbuf, &inbuf); |
269 | | if (ZSTD_isError(ret)) |
270 | | return -1; |
271 | | /* If we completed a frame, and there's more data, try again */ |
272 | | } while (ret == 0 && inbuf.pos < inbuf.size); |
273 | | |
274 | | /* Did not consume all the data */ |
275 | | if (inbuf.pos < inbuf.size) |
276 | | return -1; |
277 | | |
278 | | if (outbuf.pos > OSSL_SSIZE_MAX) |
279 | | return -1; |
280 | | fret = (ossl_ssize_t)outbuf.pos; |
281 | | if (fret < 0) |
282 | | return -1; |
283 | | return fret; |
284 | | } |
285 | | |
286 | | static COMP_METHOD zstd_stateful_method = { |
287 | | NID_zstd, |
288 | | LN_zstd, |
289 | | zstd_stateful_init, |
290 | | zstd_stateful_finish, |
291 | | zstd_stateful_compress_block, |
292 | | zstd_stateful_expand_block |
293 | | }; |
294 | | |
295 | | static int zstd_oneshot_init(COMP_CTX *ctx) |
296 | | { |
297 | | return 1; |
298 | | } |
299 | | |
300 | | static void zstd_oneshot_finish(COMP_CTX *ctx) |
301 | | { |
302 | | } |
303 | | |
304 | | static ossl_ssize_t zstd_oneshot_compress_block(COMP_CTX *ctx, unsigned char *out, |
305 | | size_t olen, unsigned char *in, |
306 | | size_t ilen) |
307 | | { |
308 | | size_t out_size; |
309 | | ossl_ssize_t ret; |
310 | | |
311 | | if (ilen == 0) |
312 | | return 0; |
313 | | |
314 | | /* Note: uses STDLIB memory allocators */ |
315 | | out_size = ZSTD_compress(out, olen, in, ilen, ZSTD_CLEVEL_DEFAULT); |
316 | | if (ZSTD_isError(out_size)) |
317 | | return -1; |
318 | | |
319 | | if (out_size > OSSL_SSIZE_MAX) |
320 | | return -1; |
321 | | ret = (ossl_ssize_t)out_size; |
322 | | if (ret < 0) |
323 | | return -1; |
324 | | return ret; |
325 | | } |
326 | | |
327 | | static ossl_ssize_t zstd_oneshot_expand_block(COMP_CTX *ctx, unsigned char *out, |
328 | | size_t olen, unsigned char *in, |
329 | | size_t ilen) |
330 | | { |
331 | | size_t out_size; |
332 | | ossl_ssize_t ret; |
333 | | |
334 | | if (ilen == 0) |
335 | | return 0; |
336 | | |
337 | | /* Note: uses STDLIB memory allocators */ |
338 | | out_size = ZSTD_decompress(out, olen, in, ilen); |
339 | | if (ZSTD_isError(out_size)) |
340 | | return -1; |
341 | | |
342 | | if (out_size > OSSL_SSIZE_MAX) |
343 | | return -1; |
344 | | ret = (ossl_ssize_t)out_size; |
345 | | if (ret < 0) |
346 | | return -1; |
347 | | return ret; |
348 | | } |
349 | | |
350 | | static COMP_METHOD zstd_oneshot_method = { |
351 | | NID_zstd, |
352 | | LN_zstd, |
353 | | zstd_oneshot_init, |
354 | | zstd_oneshot_finish, |
355 | | zstd_oneshot_compress_block, |
356 | | zstd_oneshot_expand_block |
357 | | }; |
358 | | |
359 | | static CRYPTO_ONCE zstd_once = CRYPTO_ONCE_STATIC_INIT; |
360 | | DEFINE_RUN_ONCE_STATIC(ossl_comp_zstd_init) |
361 | | { |
362 | | #ifdef ZSTD_SHARED |
363 | | #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) |
364 | | #define LIBZSTD "LIBZSTD" |
365 | | #else |
366 | | #define LIBZSTD "zstd" |
367 | | #endif |
368 | | |
369 | | zstd_dso = DSO_load(NULL, LIBZSTD, NULL, 0); |
370 | | if (zstd_dso != NULL) { |
371 | | p_createCStream = (createCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_createCStream"); |
372 | | p_initCStream = (initCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_initCStream"); |
373 | | p_freeCStream = (freeCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_freeCStream"); |
374 | | p_compressStream2 = (compressStream2_ft)DSO_bind_func(zstd_dso, "ZSTD_compressStream2"); |
375 | | p_flushStream = (flushStream_ft)DSO_bind_func(zstd_dso, "ZSTD_flushStream"); |
376 | | p_endStream = (endStream_ft)DSO_bind_func(zstd_dso, "ZSTD_endStream"); |
377 | | p_compress = (compress_ft)DSO_bind_func(zstd_dso, "ZSTD_compress"); |
378 | | p_createDStream = (createDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_createDStream"); |
379 | | p_initDStream = (initDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_initDStream"); |
380 | | p_freeDStream = (freeDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_freeDStream"); |
381 | | p_decompressStream = (decompressStream_ft)DSO_bind_func(zstd_dso, "ZSTD_decompressStream"); |
382 | | p_decompress = (decompress_ft)DSO_bind_func(zstd_dso, "ZSTD_decompress"); |
383 | | p_isError = (isError_ft)DSO_bind_func(zstd_dso, "ZSTD_isError"); |
384 | | p_getErrorName = (getErrorName_ft)DSO_bind_func(zstd_dso, "ZSTD_getErrorName"); |
385 | | p_DStreamInSize = (DStreamInSize_ft)DSO_bind_func(zstd_dso, "ZSTD_DStreamInSize"); |
386 | | p_CStreamInSize = (CStreamInSize_ft)DSO_bind_func(zstd_dso, "ZSTD_CStreamInSize"); |
387 | | } |
388 | | |
389 | | if (p_createCStream == NULL || p_initCStream == NULL || p_freeCStream == NULL |
390 | | || p_compressStream2 == NULL || p_flushStream == NULL || p_endStream == NULL |
391 | | || p_compress == NULL || p_createDStream == NULL || p_initDStream == NULL |
392 | | || p_freeDStream == NULL || p_decompressStream == NULL || p_decompress == NULL |
393 | | || p_isError == NULL || p_getErrorName == NULL || p_DStreamInSize == NULL |
394 | | || p_CStreamInSize == NULL) { |
395 | | ossl_comp_zstd_cleanup(); |
396 | | return 0; |
397 | | } |
398 | | #endif |
399 | | return 1; |
400 | | } |
401 | | #endif /* ifndef ZSTD / else */ |
402 | | |
403 | | COMP_METHOD *COMP_zstd(void) |
404 | 0 | { |
405 | 0 | COMP_METHOD *meth = NULL; |
406 | |
|
407 | | #ifndef OPENSSL_NO_ZSTD |
408 | | if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) |
409 | | meth = &zstd_stateful_method; |
410 | | #endif |
411 | 0 | return meth; |
412 | 0 | } |
413 | | |
414 | | COMP_METHOD *COMP_zstd_oneshot(void) |
415 | 0 | { |
416 | 0 | COMP_METHOD *meth = NULL; |
417 | |
|
418 | | #ifndef OPENSSL_NO_ZSTD |
419 | | if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) |
420 | | meth = &zstd_oneshot_method; |
421 | | #endif |
422 | 0 | return meth; |
423 | 0 | } |
424 | | |
425 | | /* Also called from OPENSSL_cleanup() */ |
426 | | void ossl_comp_zstd_cleanup(void) |
427 | 3 | { |
428 | | #ifdef ZSTD_SHARED |
429 | | DSO_free(zstd_dso); |
430 | | zstd_dso = NULL; |
431 | | p_createCStream = NULL; |
432 | | p_initCStream = NULL; |
433 | | p_freeCStream = NULL; |
434 | | p_compressStream2 = NULL; |
435 | | p_flushStream = NULL; |
436 | | p_endStream = NULL; |
437 | | p_compress = NULL; |
438 | | p_createDStream = NULL; |
439 | | p_initDStream = NULL; |
440 | | p_freeDStream = NULL; |
441 | | p_decompressStream = NULL; |
442 | | p_decompress = NULL; |
443 | | p_isError = NULL; |
444 | | p_getErrorName = NULL; |
445 | | p_DStreamInSize = NULL; |
446 | | p_CStreamInSize = NULL; |
447 | | #endif |
448 | 3 | } |
449 | | |
450 | | #ifndef OPENSSL_NO_ZSTD |
451 | | |
452 | | /* Zstd-based compression/decompression filter BIO */ |
453 | | |
454 | | typedef struct { |
455 | | struct { /* input structure */ |
456 | | ZSTD_DStream *state; |
457 | | ZSTD_inBuffer inbuf; /* has const src */ |
458 | | size_t bufsize; |
459 | | void *buffer; |
460 | | } decompress; |
461 | | struct { /* output structure */ |
462 | | ZSTD_CStream *state; |
463 | | ZSTD_outBuffer outbuf; |
464 | | size_t bufsize; |
465 | | size_t write_pos; |
466 | | } compress; |
467 | | } BIO_ZSTD_CTX; |
468 | | |
469 | | #define ZSTD_DEFAULT_BUFSIZE 1024 |
470 | | |
471 | | static int bio_zstd_new(BIO *bi); |
472 | | static int bio_zstd_free(BIO *bi); |
473 | | static int bio_zstd_read(BIO *b, char *out, int outl); |
474 | | static int bio_zstd_write(BIO *b, const char *in, int inl); |
475 | | static long bio_zstd_ctrl(BIO *b, int cmd, long num, void *ptr); |
476 | | static long bio_zstd_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp); |
477 | | |
478 | | static const BIO_METHOD bio_meth_zstd = { |
479 | | BIO_TYPE_COMP, |
480 | | "zstd", |
481 | | /* TODO: Convert to new style write function */ |
482 | | bwrite_conv, |
483 | | bio_zstd_write, |
484 | | /* TODO: Convert to new style read function */ |
485 | | bread_conv, |
486 | | bio_zstd_read, |
487 | | NULL, /* bio_zstd_puts, */ |
488 | | NULL, /* bio_zstd_gets, */ |
489 | | bio_zstd_ctrl, |
490 | | bio_zstd_new, |
491 | | bio_zstd_free, |
492 | | bio_zstd_callback_ctrl |
493 | | }; |
494 | | #endif |
495 | | |
496 | | const BIO_METHOD *BIO_f_zstd(void) |
497 | 0 | { |
498 | | #ifndef OPENSSL_NO_ZSTD |
499 | | if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) |
500 | | return &bio_meth_zstd; |
501 | | #endif |
502 | | return NULL; |
503 | 0 | } |
504 | | |
505 | | #ifndef OPENSSL_NO_ZSTD |
506 | | static int bio_zstd_new(BIO *bi) |
507 | | { |
508 | | BIO_ZSTD_CTX *ctx; |
509 | | |
510 | | #ifdef ZSTD_SHARED |
511 | | (void)COMP_zstd(); |
512 | | if (zstd_dso == NULL) { |
513 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_NOT_SUPPORTED); |
514 | | return 0; |
515 | | } |
516 | | #endif |
517 | | ctx = OPENSSL_zalloc(sizeof(*ctx)); |
518 | | if (ctx == NULL) { |
519 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
520 | | return 0; |
521 | | } |
522 | | |
523 | | #ifdef ZSTD_SHARED |
524 | | ctx->decompress.state = ZSTD_createDStream(); |
525 | | #else |
526 | | ctx->decompress.state = ZSTD_createDStream_advanced(zstd_mem_funcs); |
527 | | #endif |
528 | | if (ctx->decompress.state == NULL) |
529 | | goto err; |
530 | | ZSTD_initDStream(ctx->decompress.state); |
531 | | ctx->decompress.bufsize = ZSTD_DStreamInSize(); |
532 | | |
533 | | #ifdef ZSTD_SHARED |
534 | | ctx->compress.state = ZSTD_createCStream(); |
535 | | #else |
536 | | ctx->compress.state = ZSTD_createCStream_advanced(zstd_mem_funcs); |
537 | | #endif |
538 | | if (ctx->compress.state == NULL) |
539 | | goto err; |
540 | | ZSTD_initCStream(ctx->compress.state, ZSTD_CLEVEL_DEFAULT); |
541 | | ctx->compress.bufsize = ZSTD_CStreamInSize(); |
542 | | |
543 | | BIO_set_init(bi, 1); |
544 | | BIO_set_data(bi, ctx); |
545 | | |
546 | | return 1; |
547 | | err: |
548 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
549 | | ZSTD_freeDStream(ctx->decompress.state); |
550 | | ZSTD_freeCStream(ctx->compress.state); |
551 | | OPENSSL_free(ctx); |
552 | | return 0; |
553 | | } |
554 | | |
555 | | static int bio_zstd_free(BIO *bi) |
556 | | { |
557 | | BIO_ZSTD_CTX *ctx; |
558 | | |
559 | | if (bi == NULL) |
560 | | return 0; |
561 | | |
562 | | ctx = BIO_get_data(bi); |
563 | | if (ctx != NULL) { |
564 | | ZSTD_freeDStream(ctx->decompress.state); |
565 | | OPENSSL_free(ctx->decompress.buffer); |
566 | | ZSTD_freeCStream(ctx->compress.state); |
567 | | OPENSSL_free(ctx->compress.outbuf.dst); |
568 | | OPENSSL_free(ctx); |
569 | | } |
570 | | BIO_set_data(bi, NULL); |
571 | | BIO_set_init(bi, 0); |
572 | | |
573 | | return 1; |
574 | | } |
575 | | |
576 | | static int bio_zstd_read(BIO *b, char *out, int outl) |
577 | | { |
578 | | BIO_ZSTD_CTX *ctx; |
579 | | size_t zret; |
580 | | int ret; |
581 | | ZSTD_outBuffer outBuf; |
582 | | BIO *next = BIO_next(b); |
583 | | |
584 | | if (out == NULL) { |
585 | | ERR_raise(ERR_LIB_COMP, ERR_R_PASSED_NULL_PARAMETER); |
586 | | return -1; |
587 | | } |
588 | | if (outl <= 0) |
589 | | return 0; |
590 | | |
591 | | ctx = BIO_get_data(b); |
592 | | BIO_clear_retry_flags(b); |
593 | | if (ctx->decompress.buffer == NULL) { |
594 | | ctx->decompress.buffer = OPENSSL_malloc(ctx->decompress.bufsize); |
595 | | if (ctx->decompress.buffer == NULL) { |
596 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
597 | | return -1; |
598 | | } |
599 | | ctx->decompress.inbuf.src = ctx->decompress.buffer; |
600 | | ctx->decompress.inbuf.size = 0; |
601 | | ctx->decompress.inbuf.pos = 0; |
602 | | } |
603 | | |
604 | | /* Copy output data directly to supplied buffer */ |
605 | | outBuf.dst = out; |
606 | | outBuf.size = (size_t)outl; |
607 | | outBuf.pos = 0; |
608 | | for (;;) { |
609 | | /* Decompress while data available */ |
610 | | do { |
611 | | zret = ZSTD_decompressStream(ctx->decompress.state, &outBuf, &ctx->decompress.inbuf); |
612 | | if (ZSTD_isError(zret)) { |
613 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_DECOMPRESS_ERROR); |
614 | | ERR_add_error_data(1, ZSTD_getErrorName(zret)); |
615 | | return -1; |
616 | | } |
617 | | /* No more output space */ |
618 | | if (outBuf.pos == outBuf.size) |
619 | | return (int)outBuf.pos; |
620 | | } while (ctx->decompress.inbuf.pos < ctx->decompress.inbuf.size); |
621 | | |
622 | | /* |
623 | | * No data in input buffer try to read some in, if an error then |
624 | | * return the total data read. |
625 | | */ |
626 | | ret = BIO_read(next, ctx->decompress.buffer, (int)ctx->decompress.bufsize); |
627 | | if (ret <= 0) { |
628 | | BIO_copy_next_retry(b); |
629 | | if (ret < 0 && outBuf.pos == 0) |
630 | | return ret; |
631 | | return (int)outBuf.pos; |
632 | | } |
633 | | ctx->decompress.inbuf.size = ret; |
634 | | ctx->decompress.inbuf.pos = 0; |
635 | | } |
636 | | } |
637 | | |
638 | | static int bio_zstd_write(BIO *b, const char *in, int inl) |
639 | | { |
640 | | BIO_ZSTD_CTX *ctx; |
641 | | size_t zret; |
642 | | ZSTD_inBuffer inBuf; |
643 | | int ret; |
644 | | int done = 0; |
645 | | BIO *next = BIO_next(b); |
646 | | |
647 | | if (in == NULL || inl <= 0) |
648 | | return 0; |
649 | | |
650 | | ctx = BIO_get_data(b); |
651 | | |
652 | | BIO_clear_retry_flags(b); |
653 | | if (ctx->compress.outbuf.dst == NULL) { |
654 | | ctx->compress.outbuf.dst = OPENSSL_malloc(ctx->compress.bufsize); |
655 | | if (ctx->compress.outbuf.dst == NULL) { |
656 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
657 | | return 0; |
658 | | } |
659 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
660 | | ctx->compress.outbuf.pos = 0; |
661 | | ctx->compress.write_pos = 0; |
662 | | } |
663 | | /* Obtain input data directly from supplied buffer */ |
664 | | inBuf.src = in; |
665 | | inBuf.size = inl; |
666 | | inBuf.pos = 0; |
667 | | for (;;) { |
668 | | /* If data in output buffer write it first */ |
669 | | while (ctx->compress.write_pos < ctx->compress.outbuf.pos) { |
670 | | ret = BIO_write(next, (unsigned char *)ctx->compress.outbuf.dst + ctx->compress.write_pos, |
671 | | (int)(ctx->compress.outbuf.pos - ctx->compress.write_pos)); |
672 | | if (ret <= 0) { |
673 | | BIO_copy_next_retry(b); |
674 | | if (ret < 0 && inBuf.pos == 0) |
675 | | return ret; |
676 | | return (int)inBuf.pos; |
677 | | } |
678 | | ctx->compress.write_pos += ret; |
679 | | } |
680 | | |
681 | | /* Have we consumed all supplied data? */ |
682 | | if (done) |
683 | | return (int)inBuf.pos; |
684 | | |
685 | | /* Reset buffer */ |
686 | | ctx->compress.outbuf.pos = 0; |
687 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
688 | | ctx->compress.write_pos = 0; |
689 | | /* Compress some more */ |
690 | | zret = ZSTD_compressStream2(ctx->compress.state, &ctx->compress.outbuf, &inBuf, ZSTD_e_end); |
691 | | if (ZSTD_isError(zret)) { |
692 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_COMPRESS_ERROR); |
693 | | ERR_add_error_data(1, ZSTD_getErrorName(zret)); |
694 | | return 0; |
695 | | } else if (zret == 0) { |
696 | | done = 1; |
697 | | } |
698 | | } |
699 | | } |
700 | | |
701 | | static int bio_zstd_flush(BIO *b) |
702 | | { |
703 | | BIO_ZSTD_CTX *ctx; |
704 | | size_t zret; |
705 | | int ret; |
706 | | BIO *next = BIO_next(b); |
707 | | |
708 | | ctx = BIO_get_data(b); |
709 | | |
710 | | /* If no data written or already flush show success */ |
711 | | if (ctx->compress.outbuf.dst == NULL) |
712 | | return 1; |
713 | | |
714 | | BIO_clear_retry_flags(b); |
715 | | /* No more input data */ |
716 | | ctx->compress.outbuf.pos = 0; |
717 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
718 | | ctx->compress.write_pos = 0; |
719 | | for (;;) { |
720 | | /* If data in output buffer write it first */ |
721 | | while (ctx->compress.write_pos < ctx->compress.outbuf.pos) { |
722 | | ret = BIO_write(next, (unsigned char *)ctx->compress.outbuf.dst + ctx->compress.write_pos, |
723 | | (int)(ctx->compress.outbuf.pos - ctx->compress.write_pos)); |
724 | | if (ret <= 0) { |
725 | | BIO_copy_next_retry(b); |
726 | | return ret; |
727 | | } |
728 | | ctx->compress.write_pos += ret; |
729 | | } |
730 | | |
731 | | /* Reset buffer */ |
732 | | ctx->compress.outbuf.pos = 0; |
733 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
734 | | ctx->compress.write_pos = 0; |
735 | | /* Compress some more */ |
736 | | zret = ZSTD_flushStream(ctx->compress.state, &ctx->compress.outbuf); |
737 | | if (ZSTD_isError(zret)) { |
738 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_COMPRESS_ERROR); |
739 | | ERR_add_error_data(1, ZSTD_getErrorName(zret)); |
740 | | return 0; |
741 | | } |
742 | | if (zret == 0) |
743 | | return 1; |
744 | | } |
745 | | } |
746 | | |
747 | | static long bio_zstd_ctrl(BIO *b, int cmd, long num, void *ptr) |
748 | | { |
749 | | BIO_ZSTD_CTX *ctx; |
750 | | int ret = 0, *ip; |
751 | | size_t ibs, obs; |
752 | | unsigned char *tmp; |
753 | | BIO *next = BIO_next(b); |
754 | | |
755 | | if (next == NULL) |
756 | | return 0; |
757 | | ctx = BIO_get_data(b); |
758 | | switch (cmd) { |
759 | | |
760 | | case BIO_CTRL_RESET: |
761 | | /* reset decompressor */ |
762 | | ctx->decompress.inbuf.size = 0; |
763 | | ctx->decompress.inbuf.pos = 0; |
764 | | if (ctx->decompress.state != NULL) |
765 | | ZSTD_initDStream(ctx->decompress.state); |
766 | | |
767 | | /* reset compressor */ |
768 | | ctx->compress.write_pos = 0; |
769 | | ctx->compress.outbuf.pos = 0; |
770 | | if (ctx->compress.state != NULL) |
771 | | ZSTD_initCStream(ctx->compress.state, ZSTD_CLEVEL_DEFAULT); |
772 | | |
773 | | /* keep existing bufsize, do not set it to 0 */ |
774 | | ret = 1; |
775 | | break; |
776 | | |
777 | | case BIO_CTRL_FLUSH: |
778 | | ret = bio_zstd_flush(b); |
779 | | if (ret > 0) { |
780 | | ret = BIO_flush(next); |
781 | | BIO_copy_next_retry(b); |
782 | | } |
783 | | break; |
784 | | |
785 | | case BIO_C_SET_BUFF_SIZE: |
786 | | ibs = ctx->decompress.bufsize; |
787 | | obs = ctx->compress.bufsize; |
788 | | if (ptr != NULL) { |
789 | | ip = ptr; |
790 | | if (*ip == 0) |
791 | | ibs = (size_t)num; |
792 | | else |
793 | | obs = (size_t)num; |
794 | | } else { |
795 | | obs = ibs = (size_t)num; |
796 | | } |
797 | | |
798 | | if (ibs > 0 && ibs != ctx->decompress.bufsize) { |
799 | | if (ctx->decompress.buffer != NULL) { |
800 | | tmp = OPENSSL_realloc(ctx->decompress.buffer, ibs); |
801 | | if (tmp == NULL) |
802 | | return 0; |
803 | | if (ctx->decompress.inbuf.src == ctx->decompress.buffer) |
804 | | ctx->decompress.inbuf.src = tmp; |
805 | | ctx->decompress.buffer = tmp; |
806 | | } |
807 | | ctx->decompress.bufsize = ibs; |
808 | | } |
809 | | |
810 | | if (obs > 0 && obs != ctx->compress.bufsize) { |
811 | | if (ctx->compress.outbuf.dst != NULL) { |
812 | | tmp = OPENSSL_realloc(ctx->compress.outbuf.dst, obs); |
813 | | if (tmp == NULL) |
814 | | return 0; |
815 | | ctx->compress.outbuf.dst = tmp; |
816 | | } |
817 | | ctx->compress.bufsize = obs; |
818 | | } |
819 | | ret = 1; |
820 | | break; |
821 | | |
822 | | case BIO_C_DO_STATE_MACHINE: |
823 | | BIO_clear_retry_flags(b); |
824 | | ret = BIO_ctrl(next, cmd, num, ptr); |
825 | | BIO_copy_next_retry(b); |
826 | | break; |
827 | | |
828 | | case BIO_CTRL_WPENDING: |
829 | | if (ctx->compress.outbuf.pos < ctx->compress.outbuf.size) |
830 | | ret = 1; |
831 | | else |
832 | | ret = BIO_ctrl(next, cmd, num, ptr); |
833 | | break; |
834 | | |
835 | | case BIO_CTRL_PENDING: |
836 | | if (ctx->decompress.inbuf.pos < ctx->decompress.inbuf.size) |
837 | | ret = 1; |
838 | | else |
839 | | ret = BIO_ctrl(next, cmd, num, ptr); |
840 | | break; |
841 | | |
842 | | default: |
843 | | ret = BIO_ctrl(next, cmd, num, ptr); |
844 | | break; |
845 | | } |
846 | | |
847 | | return ret; |
848 | | } |
849 | | |
850 | | static long bio_zstd_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp) |
851 | | { |
852 | | BIO *next = BIO_next(b); |
853 | | if (next == NULL) |
854 | | return 0; |
855 | | return BIO_callback_ctrl(next, cmd, fp); |
856 | | } |
857 | | |
858 | | #endif |