]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/archive_write_add_filter_lzop.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / libarchive / libarchive / archive_write_add_filter_lzop.c
1 /*-
2  * Copyright (c) 2012 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 //#undef HAVE_LZO_LZOCONF_H
30 //#undef HAVE_LZO_LZO1X_H
31
32 #ifdef HAVE_ERRNO_H
33 #include <errno.h>
34 #endif
35 #ifdef HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif
38 #ifdef HAVE_STRING_H
39 #include <string.h>
40 #endif
41 #include <time.h>
42 #ifdef HAVE_LZO_LZOCONF_H
43 #include <lzo/lzoconf.h>
44 #endif
45 #ifdef HAVE_LZO_LZO1X_H
46 #include <lzo/lzo1x.h>
47 #endif
48
49 #include "archive.h"
50 #include "archive_string.h"
51 #include "archive_endian.h"
52 #include "archive_write_private.h"
53
54 enum lzo_method {
55         METHOD_LZO1X_1 = 1,
56         METHOD_LZO1X_1_15 = 2,
57         METHOD_LZO1X_999 = 3
58 };
59 struct write_lzop {
60         int compression_level;
61 #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
62         unsigned char   *uncompressed;
63         size_t           uncompressed_buffer_size;
64         size_t           uncompressed_avail_bytes;
65         unsigned char   *compressed;
66         size_t           compressed_buffer_size;
67         enum lzo_method  method;
68         unsigned char    level;
69         lzo_voidp        work_buffer;
70         lzo_uint32       work_buffer_size;
71         char             header_written;
72 #else
73         struct archive_write_program_data *pdata;
74 #endif
75 };
76
77 static int archive_write_lzop_open(struct archive_write_filter *);
78 static int archive_write_lzop_options(struct archive_write_filter *,
79                     const char *, const char *);
80 static int archive_write_lzop_write(struct archive_write_filter *,
81                     const void *, size_t);
82 static int archive_write_lzop_close(struct archive_write_filter *);
83 static int archive_write_lzop_free(struct archive_write_filter *);
84
85 #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
86 /* Maximum block size. */
87 #define BLOCK_SIZE                      (256 * 1024)
88 /* Block information is composed of uncompressed size(4 bytes),
89  * compressed size(4 bytes) and the checksum of uncompressed data(4 bytes)
90  * in this lzop writer. */
91 #define BLOCK_INfO_SIZE                 12
92
93 #define HEADER_VERSION                  9
94 #define HEADER_LIBVERSION               11
95 #define HEADER_METHOD                   15
96 #define HEADER_LEVEL                    16
97 #define HEADER_MTIME_LOW                25
98 #define HEADER_MTIME_HIGH               29
99 #define HEADER_H_CHECKSUM               34
100
101 /*
102  * Header template.
103  */
104 static const unsigned char header[] = {
105         /* LZOP Magic code 9 bytes */
106         0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a,
107         /* LZOP utility version(fake data) 2 bytes */
108         0x10, 0x30,
109         /* LZO library version 2 bytes */
110         0x09, 0x40,
111         /* Minimum required LZO library version 2 bytes */
112         0x09, 0x40,
113         /* Method */
114         1,
115         /* Level */
116         5,
117         /* Flags 4 bytes
118          *  -OS Unix
119          *  -Stdout
120          *  -Stdin
121          *  -Adler32 used for uncompressed data 4 bytes */
122         0x03, 0x00, 0x00, 0x0d,
123         /* Mode (AE_IFREG | 0644) 4 bytes */
124         0x00, 0x00, 0x81, 0xa4,
125         /* Mtime low 4 bytes */
126         0x00, 0x00, 0x00, 0x00,
127         /* Mtime high 4 bytes */
128         0x00, 0x00, 0x00, 0x00,
129         /* Filename length */
130         0x00,
131         /* Header checksum 4 bytes */
132         0x00, 0x00, 0x00, 0x00,
133 };
134 #endif
135
136 int
137 archive_write_add_filter_lzop(struct archive *_a)
138 {
139         struct archive_write_filter *f = __archive_write_allocate_filter(_a);
140         struct write_lzop *data;
141
142         archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
143             ARCHIVE_STATE_NEW, "archive_write_add_filter_lzop");
144
145         data = calloc(1, sizeof(*data));
146         if (data == NULL) {
147                 archive_set_error(_a, ENOMEM, "Can't allocate memory");
148                 return (ARCHIVE_FATAL);
149         }
150
151         f->name = "lzop";
152         f->code = ARCHIVE_FILTER_LZOP;
153         f->data = data;
154         f->open = archive_write_lzop_open;
155         f->options = archive_write_lzop_options;
156         f->write = archive_write_lzop_write;
157         f->close = archive_write_lzop_close;
158         f->free = archive_write_lzop_free;
159 #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
160         if (lzo_init() != LZO_E_OK) {
161                 free(data);
162                 archive_set_error(_a, ARCHIVE_ERRNO_MISC,
163                     "lzo_init(type check) failed");
164                 return (ARCHIVE_FATAL);
165         }
166         if (lzo_version() < 0x940) {
167                 free(data);
168                 archive_set_error(_a, ARCHIVE_ERRNO_MISC,
169                     "liblzo library is too old(%s < 0.940)",
170                     lzo_version_string());
171                 return (ARCHIVE_FATAL);
172         }
173         data->compression_level = 5;
174         return (ARCHIVE_OK);
175 #else
176         data->pdata = __archive_write_program_allocate("lzop");
177         if (data->pdata == NULL) {
178                 free(data);
179                 archive_set_error(_a, ENOMEM, "Can't allocate memory");
180                 return (ARCHIVE_FATAL);
181         }
182         data->compression_level = 0;
183         /* Note: We return "warn" to inform of using an external lzop
184          * program. */
185         archive_set_error(_a, ARCHIVE_ERRNO_MISC,
186             "Using external lzop program for lzop compression");
187         return (ARCHIVE_WARN);
188 #endif
189 }
190
191 static int
192 archive_write_lzop_free(struct archive_write_filter *f)
193 {
194         struct write_lzop *data = (struct write_lzop *)f->data;
195
196 #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
197         free(data->uncompressed);
198         free(data->compressed);
199         free(data->work_buffer);
200 #else
201         __archive_write_program_free(data->pdata);
202 #endif
203         free(data);
204         return (ARCHIVE_OK);
205 }
206
207 static int
208 archive_write_lzop_options(struct archive_write_filter *f, const char *key,
209     const char *value)
210 {
211         struct write_lzop *data = (struct write_lzop *)f->data;
212
213         if (strcmp(key, "compression-level") == 0) {
214                 if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
215                     value[1] != '\0')
216                         return (ARCHIVE_WARN);
217                 data->compression_level = value[0] - '0';
218                 return (ARCHIVE_OK);
219         }
220         /* Note: The "warn" return is just to inform the options
221          * supervisor that we didn't handle it.  It will generate
222          * a suitable error if no one used this option. */
223         return (ARCHIVE_WARN);
224 }
225
226 #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
227 static int
228 archive_write_lzop_open(struct archive_write_filter *f)
229 {
230         struct write_lzop *data = (struct write_lzop *)f->data;
231
232         switch (data->compression_level) {
233         case 1:
234                 data->method = METHOD_LZO1X_1_15; data->level = 1; break;
235         default:
236         case 2: case 3: case 4: case 5: case 6:
237                 data->method = METHOD_LZO1X_1; data->level = 5; break;
238         case 7:
239                 data->method = METHOD_LZO1X_999; data->level = 7; break;
240         case 8:
241                 data->method = METHOD_LZO1X_999; data->level = 8; break;
242         case 9:
243                 data->method = METHOD_LZO1X_999; data->level = 9; break;
244         }
245         switch (data->method) {
246         case METHOD_LZO1X_1:
247                 data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break;
248         case METHOD_LZO1X_1_15:
249                 data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break;
250         case METHOD_LZO1X_999:
251                 data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break;
252         }
253         if (data->work_buffer == NULL) {
254                 data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size);
255                 if (data->work_buffer == NULL) {
256                         archive_set_error(f->archive, ENOMEM,
257                             "Can't allocate data for compression buffer");
258                         return (ARCHIVE_FATAL);
259                 }
260         }
261         if (data->compressed == NULL) {
262                 data->compressed_buffer_size = sizeof(header) +
263                     BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3;
264                 data->compressed = (unsigned char *)
265                     malloc(data->compressed_buffer_size);
266                 if (data->compressed == NULL) {
267                         archive_set_error(f->archive, ENOMEM,
268                             "Can't allocate data for compression buffer");
269                         return (ARCHIVE_FATAL);
270                 }
271         }
272         if (data->uncompressed == NULL) {
273                 data->uncompressed_buffer_size = BLOCK_SIZE;
274                 data->uncompressed = (unsigned char *)
275                     malloc(data->uncompressed_buffer_size);
276                 if (data->uncompressed == NULL) {
277                         archive_set_error(f->archive, ENOMEM,
278                             "Can't allocate data for compression buffer");
279                         return (ARCHIVE_FATAL);
280                 }
281                 data->uncompressed_avail_bytes = BLOCK_SIZE;
282         }
283         return (ARCHIVE_OK);
284 }
285
286 static int
287 make_header(struct archive_write_filter *f)
288 {
289         struct write_lzop *data = (struct write_lzop *)f->data;
290         int64_t t;
291         uint32_t checksum;
292
293         memcpy(data->compressed, header, sizeof(header));
294         /* Overwrite library version. */
295         data->compressed[HEADER_LIBVERSION] = (unsigned char )
296             (lzo_version() >> 8) & 0xff;
297         data->compressed[HEADER_LIBVERSION + 1] = (unsigned char )
298             lzo_version() & 0xff;
299         /* Overwrite method and level. */
300         data->compressed[HEADER_METHOD] = (unsigned char)data->method;
301         data->compressed[HEADER_LEVEL] = data->level;
302         /* Overwrite mtime with current time. */
303         t = (int64_t)time(NULL);
304         archive_be32enc(&data->compressed[HEADER_MTIME_LOW],
305             (uint32_t)(t & 0xffffffff));
306         archive_be32enc(&data->compressed[HEADER_MTIME_HIGH],
307             (uint32_t)((t >> 32) & 0xffffffff));
308         /* Overwrite header checksum with calculated value. */
309         checksum = lzo_adler32(1, data->compressed + HEADER_VERSION,
310                         (lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION));
311         archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum);
312         return (sizeof(header));
313 }
314
315 static int
316 drive_compressor(struct archive_write_filter *f)
317 {
318         struct write_lzop *data = (struct write_lzop *)f->data;
319         unsigned char *p;
320         const int block_info_bytes = 12;
321         int header_bytes, r;
322         lzo_uint usize, csize;
323         uint32_t checksum;
324
325         if (!data->header_written) {
326                 header_bytes = make_header(f);
327                 data->header_written = 1;
328         } else
329                 header_bytes = 0;
330         p = data->compressed;
331
332         usize = (lzo_uint)
333             (data->uncompressed_buffer_size - data->uncompressed_avail_bytes);
334         csize = 0;
335         switch (data->method) {
336         default:
337         case METHOD_LZO1X_1:
338                 r = lzo1x_1_compress(data->uncompressed, usize,
339                         p + header_bytes + block_info_bytes, &csize,
340                         data->work_buffer);
341                 break;
342         case METHOD_LZO1X_1_15:
343                 r = lzo1x_1_15_compress(data->uncompressed, usize,
344                         p + header_bytes + block_info_bytes, &csize,
345                         data->work_buffer);
346                 break;
347         case METHOD_LZO1X_999:
348                 r = lzo1x_999_compress_level(data->uncompressed, usize,
349                         p + header_bytes + block_info_bytes, &csize,
350                         data->work_buffer, NULL, 0, 0, data->level);
351                 break;
352         }
353         if (r != LZO_E_OK) {
354                 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
355                     "Lzop compression failed: returned status %d", r);
356                 return (ARCHIVE_FATAL);
357         }
358
359         /* Store uncompressed size. */
360         archive_be32enc(p + header_bytes, (uint32_t)usize);
361         /* Store the checksum of the uncompressed data. */
362         checksum = lzo_adler32(1, data->uncompressed, usize);
363         archive_be32enc(p + header_bytes + 8, checksum);
364
365         if (csize < usize) {
366                 /* Store compressed size. */
367                 archive_be32enc(p + header_bytes + 4, (uint32_t)csize);
368                 r = __archive_write_filter(f->next_filter, data->compressed,
369                         header_bytes + block_info_bytes + csize);
370         } else {
371                 /*
372                  * This case, we output uncompressed data instead.
373                  */
374                 /* Store uncompressed size as compressed size. */
375                 archive_be32enc(p + header_bytes + 4, (uint32_t)usize);
376                 r = __archive_write_filter(f->next_filter, data->compressed,
377                         header_bytes + block_info_bytes);
378                 if (r != ARCHIVE_OK)
379                         return (ARCHIVE_FATAL);
380                 r = __archive_write_filter(f->next_filter, data->uncompressed,
381                         usize);
382         }
383
384         if (r != ARCHIVE_OK)
385                 return (ARCHIVE_FATAL);
386         return (ARCHIVE_OK);
387 }
388
389 static int
390 archive_write_lzop_write(struct archive_write_filter *f,
391     const void *buff, size_t length)
392 {
393         struct write_lzop *data = (struct write_lzop *)f->data;
394         const char *p = buff;
395         int r;
396
397         do {
398                 if (data->uncompressed_avail_bytes > length) {
399                         memcpy(data->uncompressed
400                                 + data->uncompressed_buffer_size
401                                 - data->uncompressed_avail_bytes,
402                             p, length);
403                         data->uncompressed_avail_bytes -= length;
404                         return (ARCHIVE_OK);
405                 }
406
407                 memcpy(data->uncompressed + data->uncompressed_buffer_size
408                         - data->uncompressed_avail_bytes,
409                     p, data->uncompressed_avail_bytes);
410                 length -= data->uncompressed_avail_bytes;
411                 p += data->uncompressed_avail_bytes;
412                 data->uncompressed_avail_bytes = 0;
413
414                 r = drive_compressor(f);
415                 if (r != ARCHIVE_OK) return (r);
416                 data->uncompressed_avail_bytes = BLOCK_SIZE;
417         } while (length);
418
419         return (ARCHIVE_OK);
420 }
421
422 static int
423 archive_write_lzop_close(struct archive_write_filter *f)
424 {
425         struct write_lzop *data = (struct write_lzop *)f->data;
426         const uint32_t endmark = 0;
427         int r;
428
429         if (data->uncompressed_avail_bytes < BLOCK_SIZE) {
430                 /* Compress and output remaining data. */
431                 r = drive_compressor(f);
432                 if (r != ARCHIVE_OK)
433                         return (r);
434         }
435         /* Write a zero uncompressed size as the end mark of the series of
436          * compressed block. */
437         return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark));
438 }
439
440 #else
441 static int
442 archive_write_lzop_open(struct archive_write_filter *f)
443 {
444         struct write_lzop *data = (struct write_lzop *)f->data;
445         struct archive_string as;
446         int r;
447
448         archive_string_init(&as);
449         archive_strcpy(&as, "lzop");
450         /* Specify compression level. */
451         if (data->compression_level > 0) {
452                 archive_strappend_char(&as, ' ');
453                 archive_strappend_char(&as, '-');
454                 archive_strappend_char(&as, '0' + data->compression_level);
455         }
456
457         r = __archive_write_program_open(f, data->pdata, as.s);
458         archive_string_free(&as);
459         return (r);
460 }
461
462 static int
463 archive_write_lzop_write(struct archive_write_filter *f,
464     const void *buff, size_t length)
465 {
466         struct write_lzop *data = (struct write_lzop *)f->data;
467
468         return __archive_write_program_write(f, data->pdata, buff, length);
469 }
470
471 static int
472 archive_write_lzop_close(struct archive_write_filter *f)
473 {
474         struct write_lzop *data = (struct write_lzop *)f->data;
475
476         return __archive_write_program_close(f, data->pdata);
477 }
478 #endif