1 /*- 2 * Copyright (c) 2014 Michihiro NAKAJIMA 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "archive_platform.h" 27 28 __FBSDID("$FreeBSD$"); 29 30 #ifdef HAVE_ERRNO_H 31 #include <errno.h> 32 #endif 33 #include <stdio.h> 34 #ifdef HAVE_STDLIB_H 35 #include <stdlib.h> 36 #endif 37 #ifdef HAVE_STRING_H 38 #include <string.h> 39 #endif 40 #ifdef HAVE_LZ4_H 41 #include <lz4.h> 42 #endif 43 #ifdef HAVE_LZ4HC_H 44 #include <lz4hc.h> 45 #endif 46 47 #include "archive.h" 48 #include "archive_endian.h" 49 #include "archive_private.h" 50 #include "archive_write_private.h" 51 #include "archive_xxhash.h" 52 53 #define LZ4_MAGICNUMBER 0x184d2204 54 55 struct private_data { 56 int compression_level; 57 unsigned header_written:1; 58 unsigned version_number:1; 59 unsigned block_independence:1; 60 unsigned block_checksum:1; 61 unsigned stream_size:1; 62 unsigned stream_checksum:1; 63 unsigned preset_dictionary:1; 64 unsigned block_maximum_size:3; 65 #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 66 int64_t total_in; 67 char *out; 68 char *out_buffer; 69 size_t out_buffer_size; 70 size_t out_block_size; 71 char *in; 72 char *in_buffer_allocated; 73 char *in_buffer; 74 size_t in_buffer_size; 75 size_t block_size; 76 77 void *xxh32_state; 78 void *lz4_stream; 79 #else 80 struct archive_write_program_data *pdata; 81 #endif 82 }; 83 84 static int archive_filter_lz4_close(struct archive_write_filter *); 85 static int archive_filter_lz4_free(struct archive_write_filter *); 86 static int archive_filter_lz4_open(struct archive_write_filter *); 87 static int archive_filter_lz4_options(struct archive_write_filter *, 88 const char *, const char *); 89 static int archive_filter_lz4_write(struct archive_write_filter *, 90 const void *, size_t); 91 92 /* 93 * Add a lz4 compression filter to this write handle. 94 */ 95 int 96 archive_write_add_filter_lz4(struct archive *_a) 97 { 98 struct archive_write *a = (struct archive_write *)_a; 99 struct archive_write_filter *f = __archive_write_allocate_filter(_a); 100 struct private_data *data; 101 102 archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 103 ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4"); 104 105 data = calloc(1, sizeof(*data)); 106 if (data == NULL) { 107 archive_set_error(&a->archive, ENOMEM, "Out of memory"); 108 return (ARCHIVE_FATAL); 109 } 110 111 /* 112 * Setup default settings. 113 */ 114 data->compression_level = 1; 115 data->version_number = 0x01; 116 data->block_independence = 1; 117 data->block_checksum = 0; 118 data->stream_size = 0; 119 data->stream_checksum = 1; 120 data->preset_dictionary = 0; 121 data->block_maximum_size = 7; 122 123 /* 124 * Setup a filter setting. 125 */ 126 f->data = data; 127 f->options = &archive_filter_lz4_options; 128 f->close = &archive_filter_lz4_close; 129 f->free = &archive_filter_lz4_free; 130 f->open = &archive_filter_lz4_open; 131 f->code = ARCHIVE_FILTER_LZ4; 132 f->name = "lz4"; 133 #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 134 return (ARCHIVE_OK); 135 #else 136 /* 137 * We don't have lz4 library, and execute external lz4 program 138 * instead. 139 */ 140 data->pdata = __archive_write_program_allocate("lz4"); 141 if (data->pdata == NULL) { 142 free(data); 143 archive_set_error(&a->archive, ENOMEM, "Out of memory"); 144 return (ARCHIVE_FATAL); 145 } 146 data->compression_level = 0; 147 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 148 "Using external lz4 program"); 149 return (ARCHIVE_WARN); 150 #endif 151 } 152 153 /* 154 * Set write options. 155 */ 156 static int 157 archive_filter_lz4_options(struct archive_write_filter *f, 158 const char *key, const char *value) 159 { 160 struct private_data *data = (struct private_data *)f->data; 161 162 if (strcmp(key, "compression-level") == 0) { 163 int val; 164 if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) || 165 value[1] != '\0') 166 return (ARCHIVE_WARN); 167 168 #ifndef HAVE_LZ4HC_H 169 if(val >= 3) 170 { 171 archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER, 172 "High compression not included in this build"); 173 return (ARCHIVE_FATAL); 174 } 175 #endif 176 data->compression_level = val; 177 return (ARCHIVE_OK); 178 } 179 if (strcmp(key, "stream-checksum") == 0) { 180 data->stream_checksum = value != NULL; 181 return (ARCHIVE_OK); 182 } 183 if (strcmp(key, "block-checksum") == 0) { 184 data->block_checksum = value != NULL; 185 return (ARCHIVE_OK); 186 } 187 if (strcmp(key, "block-size") == 0) { 188 if (value == NULL || !(value[0] >= '4' && value[0] <= '7') || 189 value[1] != '\0') 190 return (ARCHIVE_WARN); 191 data->block_maximum_size = value[0] - '0'; 192 return (ARCHIVE_OK); 193 } 194 if (strcmp(key, "block-dependence") == 0) { 195 data->block_independence = value == NULL; 196 return (ARCHIVE_OK); 197 } 198 199 /* Note: The "warn" return is just to inform the options 200 * supervisor that we didn't handle it. It will generate 201 * a suitable error if no one used this option. */ 202 return (ARCHIVE_WARN); 203 } 204 205 #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 206 /* Don't compile this if we don't have liblz4. */ 207 208 static int drive_compressor(struct archive_write_filter *, const char *, 209 size_t); 210 static int drive_compressor_independence(struct archive_write_filter *, 211 const char *, size_t); 212 static int drive_compressor_dependence(struct archive_write_filter *, 213 const char *, size_t); 214 static int lz4_write_stream_descriptor(struct archive_write_filter *); 215 static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *, 216 size_t); 217 218 219 /* 220 * Setup callback. 221 */ 222 static int 223 archive_filter_lz4_open(struct archive_write_filter *f) 224 { 225 struct private_data *data = (struct private_data *)f->data; 226 int ret; 227 size_t required_size; 228 static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, 229 4 * 1024 * 1024 }; 230 size_t pre_block_size; 231 232 ret = __archive_write_open_filter(f->next_filter); 233 if (ret != 0) 234 return (ret); 235 236 if (data->block_maximum_size < 4) 237 data->block_size = bkmap[0]; 238 else 239 data->block_size = bkmap[data->block_maximum_size - 4]; 240 241 required_size = 4 + 15 + 4 + data->block_size + 4 + 4; 242 if (data->out_buffer_size < required_size) { 243 size_t bs = required_size, bpb; 244 free(data->out_buffer); 245 if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 246 /* Buffer size should be a multiple number of 247 * the of bytes per block for performance. */ 248 bpb = archive_write_get_bytes_per_block(f->archive); 249 if (bpb > bs) 250 bs = bpb; 251 else if (bpb != 0) { 252 bs += bpb; 253 bs -= bs % bpb; 254 } 255 } 256 data->out_block_size = bs; 257 bs += required_size; 258 data->out_buffer = malloc(bs); 259 data->out = data->out_buffer; 260 data->out_buffer_size = bs; 261 } 262 263 pre_block_size = (data->block_independence)? 0: 64 * 1024; 264 if (data->in_buffer_size < data->block_size + pre_block_size) { 265 free(data->in_buffer_allocated); 266 data->in_buffer_size = data->block_size; 267 data->in_buffer_allocated = 268 malloc(data->in_buffer_size + pre_block_size); 269 data->in_buffer = data->in_buffer_allocated + pre_block_size; 270 if (!data->block_independence && data->compression_level >= 3) 271 data->in_buffer = data->in_buffer_allocated; 272 data->in = data->in_buffer; 273 data->in_buffer_size = data->block_size; 274 } 275 276 if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) { 277 archive_set_error(f->archive, ENOMEM, 278 "Can't allocate data for compression buffer"); 279 return (ARCHIVE_FATAL); 280 } 281 282 f->write = archive_filter_lz4_write; 283 284 return (ARCHIVE_OK); 285 } 286 287 /* 288 * Write data to the out stream. 289 * 290 * Returns ARCHIVE_OK if all data written, error otherwise. 291 */ 292 static int 293 archive_filter_lz4_write(struct archive_write_filter *f, 294 const void *buff, size_t length) 295 { 296 struct private_data *data = (struct private_data *)f->data; 297 int ret = ARCHIVE_OK; 298 const char *p; 299 size_t remaining; 300 ssize_t size; 301 302 /* If we haven't written a stream descriptor, we have to do it first. */ 303 if (!data->header_written) { 304 ret = lz4_write_stream_descriptor(f); 305 if (ret != ARCHIVE_OK) 306 return (ret); 307 data->header_written = 1; 308 } 309 310 /* Update statistics */ 311 data->total_in += length; 312 313 p = (const char *)buff; 314 remaining = length; 315 while (remaining) { 316 size_t l; 317 /* Compress input data to output buffer */ 318 size = lz4_write_one_block(f, p, remaining); 319 if (size < ARCHIVE_OK) 320 return (ARCHIVE_FATAL); 321 l = data->out - data->out_buffer; 322 if (l >= data->out_block_size) { 323 ret = __archive_write_filter(f->next_filter, 324 data->out_buffer, data->out_block_size); 325 l -= data->out_block_size; 326 memcpy(data->out_buffer, 327 data->out_buffer + data->out_block_size, l); 328 data->out = data->out_buffer + l; 329 if (ret < ARCHIVE_WARN) 330 break; 331 } 332 p += size; 333 remaining -= size; 334 } 335 336 return (ret); 337 } 338 339 /* 340 * Finish the compression. 341 */ 342 static int 343 archive_filter_lz4_close(struct archive_write_filter *f) 344 { 345 struct private_data *data = (struct private_data *)f->data; 346 int ret, r1; 347 348 /* Finish compression cycle. */ 349 ret = (int)lz4_write_one_block(f, NULL, 0); 350 if (ret >= 0) { 351 /* 352 * Write the last block and the end of the stream data. 353 */ 354 355 /* Write End Of Stream. */ 356 memset(data->out, 0, 4); data->out += 4; 357 /* Write Stream checksum if needed. */ 358 if (data->stream_checksum) { 359 unsigned int checksum; 360 checksum = __archive_xxhash.XXH32_digest( 361 data->xxh32_state); 362 data->xxh32_state = NULL; 363 archive_le32enc(data->out, checksum); 364 data->out += 4; 365 } 366 ret = __archive_write_filter(f->next_filter, 367 data->out_buffer, data->out - data->out_buffer); 368 } 369 370 r1 = __archive_write_close_filter(f->next_filter); 371 return (r1 < ret ? r1 : ret); 372 } 373 374 static int 375 archive_filter_lz4_free(struct archive_write_filter *f) 376 { 377 struct private_data *data = (struct private_data *)f->data; 378 379 if (data->lz4_stream != NULL) { 380 #ifdef HAVE_LZ4HC_H 381 if (data->compression_level >= 3) 382 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 383 LZ4_freeStreamHC(data->lz4_stream); 384 #else 385 LZ4_freeHC(data->lz4_stream); 386 #endif 387 else 388 #endif 389 #if LZ4_VERSION_MINOR >= 3 390 LZ4_freeStream(data->lz4_stream); 391 #else 392 LZ4_free(data->lz4_stream); 393 #endif 394 } 395 free(data->out_buffer); 396 free(data->in_buffer_allocated); 397 free(data->xxh32_state); 398 free(data); 399 f->data = NULL; 400 return (ARCHIVE_OK); 401 } 402 403 static int 404 lz4_write_stream_descriptor(struct archive_write_filter *f) 405 { 406 struct private_data *data = (struct private_data *)f->data; 407 uint8_t *sd; 408 409 sd = (uint8_t *)data->out; 410 /* Write Magic Number. */ 411 archive_le32enc(&sd[0], LZ4_MAGICNUMBER); 412 /* FLG */ 413 sd[4] = (data->version_number << 6) 414 | (data->block_independence << 5) 415 | (data->block_checksum << 4) 416 | (data->stream_size << 3) 417 | (data->stream_checksum << 2) 418 | (data->preset_dictionary << 0); 419 /* BD */ 420 sd[5] = (data->block_maximum_size << 4); 421 sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff; 422 data->out += 7; 423 if (data->stream_checksum) 424 data->xxh32_state = __archive_xxhash.XXH32_init(0); 425 else 426 data->xxh32_state = NULL; 427 return (ARCHIVE_OK); 428 } 429 430 static ssize_t 431 lz4_write_one_block(struct archive_write_filter *f, const char *p, 432 size_t length) 433 { 434 struct private_data *data = (struct private_data *)f->data; 435 ssize_t r; 436 437 if (p == NULL) { 438 /* Compress remaining uncompressed data. */ 439 if (data->in_buffer == data->in) 440 return 0; 441 else { 442 size_t l = data->in - data->in_buffer; 443 r = drive_compressor(f, data->in_buffer, l); 444 if (r == ARCHIVE_OK) 445 r = (ssize_t)l; 446 } 447 } else if ((data->block_independence || data->compression_level < 3) && 448 data->in_buffer == data->in && length >= data->block_size) { 449 r = drive_compressor(f, p, data->block_size); 450 if (r == ARCHIVE_OK) 451 r = (ssize_t)data->block_size; 452 } else { 453 size_t remaining_size = data->in_buffer_size - 454 (data->in - data->in_buffer); 455 size_t l = (remaining_size > length)? length: remaining_size; 456 memcpy(data->in, p, l); 457 data->in += l; 458 if (l == remaining_size) { 459 r = drive_compressor(f, data->in_buffer, 460 data->block_size); 461 if (r == ARCHIVE_OK) 462 r = (ssize_t)l; 463 data->in = data->in_buffer; 464 } else 465 r = (ssize_t)l; 466 } 467 468 return (r); 469 } 470 471 472 /* 473 * Utility function to push input data through compressor, writing 474 * full output blocks as necessary. 475 * 476 * Note that this handles both the regular write case (finishing == 477 * false) and the end-of-archive case (finishing == true). 478 */ 479 static int 480 drive_compressor(struct archive_write_filter *f, const char *p, size_t length) 481 { 482 struct private_data *data = (struct private_data *)f->data; 483 484 if (data->stream_checksum) 485 __archive_xxhash.XXH32_update(data->xxh32_state, 486 p, (int)length); 487 if (data->block_independence) 488 return drive_compressor_independence(f, p, length); 489 else 490 return drive_compressor_dependence(f, p, length); 491 } 492 493 static int 494 drive_compressor_independence(struct archive_write_filter *f, const char *p, 495 size_t length) 496 { 497 struct private_data *data = (struct private_data *)f->data; 498 unsigned int outsize; 499 500 #ifdef HAVE_LZ4HC_H 501 if (data->compression_level >= 3) 502 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 503 outsize = LZ4_compress_HC(p, data->out + 4, 504 (int)length, (int)data->block_size, 505 data->compression_level); 506 #else 507 outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4, 508 (int)length, (int)data->block_size, 509 data->compression_level); 510 #endif 511 else 512 #endif 513 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 514 outsize = LZ4_compress_default(p, data->out + 4, 515 (int)length, (int)data->block_size); 516 #else 517 outsize = LZ4_compress_limitedOutput(p, data->out + 4, 518 (int)length, (int)data->block_size); 519 #endif 520 521 if (outsize) { 522 /* The buffer is compressed. */ 523 archive_le32enc(data->out, outsize); 524 data->out += 4; 525 } else { 526 /* The buffer is not compressed. The compressed size was 527 * bigger than its uncompressed size. */ 528 archive_le32enc(data->out, length | 0x80000000); 529 data->out += 4; 530 memcpy(data->out, p, length); 531 outsize = length; 532 } 533 data->out += outsize; 534 if (data->block_checksum) { 535 unsigned int checksum = 536 __archive_xxhash.XXH32(data->out - outsize, outsize, 0); 537 archive_le32enc(data->out, checksum); 538 data->out += 4; 539 } 540 return (ARCHIVE_OK); 541 } 542 543 static int 544 drive_compressor_dependence(struct archive_write_filter *f, const char *p, 545 size_t length) 546 { 547 struct private_data *data = (struct private_data *)f->data; 548 int outsize; 549 550 #define DICT_SIZE (64 * 1024) 551 #ifdef HAVE_LZ4HC_H 552 if (data->compression_level >= 3) { 553 if (data->lz4_stream == NULL) { 554 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 555 data->lz4_stream = LZ4_createStreamHC(); 556 LZ4_resetStreamHC(data->lz4_stream, data->compression_level); 557 #else 558 data->lz4_stream = 559 LZ4_createHC(data->in_buffer_allocated); 560 #endif 561 if (data->lz4_stream == NULL) { 562 archive_set_error(f->archive, ENOMEM, 563 "Can't allocate data for compression" 564 " buffer"); 565 return (ARCHIVE_FATAL); 566 } 567 } 568 else 569 LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); 570 571 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 572 outsize = LZ4_compress_HC_continue( 573 data->lz4_stream, p, data->out + 4, (int)length, 574 (int)data->block_size); 575 #else 576 outsize = LZ4_compressHC2_limitedOutput_continue( 577 data->lz4_stream, p, data->out + 4, (int)length, 578 (int)data->block_size, data->compression_level); 579 #endif 580 } else 581 #endif 582 { 583 if (data->lz4_stream == NULL) { 584 data->lz4_stream = LZ4_createStream(); 585 if (data->lz4_stream == NULL) { 586 archive_set_error(f->archive, ENOMEM, 587 "Can't allocate data for compression" 588 " buffer"); 589 return (ARCHIVE_FATAL); 590 } 591 } 592 else 593 LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); 594 595 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 596 outsize = LZ4_compress_fast_continue( 597 data->lz4_stream, p, data->out + 4, (int)length, 598 (int)data->block_size, 1); 599 #else 600 outsize = LZ4_compress_limitedOutput_continue( 601 data->lz4_stream, p, data->out + 4, (int)length, 602 (int)data->block_size); 603 #endif 604 } 605 606 if (outsize) { 607 /* The buffer is compressed. */ 608 archive_le32enc(data->out, outsize); 609 data->out += 4; 610 } else { 611 /* The buffer is not compressed. The compressed size was 612 * bigger than its uncompressed size. */ 613 archive_le32enc(data->out, length | 0x80000000); 614 data->out += 4; 615 memcpy(data->out, p, length); 616 outsize = length; 617 } 618 data->out += outsize; 619 if (data->block_checksum) { 620 unsigned int checksum = 621 __archive_xxhash.XXH32(data->out - outsize, outsize, 0); 622 archive_le32enc(data->out, checksum); 623 data->out += 4; 624 } 625 626 if (length == data->block_size) { 627 #ifdef HAVE_LZ4HC_H 628 if (data->compression_level >= 3) { 629 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 630 LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); 631 #else 632 LZ4_slideInputBufferHC(data->lz4_stream); 633 #endif 634 data->in_buffer = data->in_buffer_allocated + DICT_SIZE; 635 } 636 else 637 #endif 638 LZ4_saveDict(data->lz4_stream, 639 data->in_buffer_allocated, DICT_SIZE); 640 #undef DICT_SIZE 641 } 642 return (ARCHIVE_OK); 643 } 644 645 #else /* HAVE_LIBLZ4 */ 646 647 static int 648 archive_filter_lz4_open(struct archive_write_filter *f) 649 { 650 struct private_data *data = (struct private_data *)f->data; 651 struct archive_string as; 652 int r; 653 654 archive_string_init(&as); 655 archive_strcpy(&as, "lz4 -z -q -q"); 656 657 /* Specify a compression level. */ 658 if (data->compression_level > 0) { 659 archive_strcat(&as, " -"); 660 archive_strappend_char(&as, '0' + data->compression_level); 661 } 662 /* Specify a block size. */ 663 archive_strcat(&as, " -B"); 664 archive_strappend_char(&as, '0' + data->block_maximum_size); 665 666 if (data->block_checksum) 667 archive_strcat(&as, " -BX"); 668 if (data->stream_checksum == 0) 669 archive_strcat(&as, " --no-frame-crc"); 670 if (data->block_independence == 0) 671 archive_strcat(&as, " -BD"); 672 673 f->write = archive_filter_lz4_write; 674 675 r = __archive_write_program_open(f, data->pdata, as.s); 676 archive_string_free(&as); 677 return (r); 678 } 679 680 static int 681 archive_filter_lz4_write(struct archive_write_filter *f, const void *buff, 682 size_t length) 683 { 684 struct private_data *data = (struct private_data *)f->data; 685 686 return __archive_write_program_write(f, data->pdata, buff, length); 687 } 688 689 static int 690 archive_filter_lz4_close(struct archive_write_filter *f) 691 { 692 struct private_data *data = (struct private_data *)f->data; 693 694 return __archive_write_program_close(f, data->pdata); 695 } 696 697 static int 698 archive_filter_lz4_free(struct archive_write_filter *f) 699 { 700 struct private_data *data = (struct private_data *)f->data; 701 702 __archive_write_program_free(data->pdata); 703 free(data); 704 return (ARCHIVE_OK); 705 } 706 707 #endif /* HAVE_LIBLZ4 */ 708