gdb - Local mods (compile)
[dragonfly.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 infomation 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();
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         int ret;
232
233         ret = __archive_write_open_filter(f->next_filter);
234         if (ret != ARCHIVE_OK)
235                 return (ret);
236
237         switch (data->compression_level) {
238         case 1:
239                 data->method = METHOD_LZO1X_1_15; data->level = 1; break;
240         default:
241         case 2: case 3: case 4: case 5: case 6:
242                 data->method = METHOD_LZO1X_1; data->level = 5; break;
243         case 7:
244                 data->method = METHOD_LZO1X_999; data->level = 7; break;
245         case 8:
246                 data->method = METHOD_LZO1X_999; data->level = 8; break;
247         case 9:
248                 data->method = METHOD_LZO1X_999; data->level = 9; break;
249         }
250         switch (data->method) {
251         case METHOD_LZO1X_1:
252                 data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break;
253         case METHOD_LZO1X_1_15:
254                 data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break;
255         case METHOD_LZO1X_999:
256                 data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break;
257         }
258         if (data->work_buffer == NULL) {
259                 data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size);
260                 if (data->work_buffer == NULL) {
261                         archive_set_error(f->archive, ENOMEM,
262                             "Can't allocate data for compression buffer");
263                         return (ARCHIVE_FATAL);
264                 }
265         }
266         if (data->compressed == NULL) {
267                 data->compressed_buffer_size = sizeof(header) +
268                     BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3;
269                 data->compressed = (unsigned char *)
270                     malloc(data->compressed_buffer_size);
271                 if (data->compressed == NULL) {
272                         archive_set_error(f->archive, ENOMEM,
273                             "Can't allocate data for compression buffer");
274                         return (ARCHIVE_FATAL);
275                 }
276         }
277         if (data->uncompressed == NULL) {
278                 data->uncompressed_buffer_size = BLOCK_SIZE;
279                 data->uncompressed = (unsigned char *)
280                     malloc(data->uncompressed_buffer_size);
281                 if (data->uncompressed == NULL) {
282                         archive_set_error(f->archive, ENOMEM,
283                             "Can't allocate data for compression buffer");
284                         return (ARCHIVE_FATAL);
285                 }
286                 data->uncompressed_avail_bytes = BLOCK_SIZE;
287         }
288         return (ARCHIVE_OK);
289 }
290
291 static int
292 make_header(struct archive_write_filter *f)
293 {
294         struct write_lzop *data = (struct write_lzop *)f->data;
295         int64_t t;
296         uint32_t checksum;
297
298         memcpy(data->compressed, header, sizeof(header));
299         /* Overwrite library version. */
300         data->compressed[HEADER_LIBVERSION] = (unsigned char )
301             (lzo_version() >> 8) & 0xff;
302         data->compressed[HEADER_LIBVERSION + 1] = (unsigned char )
303             lzo_version() & 0xff;
304         /* Overwrite method and level. */
305         data->compressed[HEADER_METHOD] = (unsigned char)data->method;
306         data->compressed[HEADER_LEVEL] = data->level;
307         /* Overwrite mtime with current time. */
308         t = (int64_t)time(NULL);
309         archive_be32enc(&data->compressed[HEADER_MTIME_LOW],
310             (uint32_t)(t & 0xffffffff));
311         archive_be32enc(&data->compressed[HEADER_MTIME_HIGH],
312             (uint32_t)((t >> 32) & 0xffffffff));
313         /* Overwrite header checksum with calculated value. */
314         checksum = lzo_adler32(1, data->compressed + HEADER_VERSION,
315                         (lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION));
316         archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum);
317         return (sizeof(header));
318 }
319
320 static int
321 drive_compressor(struct archive_write_filter *f)
322 {
323         struct write_lzop *data = (struct write_lzop *)f->data;
324         unsigned char *p;
325         const int block_info_bytes = 12;
326         int header_bytes, r;
327         lzo_uint usize, csize;
328         uint32_t checksum;
329
330         if (!data->header_written) {
331                 header_bytes = make_header(f);
332                 data->header_written = 1;
333         } else
334                 header_bytes = 0;
335         p = data->compressed;
336
337         usize = (lzo_uint)
338             (data->uncompressed_buffer_size - data->uncompressed_avail_bytes);
339         csize = 0;
340         switch (data->method) {
341         default:
342         case METHOD_LZO1X_1:
343                 r = lzo1x_1_compress(data->uncompressed, usize,
344                         p + header_bytes + block_info_bytes, &csize,
345                         data->work_buffer);
346                 break;
347         case METHOD_LZO1X_1_15:
348                 r = lzo1x_1_15_compress(data->uncompressed, usize,
349                         p + header_bytes + block_info_bytes, &csize,
350                         data->work_buffer);
351                 break;
352         case METHOD_LZO1X_999:
353                 r = lzo1x_999_compress_level(data->uncompressed, usize,
354                         p + header_bytes + block_info_bytes, &csize,
355                         data->work_buffer, NULL, 0, 0, data->level);
356                 break;
357         }
358         if (r != LZO_E_OK) {
359                 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
360                     "Lzop compression failed: returned status %d", r);
361                 return (ARCHIVE_FATAL);
362         }
363
364         /* Store uncompressed size. */
365         archive_be32enc(p + header_bytes, (uint32_t)usize);
366         /* Store the checksum of the uncompressed data. */
367         checksum = lzo_adler32(1, data->uncompressed, usize);
368         archive_be32enc(p + header_bytes + 8, checksum);
369
370         if (csize < usize) {
371                 /* Store compressed size. */
372                 archive_be32enc(p + header_bytes + 4, (uint32_t)csize);
373                 r = __archive_write_filter(f->next_filter, data->compressed,
374                         header_bytes + block_info_bytes + csize);
375         } else {
376                 /*
377                  * This case, we output uncompressed data instead.
378                  */
379                 /* Store uncompressed size as compressed size. */
380                 archive_be32enc(p + header_bytes + 4, (uint32_t)usize);
381                 r = __archive_write_filter(f->next_filter, data->compressed,
382                         header_bytes + block_info_bytes);
383                 if (r != ARCHIVE_OK)
384                         return (ARCHIVE_FATAL);
385                 r = __archive_write_filter(f->next_filter, data->uncompressed,
386                         usize);
387         }
388
389         if (r != ARCHIVE_OK)
390                 return (ARCHIVE_FATAL);
391         return (ARCHIVE_OK);
392 }
393
394 static int
395 archive_write_lzop_write(struct archive_write_filter *f,
396     const void *buff, size_t length)
397 {
398         struct write_lzop *data = (struct write_lzop *)f->data;
399         const char *p = buff;
400         int r;
401
402         do {
403                 if (data->uncompressed_avail_bytes > length) {
404                         memcpy(data->uncompressed
405                                 + data->uncompressed_buffer_size
406                                 - data->uncompressed_avail_bytes,
407                             p, length);
408                         data->uncompressed_avail_bytes -= length;
409                         return (ARCHIVE_OK);
410                 }
411
412                 memcpy(data->uncompressed + data->uncompressed_buffer_size
413                         - data->uncompressed_avail_bytes,
414                     p, data->uncompressed_avail_bytes);
415                 length -= data->uncompressed_avail_bytes;
416                 p += data->uncompressed_avail_bytes;
417                 data->uncompressed_avail_bytes = 0;
418
419                 r = drive_compressor(f);
420                 if (r != ARCHIVE_OK) return (r);
421                 data->uncompressed_avail_bytes = BLOCK_SIZE;
422         } while (length);
423
424         return (ARCHIVE_OK);
425 }
426
427 static int
428 archive_write_lzop_close(struct archive_write_filter *f)
429 {
430         struct write_lzop *data = (struct write_lzop *)f->data;
431         const uint32_t endmark = 0;
432         int r;
433
434         if (data->uncompressed_avail_bytes < BLOCK_SIZE) {
435                 /* Compress and output remaining data. */
436                 r = drive_compressor(f);
437                 if (r != ARCHIVE_OK)
438                         return (r);
439         }
440         /* Write a zero uncompressed size as the end mark of the series of
441          * compressed block. */
442         r = __archive_write_filter(f->next_filter, &endmark, sizeof(endmark));
443         if (r != ARCHIVE_OK)
444                 return (r);
445         return (__archive_write_close_filter(f->next_filter));
446 }
447
448 #else
449 static int
450 archive_write_lzop_open(struct archive_write_filter *f)
451 {
452         struct write_lzop *data = (struct write_lzop *)f->data;
453         struct archive_string as;
454         int r;
455
456         archive_string_init(&as);
457         archive_strcpy(&as, "lzop");
458         /* Specify compression level. */
459         if (data->compression_level > 0) {
460                 archive_strappend_char(&as, ' ');
461                 archive_strappend_char(&as, '-');
462                 archive_strappend_char(&as, '0' + data->compression_level);
463         }
464
465         r = __archive_write_program_open(f, data->pdata, as.s);
466         archive_string_free(&as);
467         return (r);
468 }
469
470 static int
471 archive_write_lzop_write(struct archive_write_filter *f,
472     const void *buff, size_t length)
473 {
474         struct write_lzop *data = (struct write_lzop *)f->data;
475
476         return __archive_write_program_write(f, data->pdata, buff, length);
477 }
478
479 static int
480 archive_write_lzop_close(struct archive_write_filter *f)
481 {
482         struct write_lzop *data = (struct write_lzop *)f->data;
483
484         return __archive_write_program_close(f, data->pdata);
485 }
486 #endif