1/* 2 * Copyright (c) 2005 Rob Braun 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 * 3. Neither the name of Rob Braun nor the names of his contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29/* 30 * 19-Sep-2007 31 * DRI: Anders F Bjorklund <afb@rpm5.org> 32 */ 33/* 34 * Portions Copyright 2006, Apple Computer, Inc. 35 * Christopher Ryan <ryanc@apple.com> 36*/ 37 38 39#include "config.h" 40#ifndef HAVE_ASPRINTF 41#include "asprintf.h" 42#endif 43#include <stdio.h> 44#include <stdlib.h> 45#include <string.h> 46#include <sys/types.h> 47#include <errno.h> 48#ifdef HAVE_LIBLZMA 49#include <lzma.h> 50#endif 51#include "xar.h" 52#include "filetree.h" 53#include "io.h" 54 55#ifdef HAVE_LIBLZMA 56 57#ifndef UINT32_C 58#define UINT32_C(v) (v ## U) /* from <stdint.h> normally */ 59#endif 60#ifndef LZMA_VERSION 61#define LZMA_VERSION UINT32_C(40420000) /* = 4.42.0alpha6 */ 62#endif 63 64struct _lzma_context{ 65 uint8_t lzmacompressed; 66 lzma_stream lzma; 67 lzma_options_stream options; 68 lzma_allocator allocator; 69#if LZMA_VERSION < 40420010U 70 lzma_memory_limitter *limit; 71#else 72 lzma_memlimit *limit; 73#endif 74}; 75 76#define preset_level 7 77#define memory_limit 93*1024*1024 /* 1=1M, 5=24M, 6=39M, 7=93M, 8=185M, 9=369M */ 78 79#define LZMA_CONTEXT(x) ((struct _lzma_context *)(*x)) 80#endif 81 82int xar_lzma_fromheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) { 83#ifdef HAVE_LIBLZMA 84 85 if( !context || !LZMA_CONTEXT(context) ) 86 return 0; 87 88 if( LZMA_CONTEXT(context)->lzmacompressed){ 89 lzma_end(&LZMA_CONTEXT(context)->lzma); 90 } 91 92 /* free the context */ 93 free(LZMA_CONTEXT(context)); 94 *context = NULL; 95 96#endif 97 return 0; 98} 99 100int xar_lzma_fromheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) { 101 const char *opt; 102 xar_prop_t tmpp; 103#ifdef HAVE_LIBLZMA 104 void *out = NULL; 105 size_t outlen, offset = 0; 106 lzma_ret r; 107 108 /* on first run, we init the context and check the compression type */ 109 if( !LZMA_CONTEXT(context) ) { 110 *context = calloc(1,sizeof(struct _lzma_context)); 111 112 opt = NULL; 113 tmpp = xar_prop_pget(p, "encoding"); 114 if( tmpp ) 115 opt = xar_attr_pget(f, tmpp, "style"); 116 if( !opt ) return 0; 117 if( strcmp(opt, "application/x-lzma") != 0 ) return 0; 118 119 lzma_init_decoder(); 120 LZMA_CONTEXT(context)->lzma = LZMA_STREAM_INIT_VAR; 121 r = lzma_stream_decoder(&LZMA_CONTEXT(context)->lzma, NULL, NULL); 122 if( (r != LZMA_OK) ) { 123 xar_err_new(x); 124 xar_err_set_file(x, f); 125 xar_err_set_string(x, "Error decompressing file"); 126 xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION); 127 return -1; 128 } 129 LZMA_CONTEXT(context)->lzmacompressed = 1; 130 }else if( !LZMA_CONTEXT(context)->lzmacompressed ){ 131 /* once the context has been initialized, then we have already 132 checked the compression type, so we need only check if we 133 actually are compressed */ 134 return 0; 135 } 136 137 outlen = *inlen; 138 139 LZMA_CONTEXT(context)->lzma.next_in = *in; 140 LZMA_CONTEXT(context)->lzma.avail_in = *inlen; 141 LZMA_CONTEXT(context)->lzma.next_out = out; 142 LZMA_CONTEXT(context)->lzma.avail_out = 0; 143 144 while( LZMA_CONTEXT(context)->lzma.avail_in != 0 ) { 145 outlen = outlen * 2; 146 out = realloc(out, outlen); 147 if( out == NULL ) abort(); 148 149 LZMA_CONTEXT(context)->lzma.next_out = ((unsigned char *)out) + offset; 150 LZMA_CONTEXT(context)->lzma.avail_out = outlen - offset; 151 152 r = lzma_code(&(LZMA_CONTEXT(context)->lzma), LZMA_RUN); 153 if( (r != LZMA_OK) && (r != LZMA_STREAM_END) ) { 154 xar_err_new(x); 155 xar_err_set_file(x, f); 156 xar_err_set_errno(x, r); 157 xar_err_set_string(x, "Error decompressing file"); 158 xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION); 159 return -1; 160 } 161 offset += outlen - offset - LZMA_CONTEXT(context)->lzma.avail_out; 162 if( (r == LZMA_STREAM_END) && (offset == 0) ) 163 break; 164 } 165 166 free(*in); 167 *in = out; 168 *inlen = offset; 169#else 170 opt = NULL; 171 tmpp = xar_prop_pget(p, "encoding"); 172 if( tmpp ) 173 opt = xar_attr_pget(f, tmpp, "style"); 174 if( !opt ) return 0; 175 if( strcmp(opt, "application/x-lzma") != 0 ) return 0; 176 xar_err_new(x); 177 xar_err_set_file(x, f); 178 xar_err_set_errno(x, 0); 179 xar_err_set_string(x, "lzma support not compiled in."); 180 xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION); 181 182#endif /* HAVE_LIBLZMA */ 183 return 0; 184} 185 186int xar_lzma_toheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) { 187#ifdef HAVE_LIBLZMA 188 xar_prop_t tmpp; 189 190 if( LZMA_CONTEXT(context)->lzmacompressed){ 191 lzma_end(&LZMA_CONTEXT(context)->lzma); 192#if LZMA_VERSION < 40420010U 193 lzma_memory_limitter_end(LZMA_CONTEXT(context)->limit, 1); 194#else 195 lzma_memlimit_end(LZMA_CONTEXT(context)->limit, 1); 196#endif 197 198 tmpp = xar_prop_pset(f, p, "encoding", NULL); 199 if( tmpp ) 200 xar_attr_pset(f, tmpp, "style", "application/x-lzma"); 201 } 202 203 /* free the context */ 204 free(LZMA_CONTEXT(context)); 205 *context = NULL; 206 207#endif /* HAVE_LIBLZMA */ 208 return 0; 209} 210 211int32_t xar_lzma_toheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) { 212 const char *opt; 213#ifdef HAVE_LIBLZMA 214 void *out = NULL; 215 size_t outlen, offset = 0; 216 lzma_ret r; 217 218 /* on first run, we init the context and check the compression type */ 219 if( !LZMA_CONTEXT(context) ) { 220 int level = preset_level; 221 *context = calloc(1,sizeof(struct _lzma_context)); 222 223 opt = xar_opt_get(x, XAR_OPT_COMPRESSION); 224 if( !opt ) 225 return 0; 226 227 if( strcmp(opt, XAR_OPT_VAL_LZMA) != 0 ) 228 return 0; 229 230 opt = xar_opt_get(x, XAR_OPT_COMPRESSIONARG); 231 if( opt ) { 232 int tmp; 233 errno = 0; 234 tmp = strtol(opt, NULL, 10); 235 if( errno == 0 ) { 236 if( (level >= 0) && (level <= 9) ) 237 level = tmp; 238 } 239 } 240 241 lzma_init_encoder(); 242 LZMA_CONTEXT(context)->options.check = LZMA_CHECK_CRC64; 243 LZMA_CONTEXT(context)->options.has_crc32 = 1; /* true */ 244 LZMA_CONTEXT(context)->options.alignment = 0; 245#if defined (__ppc__) || defined (powerpc) || defined (__ppc64__) 246 LZMA_CONTEXT(context)->options.filters[0].id = LZMA_FILTER_POWERPC; 247#elif defined (__i386__) || defined (__amd64__) || defined(__x86_64__) 248 LZMA_CONTEXT(context)->options.filters[0].id = LZMA_FILTER_X86; 249#else 250 LZMA_CONTEXT(context)->options.filters[0].id = LZMA_FILTER_COPY; 251#endif 252 LZMA_CONTEXT(context)->options.filters[0].options = NULL; 253 LZMA_CONTEXT(context)->options.filters[1].id = LZMA_FILTER_LZMA; 254 LZMA_CONTEXT(context)->options.filters[1].options = (lzma_options_lzma *)(lzma_preset_lzma + level - 1); 255 /* Terminate the filter options array. */ 256 LZMA_CONTEXT(context)->options.filters[2].id = UINT64_MAX; 257 LZMA_CONTEXT(context)->lzma = LZMA_STREAM_INIT_VAR; 258#if LZMA_VERSION < 40420010U 259 LZMA_CONTEXT(context)->limit = lzma_memory_limitter_create(memory_limit); 260 LZMA_CONTEXT(context)->allocator.alloc = (void*) lzma_memory_alloc; 261 LZMA_CONTEXT(context)->allocator.free = (void*) lzma_memory_free; 262#else 263 LZMA_CONTEXT(context)->limit = lzma_memlimit_create(memory_limit); 264 LZMA_CONTEXT(context)->allocator.alloc = (void*) lzma_memlimit_alloc; 265 LZMA_CONTEXT(context)->allocator.free = (void*) lzma_memlimit_free; 266#endif 267 LZMA_CONTEXT(context)->allocator.opaque = LZMA_CONTEXT(context)->limit; 268 LZMA_CONTEXT(context)->lzma.allocator = &LZMA_CONTEXT(context)->allocator; 269 r = lzma_stream_encoder_single(&LZMA_CONTEXT(context)->lzma, &(LZMA_CONTEXT(context)->options)); 270 if( (r != LZMA_OK) ) { 271 xar_err_new(x); 272 xar_err_set_file(x, f); 273 xar_err_set_string(x, "Error compressing file"); 274 xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION); 275 return -1; 276 } 277 LZMA_CONTEXT(context)->lzmacompressed = 1; 278 if( *inlen == 0 ) 279 return 0; 280 }else if( !LZMA_CONTEXT(context)->lzmacompressed ){ 281 /* once the context has been initialized, then we have already 282 checked the compression type, so we need only check if we 283 actually are compressed */ 284 return 0; 285 } 286 287 outlen = *inlen/2; 288 if(outlen == 0) outlen = 1024; 289 LZMA_CONTEXT(context)->lzma.next_in = *in; 290 LZMA_CONTEXT(context)->lzma.avail_in = *inlen; 291 LZMA_CONTEXT(context)->lzma.next_out = out; 292 LZMA_CONTEXT(context)->lzma.avail_out = 0; 293 294 if( *inlen != 0 ) { 295 do { 296 outlen *= 2; 297 out = realloc(out, outlen); 298 if( out == NULL ) abort(); 299 300 LZMA_CONTEXT(context)->lzma.next_out = ((unsigned char *)out) + offset; 301 LZMA_CONTEXT(context)->lzma.avail_out = outlen - offset; 302 303 r = lzma_code(&LZMA_CONTEXT(context)->lzma, LZMA_RUN); 304 offset = outlen - LZMA_CONTEXT(context)->lzma.avail_out; 305 } while( r == LZMA_OK && LZMA_CONTEXT(context)->lzma.avail_in != 0); 306 } else { 307 do { 308 outlen *= 2; 309 out = realloc(out, outlen); 310 if( out == NULL ) abort(); 311 312 LZMA_CONTEXT(context)->lzma.next_out = ((unsigned char *)out) + offset; 313 LZMA_CONTEXT(context)->lzma.avail_out = outlen - offset; 314 315 r = lzma_code(&LZMA_CONTEXT(context)->lzma, LZMA_FINISH); 316 offset = outlen - LZMA_CONTEXT(context)->lzma.avail_out; 317 } while( r == LZMA_OK && r != LZMA_STREAM_END); 318 } 319 320 if( (r != LZMA_OK && r != LZMA_STREAM_END) ) { 321 xar_err_new(x); 322 xar_err_set_file(x, f); 323 xar_err_set_string(x, "Error compressing file"); 324 xar_err_set_errno(x, r); 325 xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION); 326 return -1; 327 } 328 329 free(*in); 330 *in = out; 331 *inlen = offset; 332#else 333 opt = xar_opt_get(x, XAR_OPT_COMPRESSION); 334 if( !opt ) 335 return 0; 336 if( strcmp(opt, XAR_OPT_VAL_LZMA) != 0 ) 337 return 0; 338 xar_err_new(x); 339 xar_err_set_file(x, f); 340 xar_err_set_errno(x, 0); 341 xar_err_set_string(x, "lzma support not compiled in."); 342 xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION); 343 344#endif /* HAVE_LIBLZMA */ 345 return 0; 346} 347