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