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) 88299529Smm/* Block information 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 176299529Smm data->pdata = __archive_write_program_allocate("lzop"); 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 232248590Smm switch (data->compression_level) { 233248590Smm case 1: 234248590Smm data->method = METHOD_LZO1X_1_15; data->level = 1; break; 235248590Smm default: 236248590Smm case 2: case 3: case 4: case 5: case 6: 237248590Smm data->method = METHOD_LZO1X_1; data->level = 5; break; 238248590Smm case 7: 239248590Smm data->method = METHOD_LZO1X_999; data->level = 7; break; 240248590Smm case 8: 241248590Smm data->method = METHOD_LZO1X_999; data->level = 8; break; 242248590Smm case 9: 243248590Smm data->method = METHOD_LZO1X_999; data->level = 9; break; 244248590Smm } 245248590Smm switch (data->method) { 246248590Smm case METHOD_LZO1X_1: 247248590Smm data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break; 248248590Smm case METHOD_LZO1X_1_15: 249248590Smm data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break; 250248590Smm case METHOD_LZO1X_999: 251248590Smm data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break; 252248590Smm } 253248590Smm if (data->work_buffer == NULL) { 254248590Smm data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size); 255248590Smm if (data->work_buffer == NULL) { 256248590Smm archive_set_error(f->archive, ENOMEM, 257248590Smm "Can't allocate data for compression buffer"); 258248590Smm return (ARCHIVE_FATAL); 259248590Smm } 260248590Smm } 261248590Smm if (data->compressed == NULL) { 262248590Smm data->compressed_buffer_size = sizeof(header) + 263248590Smm BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3; 264248590Smm data->compressed = (unsigned char *) 265248590Smm malloc(data->compressed_buffer_size); 266248590Smm if (data->compressed == NULL) { 267248590Smm archive_set_error(f->archive, ENOMEM, 268248590Smm "Can't allocate data for compression buffer"); 269248590Smm return (ARCHIVE_FATAL); 270248590Smm } 271248590Smm } 272248590Smm if (data->uncompressed == NULL) { 273248590Smm data->uncompressed_buffer_size = BLOCK_SIZE; 274248590Smm data->uncompressed = (unsigned char *) 275248590Smm malloc(data->uncompressed_buffer_size); 276248590Smm if (data->uncompressed == NULL) { 277248590Smm archive_set_error(f->archive, ENOMEM, 278248590Smm "Can't allocate data for compression buffer"); 279248590Smm return (ARCHIVE_FATAL); 280248590Smm } 281248590Smm data->uncompressed_avail_bytes = BLOCK_SIZE; 282248590Smm } 283248590Smm return (ARCHIVE_OK); 284248590Smm} 285248590Smm 286248590Smmstatic int 287248590Smmmake_header(struct archive_write_filter *f) 288248590Smm{ 289248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 290248590Smm int64_t t; 291248590Smm uint32_t checksum; 292248590Smm 293248590Smm memcpy(data->compressed, header, sizeof(header)); 294248590Smm /* Overwrite library version. */ 295248590Smm data->compressed[HEADER_LIBVERSION] = (unsigned char ) 296248590Smm (lzo_version() >> 8) & 0xff; 297248590Smm data->compressed[HEADER_LIBVERSION + 1] = (unsigned char ) 298248590Smm lzo_version() & 0xff; 299248590Smm /* Overwrite method and level. */ 300248590Smm data->compressed[HEADER_METHOD] = (unsigned char)data->method; 301248590Smm data->compressed[HEADER_LEVEL] = data->level; 302248590Smm /* Overwrite mtime with current time. */ 303248590Smm t = (int64_t)time(NULL); 304248590Smm archive_be32enc(&data->compressed[HEADER_MTIME_LOW], 305248590Smm (uint32_t)(t & 0xffffffff)); 306248590Smm archive_be32enc(&data->compressed[HEADER_MTIME_HIGH], 307248590Smm (uint32_t)((t >> 32) & 0xffffffff)); 308248590Smm /* Overwrite header checksum with calculated value. */ 309248590Smm checksum = lzo_adler32(1, data->compressed + HEADER_VERSION, 310248590Smm (lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION)); 311248590Smm archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum); 312248590Smm return (sizeof(header)); 313248590Smm} 314248590Smm 315248590Smmstatic int 316248590Smmdrive_compressor(struct archive_write_filter *f) 317248590Smm{ 318248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 319248590Smm unsigned char *p; 320248590Smm const int block_info_bytes = 12; 321248590Smm int header_bytes, r; 322248590Smm lzo_uint usize, csize; 323248590Smm uint32_t checksum; 324248590Smm 325248590Smm if (!data->header_written) { 326248590Smm header_bytes = make_header(f); 327248590Smm data->header_written = 1; 328248590Smm } else 329248590Smm header_bytes = 0; 330248590Smm p = data->compressed; 331248590Smm 332248590Smm usize = (lzo_uint) 333248590Smm (data->uncompressed_buffer_size - data->uncompressed_avail_bytes); 334248590Smm csize = 0; 335248590Smm switch (data->method) { 336248590Smm default: 337248590Smm case METHOD_LZO1X_1: 338248590Smm r = lzo1x_1_compress(data->uncompressed, usize, 339248590Smm p + header_bytes + block_info_bytes, &csize, 340248590Smm data->work_buffer); 341248590Smm break; 342248590Smm case METHOD_LZO1X_1_15: 343248590Smm r = lzo1x_1_15_compress(data->uncompressed, usize, 344248590Smm p + header_bytes + block_info_bytes, &csize, 345248590Smm data->work_buffer); 346248590Smm break; 347248590Smm case METHOD_LZO1X_999: 348248590Smm r = lzo1x_999_compress_level(data->uncompressed, usize, 349248590Smm p + header_bytes + block_info_bytes, &csize, 350248590Smm data->work_buffer, NULL, 0, 0, data->level); 351248590Smm break; 352248590Smm } 353248590Smm if (r != LZO_E_OK) { 354248590Smm archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 355248590Smm "Lzop compression failed: returned status %d", r); 356248590Smm return (ARCHIVE_FATAL); 357248590Smm } 358248590Smm 359248590Smm /* Store uncompressed size. */ 360248590Smm archive_be32enc(p + header_bytes, (uint32_t)usize); 361248590Smm /* Store the checksum of the uncompressed data. */ 362248590Smm checksum = lzo_adler32(1, data->uncompressed, usize); 363248590Smm archive_be32enc(p + header_bytes + 8, checksum); 364248590Smm 365248590Smm if (csize < usize) { 366248590Smm /* Store compressed size. */ 367248590Smm archive_be32enc(p + header_bytes + 4, (uint32_t)csize); 368248590Smm r = __archive_write_filter(f->next_filter, data->compressed, 369248590Smm header_bytes + block_info_bytes + csize); 370248590Smm } else { 371248590Smm /* 372248590Smm * This case, we output uncompressed data instead. 373248590Smm */ 374248590Smm /* Store uncompressed size as compressed size. */ 375248590Smm archive_be32enc(p + header_bytes + 4, (uint32_t)usize); 376248590Smm r = __archive_write_filter(f->next_filter, data->compressed, 377248590Smm header_bytes + block_info_bytes); 378248590Smm if (r != ARCHIVE_OK) 379248590Smm return (ARCHIVE_FATAL); 380248590Smm r = __archive_write_filter(f->next_filter, data->uncompressed, 381248590Smm usize); 382248590Smm } 383248590Smm 384248590Smm if (r != ARCHIVE_OK) 385248590Smm return (ARCHIVE_FATAL); 386248590Smm return (ARCHIVE_OK); 387248590Smm} 388248590Smm 389248590Smmstatic int 390248590Smmarchive_write_lzop_write(struct archive_write_filter *f, 391248590Smm const void *buff, size_t length) 392248590Smm{ 393248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 394248590Smm const char *p = buff; 395248590Smm int r; 396248590Smm 397248590Smm do { 398248590Smm if (data->uncompressed_avail_bytes > length) { 399248590Smm memcpy(data->uncompressed 400248590Smm + data->uncompressed_buffer_size 401248590Smm - data->uncompressed_avail_bytes, 402248590Smm p, length); 403248590Smm data->uncompressed_avail_bytes -= length; 404248590Smm return (ARCHIVE_OK); 405248590Smm } 406248590Smm 407248590Smm memcpy(data->uncompressed + data->uncompressed_buffer_size 408248590Smm - data->uncompressed_avail_bytes, 409248590Smm p, data->uncompressed_avail_bytes); 410248590Smm length -= data->uncompressed_avail_bytes; 411248590Smm p += data->uncompressed_avail_bytes; 412248590Smm data->uncompressed_avail_bytes = 0; 413248590Smm 414248590Smm r = drive_compressor(f); 415248590Smm if (r != ARCHIVE_OK) return (r); 416248590Smm data->uncompressed_avail_bytes = BLOCK_SIZE; 417248590Smm } while (length); 418248590Smm 419248590Smm return (ARCHIVE_OK); 420248590Smm} 421248590Smm 422248590Smmstatic int 423248590Smmarchive_write_lzop_close(struct archive_write_filter *f) 424248590Smm{ 425248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 426248590Smm const uint32_t endmark = 0; 427248590Smm int r; 428248590Smm 429248590Smm if (data->uncompressed_avail_bytes < BLOCK_SIZE) { 430248590Smm /* Compress and output remaining data. */ 431248590Smm r = drive_compressor(f); 432248590Smm if (r != ARCHIVE_OK) 433248590Smm return (r); 434248590Smm } 435248590Smm /* Write a zero uncompressed size as the end mark of the series of 436248590Smm * compressed block. */ 437358088Smm return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark)); 438248590Smm} 439248590Smm 440248590Smm#else 441248590Smmstatic int 442248590Smmarchive_write_lzop_open(struct archive_write_filter *f) 443248590Smm{ 444248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 445248590Smm struct archive_string as; 446248590Smm int r; 447248590Smm 448248590Smm archive_string_init(&as); 449248590Smm archive_strcpy(&as, "lzop"); 450248590Smm /* Specify compression level. */ 451248590Smm if (data->compression_level > 0) { 452248590Smm archive_strappend_char(&as, ' '); 453248590Smm archive_strappend_char(&as, '-'); 454248590Smm archive_strappend_char(&as, '0' + data->compression_level); 455248590Smm } 456248590Smm 457248590Smm r = __archive_write_program_open(f, data->pdata, as.s); 458248590Smm archive_string_free(&as); 459248590Smm return (r); 460248590Smm} 461248590Smm 462248590Smmstatic int 463248590Smmarchive_write_lzop_write(struct archive_write_filter *f, 464248590Smm const void *buff, size_t length) 465248590Smm{ 466248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 467248590Smm 468248590Smm return __archive_write_program_write(f, data->pdata, buff, length); 469248590Smm} 470248590Smm 471248590Smmstatic int 472248590Smmarchive_write_lzop_close(struct archive_write_filter *f) 473248590Smm{ 474248590Smm struct write_lzop *data = (struct write_lzop *)f->data; 475248590Smm 476248590Smm return __archive_write_program_close(f, data->pdata); 477248590Smm} 478248590Smm#endif 479