1324145Smm/*- 2324145Smm * Copyright (c) 2017 Sean Purcell 3324145Smm * All rights reserved. 4324145Smm * 5324145Smm * Redistribution and use in source and binary forms, with or without 6324145Smm * modification, are permitted provided that the following conditions 7324145Smm * are met: 8324145Smm * 1. Redistributions of source code must retain the above copyright 9324145Smm * notice, this list of conditions and the following disclaimer. 10324145Smm * 2. Redistributions in binary form must reproduce the above copyright 11324145Smm * notice, this list of conditions and the following disclaimer in the 12324145Smm * documentation and/or other materials provided with the distribution. 13324145Smm * 14324145Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15324145Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16324145Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17324145Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18324145Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19324145Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20324145Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21324145Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22324145Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23324145Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24324145Smm */ 25324145Smm 26324145Smm#include "archive_platform.h" 27324145Smm 28324145Smm__FBSDID("$FreeBSD: stable/10/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c 362134 2020-06-12 23:02:34Z mm $"); 29324145Smm 30324145Smm 31324145Smm#ifdef HAVE_ERRNO_H 32324145Smm#include <errno.h> 33324145Smm#endif 34324145Smm#ifdef HAVE_STDLIB_H 35324145Smm#include <stdlib.h> 36324145Smm#endif 37324145Smm#ifdef HAVE_STRING_H 38324145Smm#include <string.h> 39324145Smm#endif 40324145Smm#ifdef HAVE_ZSTD_H 41324145Smm#include <zstd.h> 42324145Smm#endif 43324145Smm 44324145Smm#include "archive.h" 45324145Smm#include "archive_private.h" 46324145Smm#include "archive_string.h" 47324145Smm#include "archive_write_private.h" 48324145Smm 49324145Smm/* Don't compile this if we don't have zstd.h */ 50324145Smm 51324145Smmstruct private_data { 52324145Smm int compression_level; 53324145Smm#if HAVE_ZSTD_H && HAVE_LIBZSTD 54324145Smm ZSTD_CStream *cstream; 55324145Smm int64_t total_in; 56324145Smm ZSTD_outBuffer out; 57324145Smm#else 58324145Smm struct archive_write_program_data *pdata; 59324145Smm#endif 60324145Smm}; 61324145Smm 62362134Smm/* If we don't have the library use default range values (zstdcli.c v1.4.0) */ 63362134Smm#define CLEVEL_MIN -99 64362134Smm#define CLEVEL_STD_MIN 0 /* prior to 1.3.4 and more recent without using --fast */ 65362134Smm#define CLEVEL_DEFAULT 3 66362134Smm#define CLEVEL_STD_MAX 19 /* without using --ultra */ 67362134Smm#define CLEVEL_MAX 22 68362134Smm 69362134Smm#define MINVER_NEGCLEVEL 10304 70362134Smm#define MINVER_MINCLEVEL 10306 71362134Smm 72324145Smmstatic int archive_compressor_zstd_options(struct archive_write_filter *, 73324145Smm const char *, const char *); 74324145Smmstatic int archive_compressor_zstd_open(struct archive_write_filter *); 75324145Smmstatic int archive_compressor_zstd_write(struct archive_write_filter *, 76324145Smm const void *, size_t); 77324145Smmstatic int archive_compressor_zstd_close(struct archive_write_filter *); 78324145Smmstatic int archive_compressor_zstd_free(struct archive_write_filter *); 79324145Smm#if HAVE_ZSTD_H && HAVE_LIBZSTD 80324145Smmstatic int drive_compressor(struct archive_write_filter *, 81324145Smm struct private_data *, int, const void *, size_t); 82324145Smm#endif 83324145Smm 84324145Smm 85324145Smm/* 86324145Smm * Add a zstd compression filter to this write handle. 87324145Smm */ 88324145Smmint 89324145Smmarchive_write_add_filter_zstd(struct archive *_a) 90324145Smm{ 91324145Smm struct archive_write *a = (struct archive_write *)_a; 92324145Smm struct archive_write_filter *f = __archive_write_allocate_filter(_a); 93324145Smm struct private_data *data; 94324145Smm archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 95324145Smm ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd"); 96324145Smm 97324145Smm data = calloc(1, sizeof(*data)); 98324145Smm if (data == NULL) { 99324145Smm archive_set_error(&a->archive, ENOMEM, "Out of memory"); 100324145Smm return (ARCHIVE_FATAL); 101324145Smm } 102324145Smm f->data = data; 103324145Smm f->open = &archive_compressor_zstd_open; 104324145Smm f->options = &archive_compressor_zstd_options; 105324145Smm f->close = &archive_compressor_zstd_close; 106324145Smm f->free = &archive_compressor_zstd_free; 107324145Smm f->code = ARCHIVE_FILTER_ZSTD; 108324145Smm f->name = "zstd"; 109362134Smm data->compression_level = CLEVEL_DEFAULT; 110324145Smm#if HAVE_ZSTD_H && HAVE_LIBZSTD 111324145Smm data->cstream = ZSTD_createCStream(); 112324145Smm if (data->cstream == NULL) { 113324145Smm free(data); 114324145Smm archive_set_error(&a->archive, ENOMEM, 115324145Smm "Failed to allocate zstd compressor object"); 116324145Smm return (ARCHIVE_FATAL); 117324145Smm } 118324145Smm 119324145Smm return (ARCHIVE_OK); 120324145Smm#else 121324145Smm data->pdata = __archive_write_program_allocate("zstd"); 122324145Smm if (data->pdata == NULL) { 123324145Smm free(data); 124324145Smm archive_set_error(&a->archive, ENOMEM, "Out of memory"); 125324145Smm return (ARCHIVE_FATAL); 126324145Smm } 127324145Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 128324145Smm "Using external zstd program"); 129324145Smm return (ARCHIVE_WARN); 130324145Smm#endif 131324145Smm} 132324145Smm 133324145Smmstatic int 134324145Smmarchive_compressor_zstd_free(struct archive_write_filter *f) 135324145Smm{ 136324145Smm struct private_data *data = (struct private_data *)f->data; 137324145Smm#if HAVE_ZSTD_H && HAVE_LIBZSTD 138324145Smm ZSTD_freeCStream(data->cstream); 139324145Smm free(data->out.dst); 140324145Smm#else 141324145Smm __archive_write_program_free(data->pdata); 142324145Smm#endif 143324145Smm free(data); 144324145Smm f->data = NULL; 145324145Smm return (ARCHIVE_OK); 146324145Smm} 147324145Smm 148362134Smmstatic int string_is_numeric (const char* value) 149362134Smm{ 150362134Smm size_t len = strlen(value); 151362134Smm size_t i; 152362134Smm 153362134Smm if (len == 0) { 154362134Smm return (ARCHIVE_WARN); 155362134Smm } 156362134Smm else if (len == 1 && !(value[0] >= '0' && value[0] <= '9')) { 157362134Smm return (ARCHIVE_WARN); 158362134Smm } 159362134Smm else if (!(value[0] >= '0' && value[0] <= '9') && 160362134Smm value[0] != '-' && value[0] != '+') { 161362134Smm return (ARCHIVE_WARN); 162362134Smm } 163362134Smm 164362134Smm for (i = 1; i < len; i++) { 165362134Smm if (!(value[i] >= '0' && value[i] <= '9')) { 166362134Smm return (ARCHIVE_WARN); 167362134Smm } 168362134Smm } 169362134Smm 170362134Smm return (ARCHIVE_OK); 171362134Smm} 172362134Smm 173324145Smm/* 174324145Smm * Set write options. 175324145Smm */ 176324145Smmstatic int 177324145Smmarchive_compressor_zstd_options(struct archive_write_filter *f, const char *key, 178324145Smm const char *value) 179324145Smm{ 180324145Smm struct private_data *data = (struct private_data *)f->data; 181324145Smm 182324145Smm if (strcmp(key, "compression-level") == 0) { 183324145Smm int level = atoi(value); 184362134Smm /* If we don't have the library, hard-code the max level */ 185362134Smm int minimum = CLEVEL_MIN; 186362134Smm int maximum = CLEVEL_MAX; 187362134Smm if (string_is_numeric(value) != ARCHIVE_OK) { 188362134Smm return (ARCHIVE_WARN); 189362134Smm } 190324145Smm#if HAVE_ZSTD_H && HAVE_LIBZSTD 191362134Smm maximum = ZSTD_maxCLevel(); 192362134Smm#if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL 193362134Smm if (ZSTD_versionNumber() >= MINVER_MINCLEVEL) { 194362134Smm minimum = ZSTD_minCLevel(); 195362134Smm } 196362134Smm else 197324145Smm#endif 198362134Smm if (ZSTD_versionNumber() < MINVER_NEGCLEVEL) { 199362134Smm minimum = CLEVEL_STD_MIN; 200362134Smm } 201362134Smm#endif 202362134Smm if (level < minimum || level > maximum) { 203324145Smm return (ARCHIVE_WARN); 204324145Smm } 205324145Smm data->compression_level = level; 206324145Smm return (ARCHIVE_OK); 207324145Smm } 208324145Smm 209324145Smm /* Note: The "warn" return is just to inform the options 210324145Smm * supervisor that we didn't handle it. It will generate 211324145Smm * a suitable error if no one used this option. */ 212324145Smm return (ARCHIVE_WARN); 213324145Smm} 214324145Smm 215324145Smm#if HAVE_ZSTD_H && HAVE_LIBZSTD 216324145Smm/* 217324145Smm * Setup callback. 218324145Smm */ 219324145Smmstatic int 220324145Smmarchive_compressor_zstd_open(struct archive_write_filter *f) 221324145Smm{ 222324145Smm struct private_data *data = (struct private_data *)f->data; 223324145Smm 224324145Smm if (data->out.dst == NULL) { 225324145Smm size_t bs = ZSTD_CStreamOutSize(), bpb; 226324145Smm if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 227324145Smm /* Buffer size should be a multiple number of 228324145Smm * the of bytes per block for performance. */ 229324145Smm bpb = archive_write_get_bytes_per_block(f->archive); 230324145Smm if (bpb > bs) 231324145Smm bs = bpb; 232324145Smm else if (bpb != 0) 233324145Smm bs -= bs % bpb; 234324145Smm } 235324145Smm data->out.size = bs; 236324145Smm data->out.pos = 0; 237324145Smm data->out.dst 238324145Smm = (unsigned char *)malloc(data->out.size); 239324145Smm if (data->out.dst == NULL) { 240324145Smm archive_set_error(f->archive, ENOMEM, 241324145Smm "Can't allocate data for compression buffer"); 242324145Smm return (ARCHIVE_FATAL); 243324145Smm } 244324145Smm } 245324145Smm 246324145Smm f->write = archive_compressor_zstd_write; 247324145Smm 248324145Smm if (ZSTD_isError(ZSTD_initCStream(data->cstream, 249324145Smm data->compression_level))) { 250324145Smm archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 251324145Smm "Internal error initializing zstd compressor object"); 252324145Smm return (ARCHIVE_FATAL); 253324145Smm } 254324145Smm 255324145Smm return (ARCHIVE_OK); 256324145Smm} 257324145Smm 258324145Smm/* 259324145Smm * Write data to the compressed stream. 260324145Smm */ 261324145Smmstatic int 262324145Smmarchive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, 263324145Smm size_t length) 264324145Smm{ 265324145Smm struct private_data *data = (struct private_data *)f->data; 266324145Smm int ret; 267324145Smm 268324145Smm /* Update statistics */ 269324145Smm data->total_in += length; 270324145Smm 271324145Smm if ((ret = drive_compressor(f, data, 0, buff, length)) != ARCHIVE_OK) 272324145Smm return (ret); 273324145Smm 274324145Smm return (ARCHIVE_OK); 275324145Smm} 276324145Smm 277324145Smm/* 278324145Smm * Finish the compression... 279324145Smm */ 280324145Smmstatic int 281324145Smmarchive_compressor_zstd_close(struct archive_write_filter *f) 282324145Smm{ 283324145Smm struct private_data *data = (struct private_data *)f->data; 284324145Smm 285324145Smm /* Finish zstd frame */ 286358090Smm return drive_compressor(f, data, 1, NULL, 0); 287324145Smm} 288324145Smm 289324145Smm/* 290324145Smm * Utility function to push input data through compressor, 291324145Smm * writing full output blocks as necessary. 292324145Smm * 293324145Smm * Note that this handles both the regular write case (finishing == 294324145Smm * false) and the end-of-archive case (finishing == true). 295324145Smm */ 296324145Smmstatic int 297324145Smmdrive_compressor(struct archive_write_filter *f, 298324145Smm struct private_data *data, int finishing, const void *src, size_t length) 299324145Smm{ 300324145Smm ZSTD_inBuffer in = (ZSTD_inBuffer) { src, length, 0 }; 301324145Smm 302324145Smm for (;;) { 303324145Smm if (data->out.pos == data->out.size) { 304324145Smm const int ret = __archive_write_filter(f->next_filter, 305324145Smm data->out.dst, data->out.size); 306324145Smm if (ret != ARCHIVE_OK) 307324145Smm return (ARCHIVE_FATAL); 308324145Smm data->out.pos = 0; 309324145Smm } 310324145Smm 311324145Smm /* If there's nothing to do, we're done. */ 312324145Smm if (!finishing && in.pos == in.size) 313324145Smm return (ARCHIVE_OK); 314324145Smm 315324145Smm { 316324145Smm const size_t zstdret = !finishing ? 317324145Smm ZSTD_compressStream(data->cstream, &data->out, &in) 318324145Smm : ZSTD_endStream(data->cstream, &data->out); 319324145Smm 320324145Smm if (ZSTD_isError(zstdret)) { 321324145Smm archive_set_error(f->archive, 322324145Smm ARCHIVE_ERRNO_MISC, 323324145Smm "Zstd compression failed: %s", 324324145Smm ZSTD_getErrorName(zstdret)); 325324145Smm return (ARCHIVE_FATAL); 326324145Smm } 327324145Smm 328324145Smm /* If we're finishing, 0 means nothing left to flush */ 329324145Smm if (finishing && zstdret == 0) { 330324145Smm const int ret = __archive_write_filter(f->next_filter, 331324145Smm data->out.dst, data->out.pos); 332324145Smm return (ret); 333324145Smm } 334324145Smm } 335324145Smm } 336324145Smm} 337324145Smm 338324145Smm#else /* HAVE_ZSTD_H && HAVE_LIBZSTD */ 339324145Smm 340324145Smmstatic int 341324145Smmarchive_compressor_zstd_open(struct archive_write_filter *f) 342324145Smm{ 343324145Smm struct private_data *data = (struct private_data *)f->data; 344324145Smm struct archive_string as; 345324145Smm int r; 346324145Smm 347324145Smm archive_string_init(&as); 348362134Smm /* --no-check matches library default */ 349362134Smm archive_strcpy(&as, "zstd --no-check"); 350324145Smm 351362134Smm if (data->compression_level < CLEVEL_STD_MIN) { 352362134Smm struct archive_string as2; 353362134Smm archive_string_init(&as2); 354362134Smm archive_string_sprintf(&as2, " --fast=%d", -data->compression_level); 355362134Smm archive_string_concat(&as, &as2); 356362134Smm archive_string_free(&as2); 357362134Smm } else { 358362134Smm struct archive_string as2; 359362134Smm archive_string_init(&as2); 360362134Smm archive_string_sprintf(&as2, " -%d", data->compression_level); 361362134Smm archive_string_concat(&as, &as2); 362362134Smm archive_string_free(&as2); 363362134Smm } 364362134Smm 365362134Smm if (data->compression_level > CLEVEL_STD_MAX) { 366362134Smm archive_strcat(&as, " --ultra"); 367362134Smm } 368362134Smm 369324145Smm f->write = archive_compressor_zstd_write; 370324145Smm r = __archive_write_program_open(f, data->pdata, as.s); 371324145Smm archive_string_free(&as); 372324145Smm return (r); 373324145Smm} 374324145Smm 375324145Smmstatic int 376324145Smmarchive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, 377324145Smm size_t length) 378324145Smm{ 379324145Smm struct private_data *data = (struct private_data *)f->data; 380324145Smm 381324145Smm return __archive_write_program_write(f, data->pdata, buff, length); 382324145Smm} 383324145Smm 384324145Smmstatic int 385324145Smmarchive_compressor_zstd_close(struct archive_write_filter *f) 386324145Smm{ 387324145Smm struct private_data *data = (struct private_data *)f->data; 388324145Smm 389324145Smm return __archive_write_program_close(f, data->pdata); 390324145Smm} 391324145Smm 392324145Smm#endif /* HAVE_ZSTD_H && HAVE_LIBZSTD */ 393