1248590Smm/*- 2248590Smm * Copyright (c) 2012 Michihiro NAKAJIMA 3248590Smm * All rights reserved. 4248590Smm * 5248590Smm * Redistribution and use in source and binary forms, with or without 6248590Smm * modification, are permitted provided that the following conditions 7248590Smm * are met: 8248590Smm * 1. Redistributions of source code must retain the above copyright 9248590Smm * notice, this list of conditions and the following disclaimer. 10248590Smm * 2. Redistributions in binary form must reproduce the above copyright 11248590Smm * notice, this list of conditions and the following disclaimer in the 12248590Smm * documentation and/or other materials provided with the distribution. 13248590Smm * 14248590Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15248590Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16248590Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17248590Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18248590Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19248590Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20248590Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21248590Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22248590Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23248590Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24248590Smm */ 25248590Smm 26248590Smm#include "archive_platform.h" 27248590Smm 28248590Smm__FBSDID("$FreeBSD$"); 29248590Smm//#undef HAVE_LZO_LZOCONF_H 30248590Smm//#undef HAVE_LZO_LZO1X_H 31248590Smm 32248590Smm#ifdef HAVE_ERRNO_H 33248590Smm#include <errno.h> 34248590Smm#endif 35248590Smm#ifdef HAVE_STDLIB_H 36248590Smm#include <stdlib.h> 37248590Smm#endif 38248590Smm#ifdef HAVE_STRING_H 39248590Smm#include <string.h> 40248590Smm#endif 41248590Smm#include <time.h> 42248590Smm#ifdef HAVE_LZO_LZOCONF_H 43248590Smm#include <lzo/lzoconf.h> 44248590Smm#endif 45248590Smm#ifdef HAVE_LZO_LZO1X_H 46248590Smm#include <lzo/lzo1x.h> 47248590Smm#endif 48248590Smm 49248590Smm#include "archive.h" 50248590Smm#include "archive_string.h" 51248590Smm#include "archive_endian.h" 52248590Smm#include "archive_write_private.h" 53248590Smm 54248590Smmenum lzo_method { 55248590Smm METHOD_LZO1X_1 = 1, 56248590Smm METHOD_LZO1X_1_15 = 2, 57248590Smm METHOD_LZO1X_999 = 3 58248590Smm}; 59248590Smmstruct write_lzop { 60248590Smm int compression_level; 61248590Smm#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) 62248590Smm unsigned char *uncompressed; 63248590Smm size_t uncompressed_buffer_size; 64248590Smm size_t uncompressed_avail_bytes; 65248590Smm unsigned char *compressed; 66248590Smm size_t compressed_buffer_size; 67248590Smm enum lzo_method method; 68248590Smm unsigned char level; 69248590Smm lzo_voidp work_buffer; 70248590Smm lzo_uint32 work_buffer_size; 71248590Smm char header_written; 72248590Smm#else 73248590Smm struct archive_write_program_data *pdata; 74248590Smm#endif 75248590Smm}; 76248590Smm 77248590Smmstatic int archive_write_lzop_open(struct archive_write_filter *); 78248590Smmstatic int archive_write_lzop_options(struct archive_write_filter *, 79248590Smm const char *, const char *); 80248590Smmstatic int archive_write_lzop_write(struct archive_write_filter *, 81248590Smm const void *, size_t); 82248590Smmstatic int archive_write_lzop_close(struct archive_write_filter *); 83248590Smmstatic int archive_write_lzop_free(struct archive_write_filter *); 84248590Smm 85248590Smm#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) 86248590Smm/* Maximum block size. */ 87248590Smm#define BLOCK_SIZE (256 * 1024) 88248590Smm/* Block infomation is composed of uncompressed size(4 bytes), 89248590Smm * compressed size(4 bytes) and the checksum of uncompressed data(4 bytes) 90248590Smm * in this lzop writer. */ 91248590Smm#define BLOCK_INfO_SIZE 12 92248590Smm 93248590Smm#define HEADER_VERSION 9 94248590Smm#define HEADER_LIBVERSION 11 95248590Smm#define HEADER_METHOD 15 96248590Smm#define HEADER_LEVEL 16 97248590Smm#define HEADER_MTIME_LOW 25 98248590Smm#define HEADER_MTIME_HIGH 29 99248590Smm#define HEADER_H_CHECKSUM 34 100248590Smm 101248590Smm/* 102248590Smm * Header template. 103248590Smm */ 104248590Smmstatic const unsigned char header[] = { 105248590Smm /* LZOP Magic code 9 bytes */ 106248590Smm 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a, 107248590Smm /* LZOP utility version(fake data) 2 bytes */ 108248590Smm 0x10, 0x30, 109248590Smm /* LZO library version 2 bytes */ 110248590Smm 0x09, 0x40, 111248590Smm /* Minimum required LZO library version 2 bytes */ 112248590Smm 0x09, 0x40, 113248590Smm /* Method */ 114248590Smm 1, 115248590Smm /* Level */ 116248590Smm 5, 117248590Smm /* Flags 4 bytes 118248590Smm * -OS Unix 119248590Smm * -Stdout 120248590Smm * -Stdin 121248590Smm * -Adler32 used for uncompressed data 4 bytes */ 122248590Smm 0x03, 0x00, 0x00, 0x0d, 123248590Smm /* Mode (AE_IFREG | 0644) 4 bytes */ 124248590Smm 0x00, 0x00, 0x81, 0xa4, 125248590Smm /* Mtime low 4 bytes */ 126248590Smm 0x00, 0x00, 0x00, 0x00, 127248590Smm /* Mtime high 4 bytes */ 128248590Smm 0x00, 0x00, 0x00, 0x00, 129248590Smm /* Filename length */ 130248590Smm 0x00, 131248590Smm /* Header checksum 4 bytes */ 132248590Smm 0x00, 0x00, 0x00, 0x00, 133248590Smm}; 134248590Smm#endif 135248590Smm 136248590Smmint 137248590Smmarchive_write_add_filter_lzop(struct archive *_a) 138248590Smm{ 139248590Smm struct archive_write_filter *f = __archive_write_allocate_filter(_a); 140248590Smm struct write_lzop *data; 141248590Smm 142248590Smm archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, 143248590Smm ARCHIVE_STATE_NEW, "archive_write_add_filter_lzop"); 144248590Smm 145248590Smm data = calloc(1, sizeof(*data)); 146248590Smm if (data == NULL) { 147248590Smm archive_set_error(_a, ENOMEM, "Can't allocate memory"); 148248590Smm return (ARCHIVE_FATAL); 149248590Smm } 150248590Smm 151248590Smm f->name = "lzop"; 152248590Smm f->code = ARCHIVE_FILTER_LZOP; 153248590Smm f->data = data; 154248590Smm f->open = archive_write_lzop_open; 155248590Smm f->options = archive_write_lzop_options; 156248590Smm f->write = archive_write_lzop_write; 157248590Smm f->close = archive_write_lzop_close; 158248590Smm f->free = archive_write_lzop_free; 159248590Smm#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) 160248590Smm if (lzo_init() != LZO_E_OK) { 161248590Smm free(data); 162248590Smm archive_set_error(_a, ARCHIVE_ERRNO_MISC, 163248590Smm "lzo_init(type check) failed"); 164248590Smm return (ARCHIVE_FATAL); 165248590Smm } 166248590Smm if (lzo_version() < 0x940) { 167248590Smm free(data); 168248590Smm archive_set_error(_a, ARCHIVE_ERRNO_MISC, 169248590Smm "liblzo library is too old(%s < 0.940)", 170248590Smm lzo_version_string()); 171248590Smm return (ARCHIVE_FATAL); 172248590Smm } 173248590Smm data->compression_level = 5; 174248590Smm return (ARCHIVE_OK); 175248590Smm#else 176248590Smm data->pdata = __archive_write_program_allocate(); 177248590Smm if (data->pdata == NULL) { 178248590Smm free(data); 179248590Smm archive_set_error(_a, ENOMEM, "Can't allocate memory"); 180248590Smm return (ARCHIVE_FATAL); 181248590Smm } 182248590Smm data->compression_level = 0; 183248590Smm /* Note: We return "warn" to inform of using an external lzop 184248590Smm * program. */ 185248590Smm archive_set_error(_a, ARCHIVE_ERRNO_MISC, 186248590Smm "Using external lzop program for lzop compression"); 187248590Smm return (ARCHIVE_WARN); 188248590Smm#endif 189248590Smm} 190248590Smm 191248590Smmstatic int 192248590Smmarchive_write_lzop_free(struct archive_write_filter *f) 193248590Smm{ 194248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 195248590Smm 196248590Smm#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) 197248590Smm free(data->uncompressed); 198248590Smm free(data->compressed); 199248590Smm free(data->work_buffer); 200248590Smm#else 201248590Smm __archive_write_program_free(data->pdata); 202248590Smm#endif 203248590Smm free(data); 204248590Smm return (ARCHIVE_OK); 205248590Smm} 206248590Smm 207248590Smmstatic int 208248590Smmarchive_write_lzop_options(struct archive_write_filter *f, const char *key, 209248590Smm const char *value) 210248590Smm{ 211248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 212248590Smm 213248590Smm if (strcmp(key, "compression-level") == 0) { 214248590Smm if (value == NULL || !(value[0] >= '1' && value[0] <= '9') || 215248590Smm value[1] != '\0') 216248590Smm return (ARCHIVE_WARN); 217248590Smm data->compression_level = value[0] - '0'; 218248590Smm return (ARCHIVE_OK); 219248590Smm } 220248590Smm /* Note: The "warn" return is just to inform the options 221248590Smm * supervisor that we didn't handle it. It will generate 222248590Smm * a suitable error if no one used this option. */ 223248590Smm return (ARCHIVE_WARN); 224248590Smm} 225248590Smm 226248590Smm#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) 227248590Smmstatic int 228248590Smmarchive_write_lzop_open(struct archive_write_filter *f) 229248590Smm{ 230248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 231248590Smm int ret; 232248590Smm 233248590Smm ret = __archive_write_open_filter(f->next_filter); 234248590Smm if (ret != ARCHIVE_OK) 235248590Smm return (ret); 236248590Smm 237248590Smm switch (data->compression_level) { 238248590Smm case 1: 239248590Smm data->method = METHOD_LZO1X_1_15; data->level = 1; break; 240248590Smm default: 241248590Smm case 2: case 3: case 4: case 5: case 6: 242248590Smm data->method = METHOD_LZO1X_1; data->level = 5; break; 243248590Smm case 7: 244248590Smm data->method = METHOD_LZO1X_999; data->level = 7; break; 245248590Smm case 8: 246248590Smm data->method = METHOD_LZO1X_999; data->level = 8; break; 247248590Smm case 9: 248248590Smm data->method = METHOD_LZO1X_999; data->level = 9; break; 249248590Smm } 250248590Smm switch (data->method) { 251248590Smm case METHOD_LZO1X_1: 252248590Smm data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break; 253248590Smm case METHOD_LZO1X_1_15: 254248590Smm data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break; 255248590Smm case METHOD_LZO1X_999: 256248590Smm data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break; 257248590Smm } 258248590Smm if (data->work_buffer == NULL) { 259248590Smm data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size); 260248590Smm if (data->work_buffer == NULL) { 261248590Smm archive_set_error(f->archive, ENOMEM, 262248590Smm "Can't allocate data for compression buffer"); 263248590Smm return (ARCHIVE_FATAL); 264248590Smm } 265248590Smm } 266248590Smm if (data->compressed == NULL) { 267248590Smm data->compressed_buffer_size = sizeof(header) + 268248590Smm BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3; 269248590Smm data->compressed = (unsigned char *) 270248590Smm malloc(data->compressed_buffer_size); 271248590Smm if (data->compressed == NULL) { 272248590Smm archive_set_error(f->archive, ENOMEM, 273248590Smm "Can't allocate data for compression buffer"); 274248590Smm return (ARCHIVE_FATAL); 275248590Smm } 276248590Smm } 277248590Smm if (data->uncompressed == NULL) { 278248590Smm data->uncompressed_buffer_size = BLOCK_SIZE; 279248590Smm data->uncompressed = (unsigned char *) 280248590Smm malloc(data->uncompressed_buffer_size); 281248590Smm if (data->uncompressed == NULL) { 282248590Smm archive_set_error(f->archive, ENOMEM, 283248590Smm "Can't allocate data for compression buffer"); 284248590Smm return (ARCHIVE_FATAL); 285248590Smm } 286248590Smm data->uncompressed_avail_bytes = BLOCK_SIZE; 287248590Smm } 288248590Smm return (ARCHIVE_OK); 289248590Smm} 290248590Smm 291248590Smmstatic int 292248590Smmmake_header(struct archive_write_filter *f) 293248590Smm{ 294248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 295248590Smm int64_t t; 296248590Smm uint32_t checksum; 297248590Smm 298248590Smm memcpy(data->compressed, header, sizeof(header)); 299248590Smm /* Overwrite library version. */ 300248590Smm data->compressed[HEADER_LIBVERSION] = (unsigned char ) 301248590Smm (lzo_version() >> 8) & 0xff; 302248590Smm data->compressed[HEADER_LIBVERSION + 1] = (unsigned char ) 303248590Smm lzo_version() & 0xff; 304248590Smm /* Overwrite method and level. */ 305248590Smm data->compressed[HEADER_METHOD] = (unsigned char)data->method; 306248590Smm data->compressed[HEADER_LEVEL] = data->level; 307248590Smm /* Overwrite mtime with current time. */ 308248590Smm t = (int64_t)time(NULL); 309248590Smm archive_be32enc(&data->compressed[HEADER_MTIME_LOW], 310248590Smm (uint32_t)(t & 0xffffffff)); 311248590Smm archive_be32enc(&data->compressed[HEADER_MTIME_HIGH], 312248590Smm (uint32_t)((t >> 32) & 0xffffffff)); 313248590Smm /* Overwrite header checksum with calculated value. */ 314248590Smm checksum = lzo_adler32(1, data->compressed + HEADER_VERSION, 315248590Smm (lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION)); 316248590Smm archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum); 317248590Smm return (sizeof(header)); 318248590Smm} 319248590Smm 320248590Smmstatic int 321248590Smmdrive_compressor(struct archive_write_filter *f) 322248590Smm{ 323248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 324248590Smm unsigned char *p; 325248590Smm const int block_info_bytes = 12; 326248590Smm int header_bytes, r; 327248590Smm lzo_uint usize, csize; 328248590Smm uint32_t checksum; 329248590Smm 330248590Smm if (!data->header_written) { 331248590Smm header_bytes = make_header(f); 332248590Smm data->header_written = 1; 333248590Smm } else 334248590Smm header_bytes = 0; 335248590Smm p = data->compressed; 336248590Smm 337248590Smm usize = (lzo_uint) 338248590Smm (data->uncompressed_buffer_size - data->uncompressed_avail_bytes); 339248590Smm csize = 0; 340248590Smm switch (data->method) { 341248590Smm default: 342248590Smm case METHOD_LZO1X_1: 343248590Smm r = lzo1x_1_compress(data->uncompressed, usize, 344248590Smm p + header_bytes + block_info_bytes, &csize, 345248590Smm data->work_buffer); 346248590Smm break; 347248590Smm case METHOD_LZO1X_1_15: 348248590Smm r = lzo1x_1_15_compress(data->uncompressed, usize, 349248590Smm p + header_bytes + block_info_bytes, &csize, 350248590Smm data->work_buffer); 351248590Smm break; 352248590Smm case METHOD_LZO1X_999: 353248590Smm r = lzo1x_999_compress_level(data->uncompressed, usize, 354248590Smm p + header_bytes + block_info_bytes, &csize, 355248590Smm data->work_buffer, NULL, 0, 0, data->level); 356248590Smm break; 357248590Smm } 358248590Smm if (r != LZO_E_OK) { 359248590Smm archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 360248590Smm "Lzop compression failed: returned status %d", r); 361248590Smm return (ARCHIVE_FATAL); 362248590Smm } 363248590Smm 364248590Smm /* Store uncompressed size. */ 365248590Smm archive_be32enc(p + header_bytes, (uint32_t)usize); 366248590Smm /* Store the checksum of the uncompressed data. */ 367248590Smm checksum = lzo_adler32(1, data->uncompressed, usize); 368248590Smm archive_be32enc(p + header_bytes + 8, checksum); 369248590Smm 370248590Smm if (csize < usize) { 371248590Smm /* Store compressed size. */ 372248590Smm archive_be32enc(p + header_bytes + 4, (uint32_t)csize); 373248590Smm r = __archive_write_filter(f->next_filter, data->compressed, 374248590Smm header_bytes + block_info_bytes + csize); 375248590Smm } else { 376248590Smm /* 377248590Smm * This case, we output uncompressed data instead. 378248590Smm */ 379248590Smm /* Store uncompressed size as compressed size. */ 380248590Smm archive_be32enc(p + header_bytes + 4, (uint32_t)usize); 381248590Smm r = __archive_write_filter(f->next_filter, data->compressed, 382248590Smm header_bytes + block_info_bytes); 383248590Smm if (r != ARCHIVE_OK) 384248590Smm return (ARCHIVE_FATAL); 385248590Smm r = __archive_write_filter(f->next_filter, data->uncompressed, 386248590Smm usize); 387248590Smm } 388248590Smm 389248590Smm if (r != ARCHIVE_OK) 390248590Smm return (ARCHIVE_FATAL); 391248590Smm return (ARCHIVE_OK); 392248590Smm} 393248590Smm 394248590Smmstatic int 395248590Smmarchive_write_lzop_write(struct archive_write_filter *f, 396248590Smm const void *buff, size_t length) 397248590Smm{ 398248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 399248590Smm const char *p = buff; 400248590Smm int r; 401248590Smm 402248590Smm do { 403248590Smm if (data->uncompressed_avail_bytes > length) { 404248590Smm memcpy(data->uncompressed 405248590Smm + data->uncompressed_buffer_size 406248590Smm - data->uncompressed_avail_bytes, 407248590Smm p, length); 408248590Smm data->uncompressed_avail_bytes -= length; 409248590Smm return (ARCHIVE_OK); 410248590Smm } 411248590Smm 412248590Smm memcpy(data->uncompressed + data->uncompressed_buffer_size 413248590Smm - data->uncompressed_avail_bytes, 414248590Smm p, data->uncompressed_avail_bytes); 415248590Smm length -= data->uncompressed_avail_bytes; 416248590Smm p += data->uncompressed_avail_bytes; 417248590Smm data->uncompressed_avail_bytes = 0; 418248590Smm 419248590Smm r = drive_compressor(f); 420248590Smm if (r != ARCHIVE_OK) return (r); 421248590Smm data->uncompressed_avail_bytes = BLOCK_SIZE; 422248590Smm } while (length); 423248590Smm 424248590Smm return (ARCHIVE_OK); 425248590Smm} 426248590Smm 427248590Smmstatic int 428248590Smmarchive_write_lzop_close(struct archive_write_filter *f) 429248590Smm{ 430248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 431248590Smm const uint32_t endmark = 0; 432248590Smm int r; 433248590Smm 434248590Smm if (data->uncompressed_avail_bytes < BLOCK_SIZE) { 435248590Smm /* Compress and output remaining data. */ 436248590Smm r = drive_compressor(f); 437248590Smm if (r != ARCHIVE_OK) 438248590Smm return (r); 439248590Smm } 440248590Smm /* Write a zero uncompressed size as the end mark of the series of 441248590Smm * compressed block. */ 442248590Smm r = __archive_write_filter(f->next_filter, &endmark, sizeof(endmark)); 443248590Smm if (r != ARCHIVE_OK) 444248590Smm return (r); 445248590Smm return (__archive_write_close_filter(f->next_filter)); 446248590Smm} 447248590Smm 448248590Smm#else 449248590Smmstatic int 450248590Smmarchive_write_lzop_open(struct archive_write_filter *f) 451248590Smm{ 452248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 453248590Smm struct archive_string as; 454248590Smm int r; 455248590Smm 456248590Smm archive_string_init(&as); 457248590Smm archive_strcpy(&as, "lzop"); 458248590Smm /* Specify compression level. */ 459248590Smm if (data->compression_level > 0) { 460248590Smm archive_strappend_char(&as, ' '); 461248590Smm archive_strappend_char(&as, '-'); 462248590Smm archive_strappend_char(&as, '0' + data->compression_level); 463248590Smm } 464248590Smm 465248590Smm r = __archive_write_program_open(f, data->pdata, as.s); 466248590Smm archive_string_free(&as); 467248590Smm return (r); 468248590Smm} 469248590Smm 470248590Smmstatic int 471248590Smmarchive_write_lzop_write(struct archive_write_filter *f, 472248590Smm const void *buff, size_t length) 473248590Smm{ 474248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 475248590Smm 476248590Smm return __archive_write_program_write(f, data->pdata, buff, length); 477248590Smm} 478248590Smm 479248590Smmstatic int 480248590Smmarchive_write_lzop_close(struct archive_write_filter *f) 481248590Smm{ 482248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 483248590Smm 484248590Smm return __archive_write_program_close(f, data->pdata); 485248590Smm} 486248590Smm#endif 487