16b384f39SPeter Avalos /*-
26b384f39SPeter Avalos  * Copyright (c) 2014 Michihiro NAKAJIMA
36b384f39SPeter Avalos  * All rights reserved.
46b384f39SPeter Avalos  *
56b384f39SPeter Avalos  * Redistribution and use in source and binary forms, with or without
66b384f39SPeter Avalos  * modification, are permitted provided that the following conditions
76b384f39SPeter Avalos  * are met:
86b384f39SPeter Avalos  * 1. Redistributions of source code must retain the above copyright
96b384f39SPeter Avalos  *    notice, this list of conditions and the following disclaimer.
106b384f39SPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
116b384f39SPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
126b384f39SPeter Avalos  *    documentation and/or other materials provided with the distribution.
136b384f39SPeter Avalos  *
146b384f39SPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
156b384f39SPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
166b384f39SPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
176b384f39SPeter Avalos  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
186b384f39SPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
196b384f39SPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
206b384f39SPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
216b384f39SPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
226b384f39SPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
236b384f39SPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
246b384f39SPeter Avalos  */
256b384f39SPeter Avalos 
266b384f39SPeter Avalos #include "archive_platform.h"
276b384f39SPeter Avalos 
286b384f39SPeter Avalos __FBSDID("$FreeBSD$");
296b384f39SPeter Avalos 
306b384f39SPeter Avalos #ifdef HAVE_ERRNO_H
316b384f39SPeter Avalos #include <errno.h>
326b384f39SPeter Avalos #endif
336b384f39SPeter Avalos #include <stdio.h>
346b384f39SPeter Avalos #ifdef HAVE_STDLIB_H
356b384f39SPeter Avalos #include <stdlib.h>
366b384f39SPeter Avalos #endif
376b384f39SPeter Avalos #ifdef HAVE_STRING_H
386b384f39SPeter Avalos #include <string.h>
396b384f39SPeter Avalos #endif
406b384f39SPeter Avalos #ifdef HAVE_LZ4_H
416b384f39SPeter Avalos #include <lz4.h>
426b384f39SPeter Avalos #endif
436b384f39SPeter Avalos #ifdef HAVE_LZ4HC_H
446b384f39SPeter Avalos #include <lz4hc.h>
456b384f39SPeter Avalos #endif
466b384f39SPeter Avalos 
476b384f39SPeter Avalos #include "archive.h"
486b384f39SPeter Avalos #include "archive_endian.h"
496b384f39SPeter Avalos #include "archive_private.h"
506b384f39SPeter Avalos #include "archive_write_private.h"
516b384f39SPeter Avalos #include "archive_xxhash.h"
526b384f39SPeter Avalos 
536b384f39SPeter Avalos #define LZ4_MAGICNUMBER	0x184d2204
546b384f39SPeter Avalos 
556b384f39SPeter Avalos struct private_data {
566b384f39SPeter Avalos 	int		 compression_level;
576b384f39SPeter Avalos 	unsigned	 header_written:1;
586b384f39SPeter Avalos 	unsigned	 version_number:1;
596b384f39SPeter Avalos 	unsigned	 block_independence:1;
606b384f39SPeter Avalos 	unsigned	 block_checksum:1;
616b384f39SPeter Avalos 	unsigned	 stream_size:1;
626b384f39SPeter Avalos 	unsigned	 stream_checksum:1;
636b384f39SPeter Avalos 	unsigned	 preset_dictionary:1;
646b384f39SPeter Avalos 	unsigned	 block_maximum_size:3;
656b384f39SPeter Avalos #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
666b384f39SPeter Avalos 	int64_t		 total_in;
676b384f39SPeter Avalos 	char		*out;
686b384f39SPeter Avalos 	char		*out_buffer;
696b384f39SPeter Avalos 	size_t		 out_buffer_size;
706b384f39SPeter Avalos 	size_t		 out_block_size;
716b384f39SPeter Avalos 	char		*in;
726b384f39SPeter Avalos 	char		*in_buffer_allocated;
736b384f39SPeter Avalos 	char		*in_buffer;
746b384f39SPeter Avalos 	size_t		 in_buffer_size;
756b384f39SPeter Avalos 	size_t		 block_size;
766b384f39SPeter Avalos 
776b384f39SPeter Avalos 	void		*xxh32_state;
786b384f39SPeter Avalos 	void		*lz4_stream;
796b384f39SPeter Avalos #else
806b384f39SPeter Avalos 	struct archive_write_program_data *pdata;
816b384f39SPeter Avalos #endif
826b384f39SPeter Avalos };
836b384f39SPeter Avalos 
846b384f39SPeter Avalos static int archive_filter_lz4_close(struct archive_write_filter *);
856b384f39SPeter Avalos static int archive_filter_lz4_free(struct archive_write_filter *);
866b384f39SPeter Avalos static int archive_filter_lz4_open(struct archive_write_filter *);
876b384f39SPeter Avalos static int archive_filter_lz4_options(struct archive_write_filter *,
886b384f39SPeter Avalos 		    const char *, const char *);
896b384f39SPeter Avalos static int archive_filter_lz4_write(struct archive_write_filter *,
906b384f39SPeter Avalos 		    const void *, size_t);
916b384f39SPeter Avalos 
926b384f39SPeter Avalos /*
936b384f39SPeter Avalos  * Add a lz4 compression filter to this write handle.
946b384f39SPeter Avalos  */
956b384f39SPeter Avalos int
archive_write_add_filter_lz4(struct archive * _a)966b384f39SPeter Avalos archive_write_add_filter_lz4(struct archive *_a)
976b384f39SPeter Avalos {
986b384f39SPeter Avalos 	struct archive_write *a = (struct archive_write *)_a;
996b384f39SPeter Avalos 	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
1006b384f39SPeter Avalos 	struct private_data *data;
1016b384f39SPeter Avalos 
1026b384f39SPeter Avalos 	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
1036b384f39SPeter Avalos 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4");
1046b384f39SPeter Avalos 
1056b384f39SPeter Avalos 	data = calloc(1, sizeof(*data));
1066b384f39SPeter Avalos 	if (data == NULL) {
1076b384f39SPeter Avalos 		archive_set_error(&a->archive, ENOMEM, "Out of memory");
1086b384f39SPeter Avalos 		return (ARCHIVE_FATAL);
1096b384f39SPeter Avalos 	}
1106b384f39SPeter Avalos 
1116b384f39SPeter Avalos 	/*
1126b384f39SPeter Avalos 	 * Setup default settings.
1136b384f39SPeter Avalos 	 */
1146b384f39SPeter Avalos 	data->compression_level = 1;
1156b384f39SPeter Avalos 	data->version_number = 0x01;
1166b384f39SPeter Avalos 	data->block_independence = 1;
1176b384f39SPeter Avalos 	data->block_checksum = 0;
1186b384f39SPeter Avalos 	data->stream_size = 0;
1196b384f39SPeter Avalos 	data->stream_checksum = 1;
1206b384f39SPeter Avalos 	data->preset_dictionary = 0;
1216b384f39SPeter Avalos 	data->block_maximum_size = 7;
1226b384f39SPeter Avalos 
1236b384f39SPeter Avalos 	/*
1246b384f39SPeter Avalos 	 * Setup a filter setting.
1256b384f39SPeter Avalos 	 */
1266b384f39SPeter Avalos 	f->data = data;
1276b384f39SPeter Avalos 	f->options = &archive_filter_lz4_options;
1286b384f39SPeter Avalos 	f->close = &archive_filter_lz4_close;
1296b384f39SPeter Avalos 	f->free = &archive_filter_lz4_free;
1306b384f39SPeter Avalos 	f->open = &archive_filter_lz4_open;
1316b384f39SPeter Avalos 	f->code = ARCHIVE_FILTER_LZ4;
1326b384f39SPeter Avalos 	f->name = "lz4";
1336b384f39SPeter Avalos #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
1346b384f39SPeter Avalos 	return (ARCHIVE_OK);
1356b384f39SPeter Avalos #else
1366b384f39SPeter Avalos 	/*
1376b384f39SPeter Avalos 	 * We don't have lz4 library, and execute external lz4 program
1386b384f39SPeter Avalos 	 * instead.
1396b384f39SPeter Avalos 	 */
1406b384f39SPeter Avalos 	data->pdata = __archive_write_program_allocate("lz4");
1416b384f39SPeter Avalos 	if (data->pdata == NULL) {
1426b384f39SPeter Avalos 		free(data);
1436b384f39SPeter Avalos 		archive_set_error(&a->archive, ENOMEM, "Out of memory");
1446b384f39SPeter Avalos 		return (ARCHIVE_FATAL);
1456b384f39SPeter Avalos 	}
1466b384f39SPeter Avalos 	data->compression_level = 0;
1476b384f39SPeter Avalos 	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
1486b384f39SPeter Avalos 	    "Using external lz4 program");
1496b384f39SPeter Avalos 	return (ARCHIVE_WARN);
1506b384f39SPeter Avalos #endif
1516b384f39SPeter Avalos }
1526b384f39SPeter Avalos 
1536b384f39SPeter Avalos /*
1546b384f39SPeter Avalos  * Set write options.
1556b384f39SPeter Avalos  */
1566b384f39SPeter Avalos static int
archive_filter_lz4_options(struct archive_write_filter * f,const char * key,const char * value)1576b384f39SPeter Avalos archive_filter_lz4_options(struct archive_write_filter *f,
1586b384f39SPeter Avalos     const char *key, const char *value)
1596b384f39SPeter Avalos {
1606b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
1616b384f39SPeter Avalos 
1626b384f39SPeter Avalos 	if (strcmp(key, "compression-level") == 0) {
1636b384f39SPeter Avalos 		int val;
1646b384f39SPeter Avalos 		if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) ||
1656b384f39SPeter Avalos 		    value[1] != '\0')
1666b384f39SPeter Avalos 			return (ARCHIVE_WARN);
1676b384f39SPeter Avalos 
1686b384f39SPeter Avalos #ifndef HAVE_LZ4HC_H
1696b384f39SPeter Avalos 		if(val >= 3)
1706b384f39SPeter Avalos 		{
1716b384f39SPeter Avalos 			archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
1726b384f39SPeter Avalos 				"High compression not included in this build");
1736b384f39SPeter Avalos 			return (ARCHIVE_FATAL);
1746b384f39SPeter Avalos 		}
1756b384f39SPeter Avalos #endif
1766b384f39SPeter Avalos 		data->compression_level = val;
1776b384f39SPeter Avalos 		return (ARCHIVE_OK);
1786b384f39SPeter Avalos 	}
1796b384f39SPeter Avalos 	if (strcmp(key, "stream-checksum") == 0) {
1806b384f39SPeter Avalos 		data->stream_checksum = value != NULL;
1816b384f39SPeter Avalos 		return (ARCHIVE_OK);
1826b384f39SPeter Avalos 	}
1836b384f39SPeter Avalos 	if (strcmp(key, "block-checksum") == 0) {
1846b384f39SPeter Avalos 		data->block_checksum = value != NULL;
1856b384f39SPeter Avalos 		return (ARCHIVE_OK);
1866b384f39SPeter Avalos 	}
1876b384f39SPeter Avalos 	if (strcmp(key, "block-size") == 0) {
1886b384f39SPeter Avalos 		if (value == NULL || !(value[0] >= '4' && value[0] <= '7') ||
1896b384f39SPeter Avalos 		    value[1] != '\0')
1906b384f39SPeter Avalos 			return (ARCHIVE_WARN);
1916b384f39SPeter Avalos 		data->block_maximum_size = value[0] - '0';
1926b384f39SPeter Avalos 		return (ARCHIVE_OK);
1936b384f39SPeter Avalos 	}
1946b384f39SPeter Avalos 	if (strcmp(key, "block-dependence") == 0) {
1956b384f39SPeter Avalos 		data->block_independence = value == NULL;
1966b384f39SPeter Avalos 		return (ARCHIVE_OK);
1976b384f39SPeter Avalos 	}
1986b384f39SPeter Avalos 
1996b384f39SPeter Avalos 	/* Note: The "warn" return is just to inform the options
2006b384f39SPeter Avalos 	 * supervisor that we didn't handle it.  It will generate
2016b384f39SPeter Avalos 	 * a suitable error if no one used this option. */
2026b384f39SPeter Avalos 	return (ARCHIVE_WARN);
2036b384f39SPeter Avalos }
2046b384f39SPeter Avalos 
2056b384f39SPeter Avalos #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
2066b384f39SPeter Avalos /* Don't compile this if we don't have liblz4. */
2076b384f39SPeter Avalos 
2086b384f39SPeter Avalos static int drive_compressor(struct archive_write_filter *, const char *,
2096b384f39SPeter Avalos     size_t);
2106b384f39SPeter Avalos static int drive_compressor_independence(struct archive_write_filter *,
2116b384f39SPeter Avalos     const char *, size_t);
2126b384f39SPeter Avalos static int drive_compressor_dependence(struct archive_write_filter *,
2136b384f39SPeter Avalos     const char *, size_t);
2146b384f39SPeter Avalos static int lz4_write_stream_descriptor(struct archive_write_filter *);
2156b384f39SPeter Avalos static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *,
2166b384f39SPeter Avalos     size_t);
2176b384f39SPeter Avalos 
2186b384f39SPeter Avalos 
2196b384f39SPeter Avalos /*
2206b384f39SPeter Avalos  * Setup callback.
2216b384f39SPeter Avalos  */
2226b384f39SPeter Avalos static int
archive_filter_lz4_open(struct archive_write_filter * f)2236b384f39SPeter Avalos archive_filter_lz4_open(struct archive_write_filter *f)
2246b384f39SPeter Avalos {
2256b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
2266b384f39SPeter Avalos 	size_t required_size;
227e95abc47Szrj 	static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,
2286b384f39SPeter Avalos 			   4 * 1024 * 1024 };
2296b384f39SPeter Avalos 	size_t pre_block_size;
2306b384f39SPeter Avalos 
2316b384f39SPeter Avalos 	if (data->block_maximum_size < 4)
2326b384f39SPeter Avalos 		data->block_size = bkmap[0];
2336b384f39SPeter Avalos 	else
2346b384f39SPeter Avalos 		data->block_size = bkmap[data->block_maximum_size - 4];
2356b384f39SPeter Avalos 
2366b384f39SPeter Avalos 	required_size = 4 + 15 + 4 + data->block_size + 4 + 4;
2376b384f39SPeter Avalos 	if (data->out_buffer_size < required_size) {
2386b384f39SPeter Avalos 		size_t bs = required_size, bpb;
2396b384f39SPeter Avalos 		free(data->out_buffer);
2406b384f39SPeter Avalos 		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
2416b384f39SPeter Avalos 			/* Buffer size should be a multiple number of
2426b384f39SPeter Avalos 			 * the of bytes per block for performance. */
2436b384f39SPeter Avalos 			bpb = archive_write_get_bytes_per_block(f->archive);
2446b384f39SPeter Avalos 			if (bpb > bs)
2456b384f39SPeter Avalos 				bs = bpb;
2466b384f39SPeter Avalos 			else if (bpb != 0) {
2476b384f39SPeter Avalos 				bs += bpb;
2486b384f39SPeter Avalos 				bs -= bs % bpb;
2496b384f39SPeter Avalos 			}
2506b384f39SPeter Avalos 		}
2516b384f39SPeter Avalos 		data->out_block_size = bs;
2526b384f39SPeter Avalos 		bs += required_size;
2536b384f39SPeter Avalos 		data->out_buffer = malloc(bs);
2546b384f39SPeter Avalos 		data->out = data->out_buffer;
2556b384f39SPeter Avalos 		data->out_buffer_size = bs;
2566b384f39SPeter Avalos 	}
2576b384f39SPeter Avalos 
2586b384f39SPeter Avalos 	pre_block_size = (data->block_independence)? 0: 64 * 1024;
2596b384f39SPeter Avalos 	if (data->in_buffer_size < data->block_size + pre_block_size) {
2606b384f39SPeter Avalos 		free(data->in_buffer_allocated);
2616b384f39SPeter Avalos 		data->in_buffer_size = data->block_size;
2626b384f39SPeter Avalos 		data->in_buffer_allocated =
2636b384f39SPeter Avalos 		    malloc(data->in_buffer_size + pre_block_size);
2646b384f39SPeter Avalos 		data->in_buffer = data->in_buffer_allocated + pre_block_size;
2656b384f39SPeter Avalos 		if (!data->block_independence && data->compression_level >= 3)
2666b384f39SPeter Avalos 		    data->in_buffer = data->in_buffer_allocated;
2676b384f39SPeter Avalos 		data->in = data->in_buffer;
2686b384f39SPeter Avalos 		data->in_buffer_size = data->block_size;
2696b384f39SPeter Avalos 	}
2706b384f39SPeter Avalos 
2716b384f39SPeter Avalos 	if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) {
2726b384f39SPeter Avalos 		archive_set_error(f->archive, ENOMEM,
2736b384f39SPeter Avalos 		    "Can't allocate data for compression buffer");
2746b384f39SPeter Avalos 		return (ARCHIVE_FATAL);
2756b384f39SPeter Avalos 	}
2766b384f39SPeter Avalos 
2776b384f39SPeter Avalos 	f->write = archive_filter_lz4_write;
2786b384f39SPeter Avalos 
2796b384f39SPeter Avalos 	return (ARCHIVE_OK);
2806b384f39SPeter Avalos }
2816b384f39SPeter Avalos 
2826b384f39SPeter Avalos /*
2836b384f39SPeter Avalos  * Write data to the out stream.
2846b384f39SPeter Avalos  *
2856b384f39SPeter Avalos  * Returns ARCHIVE_OK if all data written, error otherwise.
2866b384f39SPeter Avalos  */
2876b384f39SPeter Avalos static int
archive_filter_lz4_write(struct archive_write_filter * f,const void * buff,size_t length)2886b384f39SPeter Avalos archive_filter_lz4_write(struct archive_write_filter *f,
2896b384f39SPeter Avalos     const void *buff, size_t length)
2906b384f39SPeter Avalos {
2916b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
2926b384f39SPeter Avalos 	int ret = ARCHIVE_OK;
2936b384f39SPeter Avalos 	const char *p;
2946b384f39SPeter Avalos 	size_t remaining;
2956b384f39SPeter Avalos 	ssize_t size;
2966b384f39SPeter Avalos 
2976b384f39SPeter Avalos 	/* If we haven't written a stream descriptor, we have to do it first. */
2986b384f39SPeter Avalos 	if (!data->header_written) {
2996b384f39SPeter Avalos 		ret = lz4_write_stream_descriptor(f);
3006b384f39SPeter Avalos 		if (ret != ARCHIVE_OK)
3016b384f39SPeter Avalos 			return (ret);
3026b384f39SPeter Avalos 		data->header_written = 1;
3036b384f39SPeter Avalos 	}
3046b384f39SPeter Avalos 
3056b384f39SPeter Avalos 	/* Update statistics */
3066b384f39SPeter Avalos 	data->total_in += length;
3076b384f39SPeter Avalos 
3086b384f39SPeter Avalos 	p = (const char *)buff;
3096b384f39SPeter Avalos 	remaining = length;
3106b384f39SPeter Avalos 	while (remaining) {
3116b384f39SPeter Avalos 		size_t l;
3126b384f39SPeter Avalos 		/* Compress input data to output buffer */
3136b384f39SPeter Avalos 		size = lz4_write_one_block(f, p, remaining);
3146b384f39SPeter Avalos 		if (size < ARCHIVE_OK)
3156b384f39SPeter Avalos 			return (ARCHIVE_FATAL);
3166b384f39SPeter Avalos 		l = data->out - data->out_buffer;
3176b384f39SPeter Avalos 		if (l >= data->out_block_size) {
3186b384f39SPeter Avalos 			ret = __archive_write_filter(f->next_filter,
3196b384f39SPeter Avalos 			    data->out_buffer, data->out_block_size);
3206b384f39SPeter Avalos 			l -= data->out_block_size;
3216b384f39SPeter Avalos 			memcpy(data->out_buffer,
3226b384f39SPeter Avalos 			    data->out_buffer + data->out_block_size, l);
3236b384f39SPeter Avalos 			data->out = data->out_buffer + l;
3246b384f39SPeter Avalos 			if (ret < ARCHIVE_WARN)
3256b384f39SPeter Avalos 				break;
3266b384f39SPeter Avalos 		}
3276b384f39SPeter Avalos 		p += size;
3286b384f39SPeter Avalos 		remaining -= size;
3296b384f39SPeter Avalos 	}
3306b384f39SPeter Avalos 
3316b384f39SPeter Avalos 	return (ret);
3326b384f39SPeter Avalos }
3336b384f39SPeter Avalos 
3346b384f39SPeter Avalos /*
3356b384f39SPeter Avalos  * Finish the compression.
3366b384f39SPeter Avalos  */
3376b384f39SPeter Avalos static int
archive_filter_lz4_close(struct archive_write_filter * f)3386b384f39SPeter Avalos archive_filter_lz4_close(struct archive_write_filter *f)
3396b384f39SPeter Avalos {
3406b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
341085658deSDaniel Fojt 	int ret;
3426b384f39SPeter Avalos 
3436b384f39SPeter Avalos 	/* Finish compression cycle. */
3446b384f39SPeter Avalos 	ret = (int)lz4_write_one_block(f, NULL, 0);
3456b384f39SPeter Avalos 	if (ret >= 0) {
3466b384f39SPeter Avalos 		/*
3476b384f39SPeter Avalos 		 * Write the last block and the end of the stream data.
3486b384f39SPeter Avalos 		 */
3496b384f39SPeter Avalos 
3506b384f39SPeter Avalos 		/* Write End Of Stream. */
3516b384f39SPeter Avalos 		memset(data->out, 0, 4); data->out += 4;
3526b384f39SPeter Avalos 		/* Write Stream checksum if needed. */
3536b384f39SPeter Avalos 		if (data->stream_checksum) {
3546b384f39SPeter Avalos 			unsigned int checksum;
3556b384f39SPeter Avalos 			checksum = __archive_xxhash.XXH32_digest(
3566b384f39SPeter Avalos 					data->xxh32_state);
3576b384f39SPeter Avalos 			data->xxh32_state = NULL;
3586b384f39SPeter Avalos 			archive_le32enc(data->out, checksum);
3596b384f39SPeter Avalos 			data->out += 4;
3606b384f39SPeter Avalos 		}
3616b384f39SPeter Avalos 		ret = __archive_write_filter(f->next_filter,
3626b384f39SPeter Avalos 			    data->out_buffer, data->out - data->out_buffer);
3636b384f39SPeter Avalos 	}
364085658deSDaniel Fojt 	return ret;
3656b384f39SPeter Avalos }
3666b384f39SPeter Avalos 
3676b384f39SPeter Avalos static int
archive_filter_lz4_free(struct archive_write_filter * f)3686b384f39SPeter Avalos archive_filter_lz4_free(struct archive_write_filter *f)
3696b384f39SPeter Avalos {
3706b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
3716b384f39SPeter Avalos 
3726b384f39SPeter Avalos 	if (data->lz4_stream != NULL) {
3736b384f39SPeter Avalos #ifdef HAVE_LZ4HC_H
3746b384f39SPeter Avalos 		if (data->compression_level >= 3)
3756b384f39SPeter Avalos #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
3766b384f39SPeter Avalos 			LZ4_freeStreamHC(data->lz4_stream);
3776b384f39SPeter Avalos #else
3786b384f39SPeter Avalos 			LZ4_freeHC(data->lz4_stream);
3796b384f39SPeter Avalos #endif
3806b384f39SPeter Avalos 		else
3816b384f39SPeter Avalos #endif
3826b384f39SPeter Avalos #if LZ4_VERSION_MINOR >= 3
3836b384f39SPeter Avalos 			LZ4_freeStream(data->lz4_stream);
3846b384f39SPeter Avalos #else
3856b384f39SPeter Avalos 			LZ4_free(data->lz4_stream);
3866b384f39SPeter Avalos #endif
3876b384f39SPeter Avalos 	}
3886b384f39SPeter Avalos 	free(data->out_buffer);
3896b384f39SPeter Avalos 	free(data->in_buffer_allocated);
3906b384f39SPeter Avalos 	free(data->xxh32_state);
3916b384f39SPeter Avalos 	free(data);
3926b384f39SPeter Avalos 	f->data = NULL;
3936b384f39SPeter Avalos 	return (ARCHIVE_OK);
3946b384f39SPeter Avalos }
3956b384f39SPeter Avalos 
3966b384f39SPeter Avalos static int
lz4_write_stream_descriptor(struct archive_write_filter * f)3976b384f39SPeter Avalos lz4_write_stream_descriptor(struct archive_write_filter *f)
3986b384f39SPeter Avalos {
3996b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
4006b384f39SPeter Avalos 	uint8_t *sd;
4016b384f39SPeter Avalos 
4026b384f39SPeter Avalos 	sd = (uint8_t *)data->out;
4036b384f39SPeter Avalos 	/* Write Magic Number. */
4046b384f39SPeter Avalos 	archive_le32enc(&sd[0], LZ4_MAGICNUMBER);
4056b384f39SPeter Avalos 	/* FLG */
4066b384f39SPeter Avalos 	sd[4] = (data->version_number << 6)
4076b384f39SPeter Avalos 	      | (data->block_independence << 5)
4086b384f39SPeter Avalos 	      | (data->block_checksum << 4)
4096b384f39SPeter Avalos 	      | (data->stream_size << 3)
4106b384f39SPeter Avalos 	      | (data->stream_checksum << 2)
4116b384f39SPeter Avalos 	      | (data->preset_dictionary << 0);
4126b384f39SPeter Avalos 	/* BD */
4136b384f39SPeter Avalos 	sd[5] = (data->block_maximum_size << 4);
4146b384f39SPeter Avalos 	sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff;
4156b384f39SPeter Avalos 	data->out += 7;
4166b384f39SPeter Avalos 	if (data->stream_checksum)
4176b384f39SPeter Avalos 		data->xxh32_state = __archive_xxhash.XXH32_init(0);
4186b384f39SPeter Avalos 	else
4196b384f39SPeter Avalos 		data->xxh32_state = NULL;
4206b384f39SPeter Avalos 	return (ARCHIVE_OK);
4216b384f39SPeter Avalos }
4226b384f39SPeter Avalos 
4236b384f39SPeter Avalos static ssize_t
lz4_write_one_block(struct archive_write_filter * f,const char * p,size_t length)4246b384f39SPeter Avalos lz4_write_one_block(struct archive_write_filter *f, const char *p,
4256b384f39SPeter Avalos     size_t length)
4266b384f39SPeter Avalos {
4276b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
4286b384f39SPeter Avalos 	ssize_t r;
4296b384f39SPeter Avalos 
4306b384f39SPeter Avalos 	if (p == NULL) {
4316b384f39SPeter Avalos 		/* Compress remaining uncompressed data. */
4326b384f39SPeter Avalos 		if (data->in_buffer == data->in)
4336b384f39SPeter Avalos 			return 0;
4346b384f39SPeter Avalos 		else {
4356b384f39SPeter Avalos 			size_t l = data->in - data->in_buffer;
4366b384f39SPeter Avalos 			r = drive_compressor(f, data->in_buffer, l);
4376b384f39SPeter Avalos 			if (r == ARCHIVE_OK)
4386b384f39SPeter Avalos 				r = (ssize_t)l;
4396b384f39SPeter Avalos 		}
4406b384f39SPeter Avalos 	} else if ((data->block_independence || data->compression_level < 3) &&
4416b384f39SPeter Avalos 	    data->in_buffer == data->in && length >= data->block_size) {
4426b384f39SPeter Avalos 		r = drive_compressor(f, p, data->block_size);
4436b384f39SPeter Avalos 		if (r == ARCHIVE_OK)
4446b384f39SPeter Avalos 			r = (ssize_t)data->block_size;
4456b384f39SPeter Avalos 	} else {
4466b384f39SPeter Avalos 		size_t remaining_size = data->in_buffer_size -
4476b384f39SPeter Avalos 			(data->in - data->in_buffer);
4486b384f39SPeter Avalos 		size_t l = (remaining_size > length)? length: remaining_size;
4496b384f39SPeter Avalos 		memcpy(data->in, p, l);
4506b384f39SPeter Avalos 		data->in += l;
4516b384f39SPeter Avalos 		if (l == remaining_size) {
4526b384f39SPeter Avalos 			r = drive_compressor(f, data->in_buffer,
4536b384f39SPeter Avalos 			    data->block_size);
4546b384f39SPeter Avalos 			if (r == ARCHIVE_OK)
4556b384f39SPeter Avalos 				r = (ssize_t)l;
4566b384f39SPeter Avalos 			data->in = data->in_buffer;
4576b384f39SPeter Avalos 		} else
4586b384f39SPeter Avalos 			r = (ssize_t)l;
4596b384f39SPeter Avalos 	}
4606b384f39SPeter Avalos 
4616b384f39SPeter Avalos 	return (r);
4626b384f39SPeter Avalos }
4636b384f39SPeter Avalos 
4646b384f39SPeter Avalos 
4656b384f39SPeter Avalos /*
4666b384f39SPeter Avalos  * Utility function to push input data through compressor, writing
4676b384f39SPeter Avalos  * full output blocks as necessary.
4686b384f39SPeter Avalos  *
4696b384f39SPeter Avalos  * Note that this handles both the regular write case (finishing ==
4706b384f39SPeter Avalos  * false) and the end-of-archive case (finishing == true).
4716b384f39SPeter Avalos  */
4726b384f39SPeter Avalos static int
drive_compressor(struct archive_write_filter * f,const char * p,size_t length)4736b384f39SPeter Avalos drive_compressor(struct archive_write_filter *f, const char *p, size_t length)
4746b384f39SPeter Avalos {
4756b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
4766b384f39SPeter Avalos 
4776b384f39SPeter Avalos 	if (data->stream_checksum)
4786b384f39SPeter Avalos 		__archive_xxhash.XXH32_update(data->xxh32_state,
4796b384f39SPeter Avalos 			p, (int)length);
4806b384f39SPeter Avalos 	if (data->block_independence)
4816b384f39SPeter Avalos 		return drive_compressor_independence(f, p, length);
4826b384f39SPeter Avalos 	else
4836b384f39SPeter Avalos 		return drive_compressor_dependence(f, p, length);
4846b384f39SPeter Avalos }
4856b384f39SPeter Avalos 
4866b384f39SPeter Avalos static int
drive_compressor_independence(struct archive_write_filter * f,const char * p,size_t length)4876b384f39SPeter Avalos drive_compressor_independence(struct archive_write_filter *f, const char *p,
4886b384f39SPeter Avalos     size_t length)
4896b384f39SPeter Avalos {
4906b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
4916b384f39SPeter Avalos 	unsigned int outsize;
4926b384f39SPeter Avalos 
4936b384f39SPeter Avalos #ifdef HAVE_LZ4HC_H
4946b384f39SPeter Avalos 	if (data->compression_level >= 3)
4956b384f39SPeter Avalos #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
4966b384f39SPeter Avalos 		outsize = LZ4_compress_HC(p, data->out + 4,
4976b384f39SPeter Avalos 		     (int)length, (int)data->block_size,
4986b384f39SPeter Avalos 		    data->compression_level);
4996b384f39SPeter Avalos #else
5006b384f39SPeter Avalos 		outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4,
5016b384f39SPeter Avalos 		    (int)length, (int)data->block_size,
5026b384f39SPeter Avalos 		    data->compression_level);
5036b384f39SPeter Avalos #endif
5046b384f39SPeter Avalos 	else
5056b384f39SPeter Avalos #endif
5066b384f39SPeter Avalos #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
5076b384f39SPeter Avalos 		outsize = LZ4_compress_default(p, data->out + 4,
5086b384f39SPeter Avalos 		    (int)length, (int)data->block_size);
5096b384f39SPeter Avalos #else
5106b384f39SPeter Avalos 		outsize = LZ4_compress_limitedOutput(p, data->out + 4,
5116b384f39SPeter Avalos 		    (int)length, (int)data->block_size);
5126b384f39SPeter Avalos #endif
5136b384f39SPeter Avalos 
5146b384f39SPeter Avalos 	if (outsize) {
5156b384f39SPeter Avalos 		/* The buffer is compressed. */
5166b384f39SPeter Avalos 		archive_le32enc(data->out, outsize);
5176b384f39SPeter Avalos 		data->out += 4;
5186b384f39SPeter Avalos 	} else {
519e95abc47Szrj 		/* The buffer is not compressed. The compressed size was
5206b384f39SPeter Avalos 		 * bigger than its uncompressed size. */
5216b384f39SPeter Avalos 		archive_le32enc(data->out, length | 0x80000000);
5226b384f39SPeter Avalos 		data->out += 4;
5236b384f39SPeter Avalos 		memcpy(data->out, p, length);
5246b384f39SPeter Avalos 		outsize = length;
5256b384f39SPeter Avalos 	}
5266b384f39SPeter Avalos 	data->out += outsize;
5276b384f39SPeter Avalos 	if (data->block_checksum) {
5286b384f39SPeter Avalos 		unsigned int checksum =
5296b384f39SPeter Avalos 		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
5306b384f39SPeter Avalos 		archive_le32enc(data->out, checksum);
5316b384f39SPeter Avalos 		data->out += 4;
5326b384f39SPeter Avalos 	}
5336b384f39SPeter Avalos 	return (ARCHIVE_OK);
5346b384f39SPeter Avalos }
5356b384f39SPeter Avalos 
5366b384f39SPeter Avalos static int
drive_compressor_dependence(struct archive_write_filter * f,const char * p,size_t length)5376b384f39SPeter Avalos drive_compressor_dependence(struct archive_write_filter *f, const char *p,
5386b384f39SPeter Avalos     size_t length)
5396b384f39SPeter Avalos {
5406b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
5416b384f39SPeter Avalos 	int outsize;
5426b384f39SPeter Avalos 
5436b384f39SPeter Avalos #define DICT_SIZE	(64 * 1024)
5446b384f39SPeter Avalos #ifdef HAVE_LZ4HC_H
5456b384f39SPeter Avalos 	if (data->compression_level >= 3) {
5466b384f39SPeter Avalos 		if (data->lz4_stream == NULL) {
5476b384f39SPeter Avalos #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
5486b384f39SPeter Avalos 			data->lz4_stream = LZ4_createStreamHC();
5496b384f39SPeter Avalos 			LZ4_resetStreamHC(data->lz4_stream, data->compression_level);
5506b384f39SPeter Avalos #else
5516b384f39SPeter Avalos 			data->lz4_stream =
5526b384f39SPeter Avalos 			    LZ4_createHC(data->in_buffer_allocated);
5536b384f39SPeter Avalos #endif
5546b384f39SPeter Avalos 			if (data->lz4_stream == NULL) {
5556b384f39SPeter Avalos 				archive_set_error(f->archive, ENOMEM,
5566b384f39SPeter Avalos 				    "Can't allocate data for compression"
5576b384f39SPeter Avalos 				    " buffer");
5586b384f39SPeter Avalos 				return (ARCHIVE_FATAL);
5596b384f39SPeter Avalos 			}
5606b384f39SPeter Avalos 		}
5616b384f39SPeter Avalos 		else
5626b384f39SPeter Avalos 			LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
5636b384f39SPeter Avalos 
5646b384f39SPeter Avalos #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
5656b384f39SPeter Avalos 		outsize = LZ4_compress_HC_continue(
5666b384f39SPeter Avalos 		    data->lz4_stream, p, data->out + 4, (int)length,
5676b384f39SPeter Avalos 		    (int)data->block_size);
5686b384f39SPeter Avalos #else
5696b384f39SPeter Avalos 		outsize = LZ4_compressHC2_limitedOutput_continue(
5706b384f39SPeter Avalos 		    data->lz4_stream, p, data->out + 4, (int)length,
5716b384f39SPeter Avalos 		    (int)data->block_size, data->compression_level);
5726b384f39SPeter Avalos #endif
5736b384f39SPeter Avalos 	} else
5746b384f39SPeter Avalos #endif
5756b384f39SPeter Avalos 	{
5766b384f39SPeter Avalos 		if (data->lz4_stream == NULL) {
5776b384f39SPeter Avalos 			data->lz4_stream = LZ4_createStream();
5786b384f39SPeter Avalos 			if (data->lz4_stream == NULL) {
5796b384f39SPeter Avalos 				archive_set_error(f->archive, ENOMEM,
5806b384f39SPeter Avalos 				    "Can't allocate data for compression"
5816b384f39SPeter Avalos 				    " buffer");
5826b384f39SPeter Avalos 				return (ARCHIVE_FATAL);
5836b384f39SPeter Avalos 			}
5846b384f39SPeter Avalos 		}
5856b384f39SPeter Avalos 		else
5866b384f39SPeter Avalos 			LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
5876b384f39SPeter Avalos 
5886b384f39SPeter Avalos #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
5896b384f39SPeter Avalos 		outsize = LZ4_compress_fast_continue(
5906b384f39SPeter Avalos 		    data->lz4_stream, p, data->out + 4, (int)length,
5916b384f39SPeter Avalos 		    (int)data->block_size, 1);
5926b384f39SPeter Avalos #else
5936b384f39SPeter Avalos 		outsize = LZ4_compress_limitedOutput_continue(
5946b384f39SPeter Avalos 		    data->lz4_stream, p, data->out + 4, (int)length,
5956b384f39SPeter Avalos 		    (int)data->block_size);
5966b384f39SPeter Avalos #endif
5976b384f39SPeter Avalos 	}
5986b384f39SPeter Avalos 
5996b384f39SPeter Avalos 	if (outsize) {
6006b384f39SPeter Avalos 		/* The buffer is compressed. */
6016b384f39SPeter Avalos 		archive_le32enc(data->out, outsize);
6026b384f39SPeter Avalos 		data->out += 4;
6036b384f39SPeter Avalos 	} else {
604e95abc47Szrj 		/* The buffer is not compressed. The compressed size was
6056b384f39SPeter Avalos 		 * bigger than its uncompressed size. */
6066b384f39SPeter Avalos 		archive_le32enc(data->out, length | 0x80000000);
6076b384f39SPeter Avalos 		data->out += 4;
6086b384f39SPeter Avalos 		memcpy(data->out, p, length);
6096b384f39SPeter Avalos 		outsize = length;
6106b384f39SPeter Avalos 	}
6116b384f39SPeter Avalos 	data->out += outsize;
6126b384f39SPeter Avalos 	if (data->block_checksum) {
6136b384f39SPeter Avalos 		unsigned int checksum =
6146b384f39SPeter Avalos 		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
6156b384f39SPeter Avalos 		archive_le32enc(data->out, checksum);
6166b384f39SPeter Avalos 		data->out += 4;
6176b384f39SPeter Avalos 	}
6186b384f39SPeter Avalos 
6196b384f39SPeter Avalos 	if (length == data->block_size) {
6206b384f39SPeter Avalos #ifdef HAVE_LZ4HC_H
6216b384f39SPeter Avalos 		if (data->compression_level >= 3) {
6226b384f39SPeter Avalos #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
6236b384f39SPeter Avalos 			LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
6246b384f39SPeter Avalos #else
6256b384f39SPeter Avalos 			LZ4_slideInputBufferHC(data->lz4_stream);
6266b384f39SPeter Avalos #endif
6276b384f39SPeter Avalos 			data->in_buffer = data->in_buffer_allocated + DICT_SIZE;
6286b384f39SPeter Avalos 		}
6296b384f39SPeter Avalos 		else
6306b384f39SPeter Avalos #endif
6316b384f39SPeter Avalos 			LZ4_saveDict(data->lz4_stream,
6326b384f39SPeter Avalos 			    data->in_buffer_allocated, DICT_SIZE);
6336b384f39SPeter Avalos #undef DICT_SIZE
6346b384f39SPeter Avalos 	}
6356b384f39SPeter Avalos 	return (ARCHIVE_OK);
6366b384f39SPeter Avalos }
6376b384f39SPeter Avalos 
6386b384f39SPeter Avalos #else /* HAVE_LIBLZ4 */
6396b384f39SPeter Avalos 
6406b384f39SPeter Avalos static int
archive_filter_lz4_open(struct archive_write_filter * f)6416b384f39SPeter Avalos archive_filter_lz4_open(struct archive_write_filter *f)
6426b384f39SPeter Avalos {
6436b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
6446b384f39SPeter Avalos 	struct archive_string as;
6456b384f39SPeter Avalos 	int r;
6466b384f39SPeter Avalos 
6476b384f39SPeter Avalos 	archive_string_init(&as);
6486b384f39SPeter Avalos 	archive_strcpy(&as, "lz4 -z -q -q");
6496b384f39SPeter Avalos 
6506b384f39SPeter Avalos 	/* Specify a compression level. */
6516b384f39SPeter Avalos 	if (data->compression_level > 0) {
6526b384f39SPeter Avalos 		archive_strcat(&as, " -");
6536b384f39SPeter Avalos 		archive_strappend_char(&as, '0' + data->compression_level);
6546b384f39SPeter Avalos 	}
6556b384f39SPeter Avalos 	/* Specify a block size. */
6566b384f39SPeter Avalos 	archive_strcat(&as, " -B");
6576b384f39SPeter Avalos 	archive_strappend_char(&as, '0' + data->block_maximum_size);
6586b384f39SPeter Avalos 
6596b384f39SPeter Avalos 	if (data->block_checksum)
6606b384f39SPeter Avalos 		archive_strcat(&as, " -BX");
6616b384f39SPeter Avalos 	if (data->stream_checksum == 0)
6626b384f39SPeter Avalos 		archive_strcat(&as, " --no-frame-crc");
6636b384f39SPeter Avalos 	if (data->block_independence == 0)
6646b384f39SPeter Avalos 		archive_strcat(&as, " -BD");
6656b384f39SPeter Avalos 
6666b384f39SPeter Avalos 	f->write = archive_filter_lz4_write;
6676b384f39SPeter Avalos 
6686b384f39SPeter Avalos 	r = __archive_write_program_open(f, data->pdata, as.s);
6696b384f39SPeter Avalos 	archive_string_free(&as);
6706b384f39SPeter Avalos 	return (r);
6716b384f39SPeter Avalos }
6726b384f39SPeter Avalos 
6736b384f39SPeter Avalos static int
archive_filter_lz4_write(struct archive_write_filter * f,const void * buff,size_t length)6746b384f39SPeter Avalos archive_filter_lz4_write(struct archive_write_filter *f, const void *buff,
6756b384f39SPeter Avalos     size_t length)
6766b384f39SPeter Avalos {
6776b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
6786b384f39SPeter Avalos 
6796b384f39SPeter Avalos 	return __archive_write_program_write(f, data->pdata, buff, length);
6806b384f39SPeter Avalos }
6816b384f39SPeter Avalos 
6826b384f39SPeter Avalos static int
archive_filter_lz4_close(struct archive_write_filter * f)6836b384f39SPeter Avalos archive_filter_lz4_close(struct archive_write_filter *f)
6846b384f39SPeter Avalos {
6856b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
6866b384f39SPeter Avalos 
6876b384f39SPeter Avalos 	return __archive_write_program_close(f, data->pdata);
6886b384f39SPeter Avalos }
6896b384f39SPeter Avalos 
6906b384f39SPeter Avalos static int
archive_filter_lz4_free(struct archive_write_filter * f)6916b384f39SPeter Avalos archive_filter_lz4_free(struct archive_write_filter *f)
6926b384f39SPeter Avalos {
6936b384f39SPeter Avalos 	struct private_data *data = (struct private_data *)f->data;
6946b384f39SPeter Avalos 
6956b384f39SPeter Avalos 	__archive_write_program_free(data->pdata);
6966b384f39SPeter Avalos 	free(data);
6976b384f39SPeter Avalos 	return (ARCHIVE_OK);
6986b384f39SPeter Avalos }
6996b384f39SPeter Avalos 
7006b384f39SPeter Avalos #endif /* HAVE_LIBLZ4 */
701