/* * Copyright (C) 2006, 2011 Filip Pizlo. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY FILIP PIZLO ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FILIP PIZLO OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "tsf_internal.h" #include "tsf_serial_protocol.h" #ifdef HAVE_ZLIB #include #endif #ifdef HAVE_BZIP2 #include #endif #define Cz(reader) ((z_stream*)((reader)->stream)) #define Cbz(reader) ((bz_stream*)((reader)->stream)) static tsf_bool_t tear_down_mode(tsf_adpt_rdr_t *reader, uint8_t **spillover, uint32_t *spillover_len) { tsf_bool_t stillok=tsf_true; if (reader->mode==TSF_ZIP_ZLIB) { #ifdef HAVE_ZLIB inflateEnd(Cz(reader)); #endif } else if (reader->mode==TSF_ZIP_BZIP2) { #ifdef HAVE_BZIP2 BZ2_bzDecompressEnd(Cbz(reader)); #endif } if (reader->mode==TSF_ZIP_ZLIB || reader->mode==TSF_ZIP_BZIP2) { free(reader->stream); if (spillover!=NULL && spillover_len!=NULL) { *spillover=malloc(reader->abstract.avail_in); if (*spillover==NULL) { tsf_set_errno("Could not allocate spillover buffer"); stillok=tsf_false; } else { *spillover_len=reader->abstract.avail_in; memcpy(*spillover,reader->abstract.next_in,*spillover_len); } } } else if (reader->mode==TSF_ZIP_NONE) { if (spillover!=NULL && spillover_len!=NULL) { *spillover=malloc(reader->puti-reader->geti); if (*spillover==NULL) { tsf_set_errno("Could not allocate spillover buffer"); stillok=tsf_false; } else { *spillover_len=reader->puti-reader->geti; memcpy(*spillover,reader->buf+reader->geti,*spillover_len); } } } if (reader->mode!=TSF_ZIP_UNKNOWN) { free(reader->buf); } reader->stream=NULL; reader->buf=NULL; reader->buf_size=0; reader->mode=TSF_ZIP_UNKNOWN; return stillok; } static tsf_bool_t tear_to_none(tsf_adpt_rdr_t *reader) { tsf_assert(reader->mode==TSF_ZIP_ZLIB || reader->mode==TSF_ZIP_BZIP2); if (reader->mode==TSF_ZIP_ZLIB) { #ifdef HAVE_ZLIB inflateEnd(Cz(reader)); #endif } else { #ifdef HAVE_BZIP2 BZ2_bzDecompressEnd(Cbz(reader)); #endif } free(reader->stream); reader->mode=TSF_ZIP_UNKNOWN; if (reader->nozip_buf_size==reader->buf_size) { reader->geti=reader->abstract.next_in-reader->buf; reader->puti=reader->geti+reader->abstract.avail_in; } else { uint8_t *buf; reader->buf_size=tsf_max(reader->nozip_buf_size, reader->abstract.avail_in); buf=malloc(reader->buf_size); if (buf==NULL) { tsf_set_errno("Could not allocate buffer in tear_to_none() in " "tsf_adaptive_reader.c"); free(reader->buf); /* oh man, this leaves the reader in a flaky state */ return tsf_false; } memcpy(buf, reader->abstract.next_in, reader->abstract.avail_in); free(reader->buf); reader->geti=0; reader->puti=reader->abstract.avail_in; reader->buf=buf; } reader->mode=TSF_ZIP_NONE; return tsf_true; } static tsf_bool_t select_mode(tsf_adpt_rdr_t *reader, uint8_t *spillover, uint32_t spillover_len, tsf_bool_t free_spillover) { uint8_t magic[4]; uint32_t tocopy=tsf_min(4,spillover_len); void *tofree; if (free_spillover && spillover_len>0) { tofree=spillover; } else { tofree=NULL; } memcpy(magic,spillover,tocopy); spillover+=tocopy; spillover_len-=tocopy; if (tocopy<4) { tsf_assert(spillover_len==0); if (!tsf_full_read_of_partial(reader->reader,reader->reader_arg, magic+tocopy,4-tocopy)) { if (tofree!=NULL) free(tofree); return tsf_false; } } if (!memcmp(magic,TSF_SP_MAGIC_CODE,TSF_SP_MAGIC_LEN)) { reader->buf_size=tsf_max(reader->nozip_buf_size, spillover_len+TSF_SP_MAGIC_LEN); reader->buf=malloc(reader->buf_size); if (reader->buf==NULL) { tsf_set_errno("Could not allocate buffer"); if (tofree!=NULL) free(tofree); return tsf_false; } memcpy(reader->buf,spillover,spillover_len); memcpy(reader->buf+spillover_len,TSF_SP_MAGIC_CODE,TSF_SP_MAGIC_LEN); reader->puti=spillover_len+TSF_SP_MAGIC_LEN; reader->geti=0; if (tofree!=NULL) free(tofree); reader->mode=TSF_ZIP_NONE; } else if (!memcmp(magic,TSF_SP_BZIP2_MAGIC_CODE,TSF_SP_MAGIC_LEN) || !memcmp(magic,TSF_SP_ZLIB_MAGIC_CODE,TSF_SP_MAGIC_LEN)) { int res; if (!memcmp(magic,TSF_SP_ZLIB_MAGIC_CODE,TSF_SP_MAGIC_LEN)) { reader->buf_size=tsf_max(reader->attr.z.buf_size,spillover_len); } else { reader->buf_size=tsf_max(reader->attr.b.buf_size,spillover_len); } reader->buf=malloc(reader->buf_size); if (reader->buf==NULL) { tsf_set_errno("Could not allocate buffer"); if (tofree!=NULL) free(tofree); return tsf_false; } memcpy(reader->buf,spillover,spillover_len); if (tofree!=NULL) free(tofree); reader->abstract.next_in=reader->buf; reader->abstract.avail_in=spillover_len; if (!memcmp(magic,TSF_SP_ZLIB_MAGIC_CODE,TSF_SP_MAGIC_LEN)) { if (!reader->attr.z.allow) { tsf_set_error(TSF_E_PARSE_ERROR, "The stream is compressed using ZLib, but " "the given attributes specify that " "ZLib is not allowed"); if (tofree!=NULL) free(tofree); free(reader->buf); return tsf_false; } #ifdef HAVE_ZLIB reader->stream=malloc(sizeof(z_stream)); if (reader->stream==NULL) { tsf_set_errno("Could not malloc z_stream"); free(reader->buf); return tsf_false; } Cz(reader)->zalloc=NULL; Cz(reader)->zfree=NULL; Cz(reader)->opaque=NULL; if (reader->attr.z.advanced_init) { res=inflateInit2(Cz(reader), reader->attr.z.windowBits); } else { res=inflateInit(Cz(reader)); } if (res!=Z_OK) { tsf_set_zlib_error(res,Cz(reader)->msg, "Trying to initialize inflation in ZLib"); free(Cz(reader)); free(reader->buf); return tsf_false; } reader->mode=TSF_ZIP_ZLIB; #else tsf_set_error(TSF_E_NOT_SUPPORTED, "TSF_ZIP_ZLIB is not supported by " "tsf_zip_reader_create() because ZLib support " "was not configured into the TSF library"); if (tofree!=NULL) free(tofree); free(reader->buf); return tsf_false; #endif } else { if (!reader->attr.b.allow) { tsf_set_error(TSF_E_PARSE_ERROR, "The stream is compressed using libbzip2, but " "the given attributes specify that " "libbzip2 is not allowed"); if (tofree!=NULL) free(tofree); free(reader->buf); return tsf_false; } #ifdef HAVE_BZIP2 reader->stream=malloc(sizeof(bz_stream)); if (reader->stream==NULL) { tsf_set_errno("Could not malloc bz_stream"); free(reader->buf); return tsf_false; } Cbz(reader)->bzalloc=NULL; Cbz(reader)->bzfree=NULL; Cbz(reader)->opaque=NULL; res=BZ2_bzDecompressInit(Cbz(reader), reader->attr.b.verbosity, reader->attr.b.small); if (res!=BZ_OK) { tsf_set_libbzip2_error(res, "Trying to initialize decompression in libbzip2"); free(Cbz(reader)); free(reader->buf); return tsf_false; } reader->mode=TSF_ZIP_BZIP2; #else tsf_set_error(TSF_E_NOT_SUPPORTED, "TSF_ZIP_BZIP2 is not supported by " "tsf_zip_reader_create() because libbzip2 " "support was not configured into the TSF " "library"); free(reader->buf); if (tofree!=NULL) free(tofree); return tsf_false; #endif } } else { if (tofree!=NULL) free(tofree); tsf_set_error(TSF_E_PARSE_ERROR, "Not a valid magic code for any TSF stream file format"); return tsf_false; } return tsf_true; } static tsf_bool_t tear_down_and_select_mode(tsf_adpt_rdr_t *reader) { uint8_t *spillover; uint32_t spillover_len; return tear_down_mode(reader,&spillover,&spillover_len) && select_mode(reader,spillover,spillover_len,tsf_true); } tsf_adpt_rdr_t *tsf_adaptive_reader_create(tsf_partial_reader_t reader, void *reader_arg, uint32_t nozip_buf_size, const tsf_zip_rdr_attr_t *attr) { tsf_adpt_rdr_t *result=malloc(sizeof(tsf_adpt_rdr_t)); if (result==NULL) { tsf_set_errno("Could not malloc tsf_adpt_rdr_t"); return NULL; } result->mode=TSF_ZIP_UNKNOWN; result->reader=reader; result->reader_arg=reader_arg; result->stream=NULL; result->expect_switch=tsf_true; result->buf=NULL; result->buf_size=0; if (attr==NULL) { tsf_zip_rdr_attr_get_default(&(result->attr)); } else { result->attr=*attr; } result->nozip_buf_size=nozip_buf_size; return result; } void tsf_adaptive_reader_destroy(tsf_adpt_rdr_t *reader) { tear_down_mode(reader,NULL,NULL); free(reader); } void tsf_adaptive_reader_hint_switch(tsf_adpt_rdr_t *reader) { reader->expect_switch=tsf_true; } tsf_bool_t tsf_adaptive_reader_read(void *_reader, void *_data, uint32_t len) { tsf_adpt_rdr_t *reader=_reader; uint8_t *data=_data; if (len==0) { return tsf_true; } /* this just constitutes lazy initialization upon read */ if (reader->mode==TSF_ZIP_UNKNOWN) { if (!select_mode(reader,NULL,0,tsf_false)) { return tsf_false; } } switch (reader->mode) { case TSF_ZIP_NONE: { /* ok - this code is complicated. it is complicated because it needs to deal with the possibility of a format change - if the magic code for one of the zip formats comes in, this thing's job is to recognize it and switch from no-zip buffering to unzipping. we use the hint_switch() function calls as a hint of when to look for the format changes. */ if (reader->expect_switch && reader->puti>reader->geti) { reader->expect_switch=tsf_false; if (reader->buf[reader->geti]==TSF_SP_C_SWITCH_FORMAT) { if (!tear_down_and_select_mode(reader)) { return tsf_false; } return tsf_adaptive_reader_read(reader,data,len); } } uint32_t toread=tsf_min(reader->puti-reader->geti,len); memcpy(data,reader->buf+reader->geti,toread); reader->geti+=toread; data+=toread; len-=toread; if (len>reader->buf_size) { tsf_assert(reader->geti==reader->puti); if (reader->expect_switch) { int32_t res=reader->reader(reader->reader_arg, data,len); if (res<0) { return tsf_false; } tsf_assert(res>0); if (data[0]==TSF_SP_C_SWITCH_FORMAT) { tear_down_mode(reader,NULL,NULL); if (!select_mode(reader,data,res,tsf_false)) { return tsf_false; } return tsf_adaptive_reader_read(reader,data,len); } else { data+=res; len-=res; } } if (!tsf_full_read_of_partial(reader->reader, reader->reader_arg, data, len)) { if (tsf_get_error_code()==TSF_E_UNEXPECTED_EOF && data!=_data) { tsf_set_error(TSF_E_ERRONEOUS_EOF, "End-of-file after part of a full buffered " "read; original error: %s",tsf_get_error()); } return tsf_false; } } else if (len>0) { reader->puti=0; reader->geti=0; for (;;) { int32_t res; uint32_t tocopy; res=reader->reader(reader->reader_arg, reader->buf, reader->buf_size); if (res<0) { if (tsf_get_error_code()==TSF_E_UNEXPECTED_EOF && data!=_data) { tsf_set_error(TSF_E_ERRONEOUS_EOF, "End-of-file after part of a full buffered " "read; original error: %s",tsf_get_error()); } return tsf_false; } tsf_assert(res>0); if (reader->expect_switch) { reader->expect_switch=tsf_false; if (reader->buf[0]==TSF_SP_C_SWITCH_FORMAT) { reader->puti=res; if (!tear_down_and_select_mode(reader)) { return tsf_false; } return tsf_adaptive_reader_read(reader,data,len); } } tocopy=tsf_min(res,len); memcpy(data,reader->buf,tocopy); data+=tocopy; len-=tocopy; if (len==0) { reader->puti=res; reader->geti=tocopy; break; } } } break; } case TSF_ZIP_ZLIB: case TSF_ZIP_BZIP2: { reader->abstract.next_out=data; reader->abstract.avail_out=len; for (;;) { int res; /* is there ever a case where we would want to call inflate even though the input buffer is empty? */ if (reader->abstract.avail_in!=0) { res=tsf_zip_abstract_inflate(reader->mode, reader->stream, &(reader->abstract), TSF_ZIP_ACT_RUN); if (res==TSF_ZIP_RES_ERROR) { return tsf_false; } else if (res==TSF_ZIP_RES_END) { if (reader->abstract.avail_out!=0) { tsf_set_error(TSF_E_PARSE_ERROR, "A read request straddles a format change"); } tear_to_none(reader); } if (reader->abstract.avail_out==0) { break; } tsf_assert(reader->abstract.avail_in==0); } res=reader->reader(reader->reader_arg, reader->buf, reader->buf_size); if (res==-1) { if (tsf_get_error_code()==TSF_E_UNEXPECTED_EOF) { tsf_set_error(TSF_E_ERRONEOUS_EOF, "Got an EOF from the reader before the " "decompressor had reached end of stream"); } return tsf_false; } reader->abstract.next_in=reader->buf; reader->abstract.avail_in=res; } break; } default: tsf_set_error(TSF_E_INTERNAL, "Invalid reader mode in tsf_adaptive_reader_read()"); return tsf_false; } return tsf_true; } uint32_t tsf_adaptive_reader_get_remaining(tsf_adpt_rdr_t *reader) { switch (reader->mode) { case TSF_ZIP_UNKNOWN: return 0; case TSF_ZIP_NONE: return reader->puti-reader->geti; case TSF_ZIP_ZLIB: case TSF_ZIP_BZIP2: return reader->abstract.avail_in; default: /* should never happen */ tsf_abort("bad reader mode"); } }