]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/archive_read_support_filter_zstd.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / libarchive / libarchive / archive_read_support_filter_zstd.c
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$");
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
58 struct 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. */
68 static ssize_t  zstd_filter_read(struct archive_read_filter *, const void**);
69 static 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  */
78 static int      zstd_bidder_bid(struct archive_read_filter_bidder *,
79                     struct archive_read_filter *);
80 static int      zstd_bidder_init(struct archive_read_filter *);
81
82 int
83 archive_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  */
112 static int
113 zstd_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  */
147 static int
148 zstd_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  */
166 static int
167 zstd_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
205 static ssize_t
206 zstd_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  */
282 static int
283 zstd_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 */