1/* 2 * Copyright (c) 2005-2007 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 * 03-Apr-2005 31 * DRI: Rob Braun <bbraun@synack.net> 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 <zlib.h> 48#include <errno.h> 49#include "xar.h" 50#include "filetree.h" 51#include "io.h" 52 53struct _gzip_context{ 54 uint8_t gzipcompressed; 55 uint64_t count; 56 z_stream z; 57}; 58 59#define GZIP_CONTEXT(x) ((struct _gzip_context *)(*x)) 60 61int xar_gzip_fromheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) { 62 63 if( !context || !GZIP_CONTEXT(context) ) 64 return 0; 65 66 if( GZIP_CONTEXT(context)->gzipcompressed){ 67 inflateEnd(&GZIP_CONTEXT(context)->z); 68 } 69 70 /* free the context */ 71 free(GZIP_CONTEXT(context)); 72 *context = NULL; 73 74 return 0; 75} 76 77int xar_gzip_fromheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) { 78 const char *opt; 79 void *out = NULL; 80 size_t outlen, offset = 0; 81 int r; 82 xar_prop_t tmpp; 83 84 /* on first run, we init the context and check the compression type */ 85 if( !GZIP_CONTEXT(context) ) { 86 *context = calloc(1,sizeof(struct _gzip_context)); 87 88 opt = NULL; 89 tmpp = xar_prop_pget(p, "encoding"); 90 if( tmpp ) 91 opt = xar_attr_pget(f, tmpp, "style"); 92 if( !opt ) return 0; 93 if( strcmp(opt, "application/x-gzip") != 0 ) return 0; 94 95 inflateInit(&GZIP_CONTEXT(context)->z); 96 GZIP_CONTEXT(context)->gzipcompressed = 1; 97 }else if( !GZIP_CONTEXT(context)->gzipcompressed ){ 98 /* once the context has been initialized, then we have already 99 checked the compression type, so we need only check if we 100 actually are compressed */ 101 return 0; 102 } 103 104 outlen = *inlen; 105 106 GZIP_CONTEXT(context)->z.next_in = *in; 107 GZIP_CONTEXT(context)->z.avail_in = *inlen; 108 GZIP_CONTEXT(context)->z.next_out = out; 109 GZIP_CONTEXT(context)->z.avail_out = 0; 110 111 while( GZIP_CONTEXT(context)->z.avail_in != 0 ) { 112 outlen = outlen * 2; 113 out = realloc(out, outlen); 114 if( out == NULL ) abort(); 115 116 GZIP_CONTEXT(context)->z.next_out = ((unsigned char *)out) + offset; 117 GZIP_CONTEXT(context)->z.avail_out = outlen - offset; 118 119 r = inflate(&(GZIP_CONTEXT(context)->z), Z_NO_FLUSH); 120 if( (r != Z_OK) && (r != Z_STREAM_END) ) { 121 xar_err_new(x); 122 xar_err_set_file(x, f); 123 xar_err_set_string(x, "Error decompressing file"); 124 xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION); 125 return -1; 126 } 127 offset += outlen - offset - GZIP_CONTEXT(context)->z.avail_out; 128 if( (r == Z_STREAM_END) && (offset == 0) ) 129 break; 130 } 131 132 free(*in); 133 *in = out; 134 *inlen = offset; 135 return 0; 136} 137 138int xar_gzip_toheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) { 139 xar_prop_t tmpp; 140 141 if( GZIP_CONTEXT(context)->gzipcompressed){ 142 deflateEnd(&GZIP_CONTEXT(context)->z); 143 144 if( GZIP_CONTEXT(context)->count ) { 145 tmpp = xar_prop_pset(f, p, "encoding", NULL); 146 if( tmpp ) 147 xar_attr_pset(f, tmpp, "style", "application/x-gzip"); 148 } 149 } 150 151 /* free the context */ 152 free(GZIP_CONTEXT(context)); 153 *context = NULL; 154 155 return 0; 156} 157 158int32_t xar_gzip_toheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) { 159 void *out = NULL; 160 size_t outlen, offset = 0; 161 int r; 162 const char *opt; 163 164 /* on first run, we init the context and check the compression type */ 165 if( !GZIP_CONTEXT(context) ) { 166 int level = Z_BEST_COMPRESSION; 167 *context = calloc(1,sizeof(struct _gzip_context)); 168 169 opt = xar_opt_get(x, XAR_OPT_COMPRESSION); 170 if( !opt ) 171 return 0; 172 173 if( strcmp(opt, XAR_OPT_VAL_GZIP) != 0 ) 174 return 0; 175 176 opt = xar_opt_get(x, XAR_OPT_COMPRESSIONARG); 177 if( opt ) { 178 int tmp; 179 errno = 0; 180 tmp = strtol(opt, NULL, 10); 181 if( errno == 0 ) { 182 if( (level >= 0) && (level <= 9) ) 183 level = tmp; 184 } 185 } 186 187 deflateInit(&GZIP_CONTEXT(context)->z, level); 188 GZIP_CONTEXT(context)->gzipcompressed = 1; 189 if( *inlen == 0 ) 190 return 0; 191 }else if( !GZIP_CONTEXT(context)->gzipcompressed ){ 192 /* once the context has been initialized, then we have already 193 checked the compression type, so we need only check if we 194 actually are compressed */ 195 return 0; 196 } 197 198 outlen = *inlen/2; 199 if(outlen == 0) outlen = 1024; 200 GZIP_CONTEXT(context)->z.next_in = *in; 201 GZIP_CONTEXT(context)->z.avail_in = *inlen; 202 GZIP_CONTEXT(context)->z.next_out = out; 203 GZIP_CONTEXT(context)->z.avail_out = 0; 204 205 if( *inlen != 0 ) { 206 do { 207 outlen *= 2; 208 out = realloc(out, outlen); 209 if( out == NULL ) abort(); 210 211 GZIP_CONTEXT(context)->z.next_out = ((unsigned char *)out) + offset; 212 GZIP_CONTEXT(context)->z.avail_out = outlen - offset; 213 214 r = deflate(&GZIP_CONTEXT(context)->z, Z_NO_FLUSH); 215 offset = outlen - GZIP_CONTEXT(context)->z.avail_out; 216 } while( r == Z_OK && GZIP_CONTEXT(context)->z.avail_in != 0 ); 217 } else { 218 do { 219 outlen *= 2; 220 out = realloc(out, outlen); 221 if( out == NULL ) abort(); 222 223 GZIP_CONTEXT(context)->z.next_out = ((unsigned char *)out) + offset; 224 GZIP_CONTEXT(context)->z.avail_out = outlen - offset; 225 226 r = deflate(&GZIP_CONTEXT(context)->z, Z_FINISH); 227 offset = outlen - GZIP_CONTEXT(context)->z.avail_out; 228 } while( r == Z_OK && r != Z_STREAM_END /* no-op */); 229 } 230 231 if( (r != Z_OK && r != Z_STREAM_END) ) { 232 xar_err_new(x); 233 xar_err_set_file(x, f); 234 xar_err_set_string(x, "Error compressing file"); 235 xar_err_set_errno(x, r); 236 xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION); 237 return -1; 238 } 239 240 free(*in); 241 *in = out; 242 GZIP_CONTEXT(context)->count += *inlen; 243 *inlen = offset; 244 return 0; 245} 246