1/*-
2 * Copyright (c) 2009-2011 Sean Purcell
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: stable/11/contrib/libarchive/libarchive/archive_read_support_filter_zstd.c 362133 2020-06-12 23:01:29Z mm $");
29
30#ifdef HAVE_ERRNO_H
31#include <errno.h>
32#endif
33
34#ifdef HAVE_ERRNO_H
35#include <errno.h>
36#endif
37#include <stdio.h>
38#ifdef HAVE_STDLIB_H
39#include <stdlib.h>
40#endif
41#ifdef HAVE_STRING_H
42#include <string.h>
43#endif
44#ifdef HAVE_UNISTD_H
45#include <unistd.h>
46#endif
47#if HAVE_ZSTD_H
48#include <zstd.h>
49#endif
50
51#include "archive.h"
52#include "archive_endian.h"
53#include "archive_private.h"
54#include "archive_read_private.h"
55
56#if HAVE_ZSTD_H && HAVE_LIBZSTD
57
58struct private_data {
59	ZSTD_DStream	*dstream;
60	unsigned char	*out_block;
61	size_t		 out_block_size;
62	int64_t		 total_out;
63	char		 in_frame; /* True = in the middle of a zstd frame. */
64	char		 eof; /* True = found end of compressed data. */
65};
66
67/* Zstd Filter. */
68static ssize_t	zstd_filter_read(struct archive_read_filter *, const void**);
69static int	zstd_filter_close(struct archive_read_filter *);
70#endif
71
72/*
73 * Note that we can detect zstd compressed files even if we can't decompress
74 * them.  (In fact, we like detecting them because we can give better error
75 * messages.)  So the bid framework here gets compiled even if no zstd library
76 * is available.
77 */
78static int	zstd_bidder_bid(struct archive_read_filter_bidder *,
79		    struct archive_read_filter *);
80static int	zstd_bidder_init(struct archive_read_filter *);
81
82int
83archive_read_support_filter_zstd(struct archive *_a)
84{
85	struct archive_read *a = (struct archive_read *)_a;
86	struct archive_read_filter_bidder *bidder;
87
88	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
89	    ARCHIVE_STATE_NEW, "archive_read_support_filter_zstd");
90
91	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
92		return (ARCHIVE_FATAL);
93
94	bidder->data = NULL;
95	bidder->name = "zstd";
96	bidder->bid = zstd_bidder_bid;
97	bidder->init = zstd_bidder_init;
98	bidder->options = NULL;
99	bidder->free = NULL;
100#if HAVE_ZSTD_H && HAVE_LIBZSTD
101	return (ARCHIVE_OK);
102#else
103	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
104	    "Using external zstd program for zstd decompression");
105	return (ARCHIVE_WARN);
106#endif
107}
108
109/*
110 * Test whether we can handle this data.
111 */
112static int
113zstd_bidder_bid(struct archive_read_filter_bidder *self,
114    struct archive_read_filter *filter)
115{
116	const unsigned char *buffer;
117	ssize_t avail;
118	unsigned prefix;
119
120	/* Zstd frame magic values */
121	const unsigned zstd_magic = 0xFD2FB528U;
122	const unsigned zstd_magic_skippable_start = 0x184D2A50U;
123	const unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;
124
125	(void) self; /* UNUSED */
126
127	buffer = __archive_read_filter_ahead(filter, 4, &avail);
128	if (buffer == NULL)
129		return (0);
130
131	prefix = archive_le32dec(buffer);
132	if (prefix == zstd_magic)
133		return (32);
134	if ((prefix & zstd_magic_skippable_mask) == zstd_magic_skippable_start)
135		return (32);
136
137	return (0);
138}
139
140#if !(HAVE_ZSTD_H && HAVE_LIBZSTD)
141
142/*
143 * If we don't have the library on this system, we can't do the
144 * decompression directly.  We can, however, try to run "zstd -d"
145 * in case that's available.
146 */
147static int
148zstd_bidder_init(struct archive_read_filter *self)
149{
150	int r;
151
152	r = __archive_read_program(self, "zstd -d -qq");
153	/* Note: We set the format here even if __archive_read_program()
154	 * above fails.  We do, after all, know what the format is
155	 * even if we weren't able to read it. */
156	self->code = ARCHIVE_FILTER_ZSTD;
157	self->name = "zstd";
158	return (r);
159}
160
161#else
162
163/*
164 * Initialize the filter object
165 */
166static int
167zstd_bidder_init(struct archive_read_filter *self)
168{
169	struct private_data *state;
170	const size_t out_block_size = ZSTD_DStreamOutSize();
171	void *out_block;
172	ZSTD_DStream *dstream;
173
174	self->code = ARCHIVE_FILTER_ZSTD;
175	self->name = "zstd";
176
177	state = (struct private_data *)calloc(sizeof(*state), 1);
178	out_block = (unsigned char *)malloc(out_block_size);
179	dstream = ZSTD_createDStream();
180
181	if (state == NULL || out_block == NULL || dstream == NULL) {
182		free(out_block);
183		free(state);
184		ZSTD_freeDStream(dstream); /* supports free on NULL */
185		archive_set_error(&self->archive->archive, ENOMEM,
186		    "Can't allocate data for zstd decompression");
187		return (ARCHIVE_FATAL);
188	}
189
190	self->data = state;
191
192	state->out_block_size = out_block_size;
193	state->out_block = out_block;
194	state->dstream = dstream;
195	self->read = zstd_filter_read;
196	self->skip = NULL; /* not supported */
197	self->close = zstd_filter_close;
198
199	state->eof = 0;
200	state->in_frame = 0;
201
202	return (ARCHIVE_OK);
203}
204
205static ssize_t
206zstd_filter_read(struct archive_read_filter *self, const void **p)
207{
208	struct private_data *state;
209	size_t decompressed;
210	ssize_t avail_in;
211	ZSTD_outBuffer out;
212	ZSTD_inBuffer in;
213
214	state = (struct private_data *)self->data;
215
216	out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 };
217
218	/* Try to fill the output buffer. */
219	while (out.pos < out.size && !state->eof) {
220		if (!state->in_frame) {
221			const size_t ret = ZSTD_initDStream(state->dstream);
222			if (ZSTD_isError(ret)) {
223				archive_set_error(&self->archive->archive,
224				    ARCHIVE_ERRNO_MISC,
225				    "Error initializing zstd decompressor: %s",
226				    ZSTD_getErrorName(ret));
227				return (ARCHIVE_FATAL);
228			}
229		}
230		in.src = __archive_read_filter_ahead(self->upstream, 1,
231		    &avail_in);
232		if (avail_in < 0) {
233			return avail_in;
234		}
235		if (in.src == NULL && avail_in == 0) {
236			if (!state->in_frame) {
237				/* end of stream */
238				state->eof = 1;
239				break;
240			} else {
241				archive_set_error(&self->archive->archive,
242				    ARCHIVE_ERRNO_MISC,
243				    "Truncated zstd input");
244				return (ARCHIVE_FATAL);
245			}
246		}
247		in.size = avail_in;
248		in.pos = 0;
249
250		{
251			const size_t ret =
252			    ZSTD_decompressStream(state->dstream, &out, &in);
253
254			if (ZSTD_isError(ret)) {
255				archive_set_error(&self->archive->archive,
256				    ARCHIVE_ERRNO_MISC,
257				    "Zstd decompression failed: %s",
258				    ZSTD_getErrorName(ret));
259				return (ARCHIVE_FATAL);
260			}
261
262			/* Decompressor made some progress */
263			__archive_read_filter_consume(self->upstream, in.pos);
264
265			/* ret guaranteed to be > 0 if frame isn't done yet */
266			state->in_frame = (ret != 0);
267		}
268	}
269
270	decompressed = out.pos;
271	state->total_out += decompressed;
272	if (decompressed == 0)
273		*p = NULL;
274	else
275		*p = state->out_block;
276	return (decompressed);
277}
278
279/*
280 * Clean up the decompressor.
281 */
282static int
283zstd_filter_close(struct archive_read_filter *self)
284{
285	struct private_data *state;
286
287	state = (struct private_data *)self->data;
288
289	ZSTD_freeDStream(state->dstream);
290	free(state->out_block);
291	free(state);
292
293	return (ARCHIVE_OK);
294}
295
296#endif /* HAVE_ZLIB_H && HAVE_LIBZSTD */
297