1324145Smm/*-
2324145Smm * Copyright (c) 2009-2011 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_read_support_filter_zstd.c 362134 2020-06-12 23:02:34Z mm $");
29324145Smm
30324145Smm#ifdef HAVE_ERRNO_H
31324145Smm#include <errno.h>
32324145Smm#endif
33324145Smm
34324145Smm#ifdef HAVE_ERRNO_H
35324145Smm#include <errno.h>
36324145Smm#endif
37324145Smm#include <stdio.h>
38324145Smm#ifdef HAVE_STDLIB_H
39324145Smm#include <stdlib.h>
40324145Smm#endif
41324145Smm#ifdef HAVE_STRING_H
42324145Smm#include <string.h>
43324145Smm#endif
44324145Smm#ifdef HAVE_UNISTD_H
45324145Smm#include <unistd.h>
46324145Smm#endif
47324145Smm#if HAVE_ZSTD_H
48324145Smm#include <zstd.h>
49324145Smm#endif
50324145Smm
51324145Smm#include "archive.h"
52324145Smm#include "archive_endian.h"
53324145Smm#include "archive_private.h"
54324145Smm#include "archive_read_private.h"
55324145Smm
56324145Smm#if HAVE_ZSTD_H && HAVE_LIBZSTD
57324145Smm
58324145Smmstruct private_data {
59324145Smm	ZSTD_DStream	*dstream;
60324145Smm	unsigned char	*out_block;
61324145Smm	size_t		 out_block_size;
62324145Smm	int64_t		 total_out;
63324145Smm	char		 in_frame; /* True = in the middle of a zstd frame. */
64324145Smm	char		 eof; /* True = found end of compressed data. */
65324145Smm};
66324145Smm
67324145Smm/* Zstd Filter. */
68324145Smmstatic ssize_t	zstd_filter_read(struct archive_read_filter *, const void**);
69324145Smmstatic int	zstd_filter_close(struct archive_read_filter *);
70324145Smm#endif
71324145Smm
72324145Smm/*
73324145Smm * Note that we can detect zstd compressed files even if we can't decompress
74324145Smm * them.  (In fact, we like detecting them because we can give better error
75324145Smm * messages.)  So the bid framework here gets compiled even if no zstd library
76324145Smm * is available.
77324145Smm */
78324145Smmstatic int	zstd_bidder_bid(struct archive_read_filter_bidder *,
79324145Smm		    struct archive_read_filter *);
80324145Smmstatic int	zstd_bidder_init(struct archive_read_filter *);
81324145Smm
82324145Smmint
83324145Smmarchive_read_support_filter_zstd(struct archive *_a)
84324145Smm{
85324145Smm	struct archive_read *a = (struct archive_read *)_a;
86324145Smm	struct archive_read_filter_bidder *bidder;
87324145Smm
88324145Smm	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
89324145Smm	    ARCHIVE_STATE_NEW, "archive_read_support_filter_zstd");
90324145Smm
91324145Smm	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
92324145Smm		return (ARCHIVE_FATAL);
93324145Smm
94324145Smm	bidder->data = NULL;
95324145Smm	bidder->name = "zstd";
96324145Smm	bidder->bid = zstd_bidder_bid;
97324145Smm	bidder->init = zstd_bidder_init;
98324145Smm	bidder->options = NULL;
99324145Smm	bidder->free = NULL;
100324145Smm#if HAVE_ZSTD_H && HAVE_LIBZSTD
101324145Smm	return (ARCHIVE_OK);
102324145Smm#else
103324145Smm	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
104324145Smm	    "Using external zstd program for zstd decompression");
105324145Smm	return (ARCHIVE_WARN);
106324145Smm#endif
107324145Smm}
108324145Smm
109324145Smm/*
110324145Smm * Test whether we can handle this data.
111324145Smm */
112324145Smmstatic int
113324145Smmzstd_bidder_bid(struct archive_read_filter_bidder *self,
114324145Smm    struct archive_read_filter *filter)
115324145Smm{
116324145Smm	const unsigned char *buffer;
117324145Smm	ssize_t avail;
118324145Smm	unsigned prefix;
119324145Smm
120324145Smm	/* Zstd frame magic values */
121324145Smm	const unsigned zstd_magic = 0xFD2FB528U;
122362134Smm	const unsigned zstd_magic_skippable_start = 0x184D2A50U;
123362134Smm	const unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;
124324145Smm
125324145Smm	(void) self; /* UNUSED */
126324145Smm
127324145Smm	buffer = __archive_read_filter_ahead(filter, 4, &avail);
128324145Smm	if (buffer == NULL)
129324145Smm		return (0);
130324145Smm
131324145Smm	prefix = archive_le32dec(buffer);
132324145Smm	if (prefix == zstd_magic)
133324145Smm		return (32);
134362134Smm	if ((prefix & zstd_magic_skippable_mask) == zstd_magic_skippable_start)
135362134Smm		return (32);
136324145Smm
137324145Smm	return (0);
138324145Smm}
139324145Smm
140324145Smm#if !(HAVE_ZSTD_H && HAVE_LIBZSTD)
141324145Smm
142324145Smm/*
143324145Smm * If we don't have the library on this system, we can't do the
144324145Smm * decompression directly.  We can, however, try to run "zstd -d"
145324145Smm * in case that's available.
146324145Smm */
147324145Smmstatic int
148324145Smmzstd_bidder_init(struct archive_read_filter *self)
149324145Smm{
150324145Smm	int r;
151324145Smm
152324145Smm	r = __archive_read_program(self, "zstd -d -qq");
153324145Smm	/* Note: We set the format here even if __archive_read_program()
154324145Smm	 * above fails.  We do, after all, know what the format is
155324145Smm	 * even if we weren't able to read it. */
156324145Smm	self->code = ARCHIVE_FILTER_ZSTD;
157324145Smm	self->name = "zstd";
158324145Smm	return (r);
159324145Smm}
160324145Smm
161324145Smm#else
162324145Smm
163324145Smm/*
164324145Smm * Initialize the filter object
165324145Smm */
166324145Smmstatic int
167324145Smmzstd_bidder_init(struct archive_read_filter *self)
168324145Smm{
169324145Smm	struct private_data *state;
170324145Smm	const size_t out_block_size = ZSTD_DStreamOutSize();
171324145Smm	void *out_block;
172324145Smm	ZSTD_DStream *dstream;
173324145Smm
174324145Smm	self->code = ARCHIVE_FILTER_ZSTD;
175324145Smm	self->name = "zstd";
176324145Smm
177324145Smm	state = (struct private_data *)calloc(sizeof(*state), 1);
178324145Smm	out_block = (unsigned char *)malloc(out_block_size);
179324145Smm	dstream = ZSTD_createDStream();
180324145Smm
181324145Smm	if (state == NULL || out_block == NULL || dstream == NULL) {
182324145Smm		free(out_block);
183324145Smm		free(state);
184324145Smm		ZSTD_freeDStream(dstream); /* supports free on NULL */
185324145Smm		archive_set_error(&self->archive->archive, ENOMEM,
186324145Smm		    "Can't allocate data for zstd decompression");
187324145Smm		return (ARCHIVE_FATAL);
188324145Smm	}
189324145Smm
190324145Smm	self->data = state;
191324145Smm
192324145Smm	state->out_block_size = out_block_size;
193324145Smm	state->out_block = out_block;
194324145Smm	state->dstream = dstream;
195324145Smm	self->read = zstd_filter_read;
196324145Smm	self->skip = NULL; /* not supported */
197324145Smm	self->close = zstd_filter_close;
198324145Smm
199324145Smm	state->eof = 0;
200324145Smm	state->in_frame = 0;
201324145Smm
202324145Smm	return (ARCHIVE_OK);
203324145Smm}
204324145Smm
205324145Smmstatic ssize_t
206324145Smmzstd_filter_read(struct archive_read_filter *self, const void **p)
207324145Smm{
208324145Smm	struct private_data *state;
209324145Smm	size_t decompressed;
210324145Smm	ssize_t avail_in;
211324145Smm	ZSTD_outBuffer out;
212324145Smm	ZSTD_inBuffer in;
213324145Smm
214324145Smm	state = (struct private_data *)self->data;
215324145Smm
216324145Smm	out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 };
217324145Smm
218324145Smm	/* Try to fill the output buffer. */
219324145Smm	while (out.pos < out.size && !state->eof) {
220324145Smm		if (!state->in_frame) {
221324145Smm			const size_t ret = ZSTD_initDStream(state->dstream);
222324145Smm			if (ZSTD_isError(ret)) {
223324145Smm				archive_set_error(&self->archive->archive,
224324145Smm				    ARCHIVE_ERRNO_MISC,
225324145Smm				    "Error initializing zstd decompressor: %s",
226324145Smm				    ZSTD_getErrorName(ret));
227324145Smm				return (ARCHIVE_FATAL);
228324145Smm			}
229324145Smm		}
230324145Smm		in.src = __archive_read_filter_ahead(self->upstream, 1,
231324145Smm		    &avail_in);
232324145Smm		if (avail_in < 0) {
233324145Smm			return avail_in;
234324145Smm		}
235324145Smm		if (in.src == NULL && avail_in == 0) {
236324145Smm			if (!state->in_frame) {
237324145Smm				/* end of stream */
238324145Smm				state->eof = 1;
239324145Smm				break;
240324145Smm			} else {
241324145Smm				archive_set_error(&self->archive->archive,
242324145Smm				    ARCHIVE_ERRNO_MISC,
243324145Smm				    "Truncated zstd input");
244324145Smm				return (ARCHIVE_FATAL);
245324145Smm			}
246324145Smm		}
247324145Smm		in.size = avail_in;
248324145Smm		in.pos = 0;
249324145Smm
250324145Smm		{
251324145Smm			const size_t ret =
252324145Smm			    ZSTD_decompressStream(state->dstream, &out, &in);
253324145Smm
254324145Smm			if (ZSTD_isError(ret)) {
255324145Smm				archive_set_error(&self->archive->archive,
256324145Smm				    ARCHIVE_ERRNO_MISC,
257324145Smm				    "Zstd decompression failed: %s",
258324145Smm				    ZSTD_getErrorName(ret));
259324145Smm				return (ARCHIVE_FATAL);
260324145Smm			}
261324145Smm
262324145Smm			/* Decompressor made some progress */
263324145Smm			__archive_read_filter_consume(self->upstream, in.pos);
264324145Smm
265324145Smm			/* ret guaranteed to be > 0 if frame isn't done yet */
266324145Smm			state->in_frame = (ret != 0);
267324145Smm		}
268324145Smm	}
269324145Smm
270324145Smm	decompressed = out.pos;
271324145Smm	state->total_out += decompressed;
272324145Smm	if (decompressed == 0)
273324145Smm		*p = NULL;
274324145Smm	else
275324145Smm		*p = state->out_block;
276324145Smm	return (decompressed);
277324145Smm}
278324145Smm
279324145Smm/*
280324145Smm * Clean up the decompressor.
281324145Smm */
282324145Smmstatic int
283324145Smmzstd_filter_close(struct archive_read_filter *self)
284324145Smm{
285324145Smm	struct private_data *state;
286324145Smm
287324145Smm	state = (struct private_data *)self->data;
288324145Smm
289324145Smm	ZSTD_freeDStream(state->dstream);
290324145Smm	free(state->out_block);
291324145Smm	free(state);
292324145Smm
293324145Smm	return (ARCHIVE_OK);
294324145Smm}
295324145Smm
296324145Smm#endif /* HAVE_ZLIB_H && HAVE_LIBZSTD */
297