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 53enum lzo_method { 54 METHOD_LZO1X_1 = 1, 55 METHOD_LZO1X_1_15 = 2, 56 METHOD_LZO1X_999 = 3 57}; 58struct 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 76static int archive_write_lzop_open(struct archive_write_filter *); 77static int archive_write_lzop_options(struct archive_write_filter *, 78 const char *, const char *); 79static int archive_write_lzop_write(struct archive_write_filter *, 80 const void *, size_t); 81static int archive_write_lzop_close(struct archive_write_filter *); 82static 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 */ 103static 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 135int 136archive_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 190static int 191archive_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 206static int 207archive_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) 226static int 227archive_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 285static int 286make_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 314static int 315drive_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 388static int 389archive_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 421static int 422archive_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 440static int 441archive_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 461static int 462archive_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 470static int 471archive_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