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