/* xdelta 3 - delta compression tools and library * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007. Joshua P. MacDonald * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _XDELTA3_DECODE_H_ #define _XDELTA3_DECODE_H_ #include "xdelta3-internal.h" #define SRCORTGT(x) ((((x) & VCD_SRCORTGT) == VCD_SOURCE) ? \ VCD_SOURCE : ((((x) & VCD_SRCORTGT) == \ VCD_TARGET) ? VCD_TARGET : 0)) /* Initialize the decoder for a new window. The dec_tgtlen value is * preserved across successive window decodings, and the update to * dec_winstart is delayed until a new window actually starts. This * is to avoid throwing an error due to overflow until the last * possible moment. This makes it possible to encode exactly 4GB * through a 32-bit encoder. */ static int xd3_decode_init_window (xd3_stream *stream) { stream->dec_cpylen = 0; stream->dec_cpyoff = 0; stream->dec_cksumbytes = 0; xd3_init_cache (& stream->acache); return 0; } /* Allocates buffer space for the target window and possibly the * VCD_TARGET copy-window. Also sets the base of the two copy * segments. */ static int xd3_decode_setup_buffers (xd3_stream *stream) { /* If VCD_TARGET is set then the previous buffer may be reused. */ if (stream->dec_win_ind & VCD_TARGET) { /* But this implementation only supports copying from the last * target window. If the offset is outside that range, it can't * be done. */ if (stream->dec_cpyoff < stream->dec_laststart) { stream->msg = "unsupported VCD_TARGET offset"; return XD3_INVALID_INPUT; } /* See if the two windows are the same. This indicates the * first time VCD_TARGET is used. This causes a second buffer * to be allocated, after that the two are swapped in the * DEC_FINISH case. */ if (stream->dec_lastwin == stream->next_out) { stream->next_out = NULL; stream->space_out = 0; } // TODO: VCD_TARGET mode, this is broken stream->dec_cpyaddrbase = stream->dec_lastwin + (usize_t) (stream->dec_cpyoff - stream->dec_laststart); } /* See if the current output window is large enough. */ if (stream->space_out < stream->dec_tgtlen) { xd3_free (stream, stream->dec_buffer); stream->space_out = xd3_round_blksize (stream->dec_tgtlen, XD3_ALLOCSIZE); if ((stream->dec_buffer = (uint8_t*) xd3_alloc (stream, stream->space_out, 1)) == NULL) { return ENOMEM; } stream->next_out = stream->dec_buffer; } /* dec_tgtaddrbase refers to an invalid base address, but it is * always used with a sufficiently large instruction offset (i.e., * beyond the copy window). This condition is enforced by * xd3_decode_output_halfinst. */ stream->dec_tgtaddrbase = stream->next_out - stream->dec_cpylen; return 0; } static int xd3_decode_allocate (xd3_stream *stream, usize_t size, uint8_t **buf_ptr, usize_t *buf_alloc) { if (*buf_ptr != NULL && *buf_alloc < size) { xd3_free (stream, *buf_ptr); *buf_ptr = NULL; } if (*buf_ptr == NULL) { *buf_alloc = xd3_round_blksize (size, XD3_ALLOCSIZE); if ((*buf_ptr = (uint8_t*) xd3_alloc (stream, *buf_alloc, 1)) == NULL) { return ENOMEM; } } return 0; } static int xd3_decode_section (xd3_stream *stream, xd3_desect *section, xd3_decode_state nstate, int copy) { XD3_ASSERT (section->pos <= section->size); XD3_ASSERT (stream->dec_state != nstate); if (section->pos < section->size) { usize_t sect_take; if (stream->avail_in == 0) { return XD3_INPUT; } if ((copy == 0) && (section->pos == 0)) { /* No allocation/copy needed */ section->buf = stream->next_in; sect_take = section->size; } else { usize_t sect_need = section->size - section->pos; /* Allocate and copy */ sect_take = min (sect_need, stream->avail_in); if (section->pos == 0) { int ret; if ((ret = xd3_decode_allocate (stream, section->size, & section->copied1, & section->alloc1))) { return ret; } section->buf = section->copied1; } memcpy (section->copied1 + section->pos, stream->next_in, sect_take); } section->pos += sect_take; stream->dec_winbytes += sect_take; DECODE_INPUT (sect_take); } if (section->pos < section->size) { stream->msg = "further input required"; return XD3_INPUT; } XD3_ASSERT (section->pos == section->size); stream->dec_state = nstate; section->buf_max = section->buf + section->size; section->pos = 0; return 0; } /* Decode the size and address for half of an instruction (i.e., a * single opcode). This updates the stream->dec_position, which are * bytes already output prior to processing this instruction. Perform * bounds checking for sizes and copy addresses, which uses the * dec_position (which is why these checks are done here). */ static int xd3_decode_parse_halfinst (xd3_stream *stream, xd3_hinst *inst) { int ret; /* If the size from the instruction table is zero then read a size value. */ if ((inst->size == 0) && (ret = xd3_read_size (stream, & stream->inst_sect.buf, stream->inst_sect.buf_max, & inst->size))) { return XD3_INVALID_INPUT; } /* For copy instructions, read address. */ if (inst->type >= XD3_CPY) { IF_DEBUG2 ({ static int cnt = 0; XPR(NT "DECODE:%u: COPY at %"Q"u (winoffset %u) size %u winaddr %u\n", cnt++, stream->total_out + (stream->dec_position - stream->dec_cpylen), (stream->dec_position - stream->dec_cpylen), inst->size, inst->addr); }); if ((ret = xd3_decode_address (stream, stream->dec_position, inst->type - XD3_CPY, & stream->addr_sect.buf, stream->addr_sect.buf_max, & inst->addr))) { return ret; } /* Cannot copy an address before it is filled-in. */ if (inst->addr >= stream->dec_position) { stream->msg = "address too large"; return XD3_INVALID_INPUT; } /* Check: a VCD_TARGET or VCD_SOURCE copy cannot exceed the remaining * buffer space in its own segment. */ if (inst->addr < stream->dec_cpylen && inst->addr + inst->size > stream->dec_cpylen) { stream->msg = "size too large"; return XD3_INVALID_INPUT; } } else { IF_DEBUG2 ({ if (inst->type == XD3_ADD) { static int cnt; XPR(NT "DECODE:%d: ADD at %"Q"u (winoffset %u) size %u\n", cnt++, (stream->total_out + stream->dec_position - stream->dec_cpylen), stream->dec_position - stream->dec_cpylen, inst->size); } else { static int cnt; XD3_ASSERT (inst->type == XD3_RUN); XPR(NT "DECODE:%d: RUN at %"Q"u (winoffset %u) size %u\n", cnt++, stream->total_out + stream->dec_position - stream->dec_cpylen, stream->dec_position - stream->dec_cpylen, inst->size); } }); } /* Check: The instruction will not overflow the output buffer. */ if (stream->dec_position + inst->size > stream->dec_maxpos) { stream->msg = "size too large"; return XD3_INVALID_INPUT; } stream->dec_position += inst->size; return 0; } /* Decode a single opcode and then decode the two half-instructions. */ static int xd3_decode_instruction (xd3_stream *stream) { int ret; const xd3_dinst *inst; if (stream->inst_sect.buf == stream->inst_sect.buf_max) { stream->msg = "instruction underflow"; return XD3_INVALID_INPUT; } inst = &stream->code_table[*stream->inst_sect.buf++]; stream->dec_current1.type = inst->type1; stream->dec_current2.type = inst->type2; stream->dec_current1.size = inst->size1; stream->dec_current2.size = inst->size2; /* For each instruction with a real operation, decode the * corresponding size and addresses if necessary. Assume a * code-table may have NOOP in either position, although this is * unlikely. */ if (inst->type1 != XD3_NOOP && (ret = xd3_decode_parse_halfinst (stream, & stream->dec_current1))) { return ret; } if (inst->type2 != XD3_NOOP && (ret = xd3_decode_parse_halfinst (stream, & stream->dec_current2))) { return ret; } return 0; } /* Output the result of a single half-instruction. OPT: This the decoder hotspot. Modifies "hinst", see below. */ static int xd3_decode_output_halfinst (xd3_stream *stream, xd3_hinst *inst) { /* This method is reentrant for copy instructions which may return * XD3_GETSRCBLK to the caller. Each time through a copy takes the * minimum of inst->size and the available space on whichever block * supplies the data */ usize_t take = inst->size; XD3_ASSERT (inst->type != XD3_NOOP); switch (inst->type) { case XD3_RUN: { /* Only require a single data byte. */ if (stream->data_sect.buf == stream->data_sect.buf_max) { stream->msg = "data underflow"; return XD3_INVALID_INPUT; } memset (stream->next_out + stream->avail_out, stream->data_sect.buf[0], take); stream->data_sect.buf += 1; stream->avail_out += take; inst->type = XD3_NOOP; break; } case XD3_ADD: { /* Require at least TAKE data bytes. */ if (stream->data_sect.buf + take > stream->data_sect.buf_max) { stream->msg = "data underflow"; return XD3_INVALID_INPUT; } memcpy (stream->next_out + stream->avail_out, stream->data_sect.buf, take); stream->data_sect.buf += take; stream->avail_out += take; inst->type = XD3_NOOP; break; } default: { usize_t i; const uint8_t *src; uint8_t *dst; int overlap; /* See if it copies from the VCD_TARGET/VCD_SOURCE window or * the target window. Out-of-bounds checks for the addresses * and sizes are performed in xd3_decode_parse_halfinst. This * if/else must set "overlap", "src", and "dst". */ if (inst->addr < stream->dec_cpylen) { /* In both branches we are copying from outside the * current decoder window, the first (VCD_TARGET) is * unimplemented. */ overlap = 0; /* This branch sets "src". As a side-effect, we modify * "inst" so that if we reenter this method after a * XD3_GETSRCBLK response the state is correct. So if the * instruction can be fulfilled by a contiguous block of * memory then we will set: * * inst->type = XD3_NOOP; * inst->size = 0; */ if (stream->dec_win_ind & VCD_TARGET) { /* TODO: Users have requested long-distance copies of * similar material within a target (e.g., for dup * supression in backups). */ inst->size = 0; inst->type = XD3_NOOP; stream->msg = "VCD_TARGET not implemented"; return XD3_UNIMPLEMENTED; } else { /* In this case we have to read a source block, which * could return control to the caller. We need to * know the first block number needed for this * copy. */ xd3_source *source = stream->src; xoff_t block = source->cpyoff_blocks; usize_t blkoff = source->cpyoff_blkoff; const usize_t blksize = source->blksize; int ret; xd3_blksize_add (&block, &blkoff, source, inst->addr); XD3_ASSERT (blkoff < blksize); if ((ret = xd3_getblk (stream, block))) { /* could be a XD3_GETSRCBLK failure. */ if (ret == XD3_TOOFARBACK) { stream->msg = "non-seekable source in decode"; ret = XD3_INTERNAL; } return ret; } src = source->curblk + blkoff; /* This block is either full, or a partial block that * must contain enough bytes. */ if ((source->onblk != blksize) && (blkoff + take > source->onblk)) { IF_DEBUG1 (XPR(NT "[srcfile] short at blkno %"Q"u onblk " "%u blksize %u blkoff %u take %u\n", block, source->onblk, blksize, blkoff, take)); stream->msg = "source file too short"; return XD3_INVALID_INPUT; } XD3_ASSERT (blkoff != blksize); /* Check if we have enough data on this block to * finish the instruction. */ if (blkoff + take <= blksize) { inst->type = XD3_NOOP; inst->size = 0; } else { take = blksize - blkoff; inst->size -= take; inst->addr += take; /* because (blkoff + take > blksize), above */ XD3_ASSERT (inst->size != 0); } } } else { /* TODO: the memcpy/overlap optimization, etc. Overlap * here could be more specific, it's whether (inst->addr - * srclen) + inst->size > input_pos ? And is the system * memcpy really any good? */ overlap = 1; /* For a target-window copy, we know the entire range is * in-memory. The dec_tgtaddrbase is negatively offset by * dec_cpylen because the addresses start beyond that * point. */ src = stream->dec_tgtaddrbase + inst->addr; inst->type = XD3_NOOP; inst->size = 0; } dst = stream->next_out + stream->avail_out; stream->avail_out += take; if (overlap) { /* Can't just memcpy here due to possible overlap. */ for (i = take; i != 0; i -= 1) { *dst++ = *src++; } } else { memcpy (dst, src, take); } } } return 0; } static int xd3_decode_finish_window (xd3_stream *stream) { stream->dec_winbytes = 0; stream->dec_state = DEC_FINISH; stream->data_sect.pos = 0; stream->inst_sect.pos = 0; stream->addr_sect.pos = 0; return XD3_OUTPUT; } static int xd3_decode_secondary_sections (xd3_stream *secondary_stream) { #if SECONDARY_ANY int ret; #define DECODE_SECONDARY_SECTION(UPPER,LOWER) \ ((secondary_stream->dec_del_ind & VCD_ ## UPPER ## COMP) && \ (ret = xd3_decode_secondary (secondary_stream, \ & secondary_stream-> LOWER ## _sect, \ & xd3_sec_ ## LOWER (secondary_stream)))) if (DECODE_SECONDARY_SECTION (DATA, data) || DECODE_SECONDARY_SECTION (INST, inst) || DECODE_SECONDARY_SECTION (ADDR, addr)) { return ret; } #undef DECODE_SECONDARY_SECTION #endif return 0; } static int xd3_decode_sections (xd3_stream *stream) { usize_t need, more, take; int copy, ret; if ((stream->flags & XD3_JUST_HDR) != 0) { /* Nothing left to do. */ return xd3_decode_finish_window (stream); } /* To avoid copying, need this much data available */ need = (stream->inst_sect.size + stream->addr_sect.size + stream->data_sect.size); /* The window may be entirely processed. */ XD3_ASSERT (stream->dec_winbytes <= need); /* Compute how much more input is needed. */ more = (need - stream->dec_winbytes); /* How much to consume. */ take = min (more, stream->avail_in); /* See if the input is completely available, to avoid copy. */ copy = (take != more); /* If the window is skipped... */ if ((stream->flags & XD3_SKIP_WINDOW) != 0) { /* Skip the available input. */ DECODE_INPUT (take); stream->dec_winbytes += take; if (copy) { stream->msg = "further input required"; return XD3_INPUT; } return xd3_decode_finish_window (stream); } /* Process all but the DATA section. */ switch (stream->dec_state) { default: stream->msg = "internal error"; return XD3_INVALID_INPUT; case DEC_DATA: if ((ret = xd3_decode_section (stream, & stream->data_sect, DEC_INST, copy))) { return ret; } case DEC_INST: if ((ret = xd3_decode_section (stream, & stream->inst_sect, DEC_ADDR, copy))) { return ret; } case DEC_ADDR: if ((ret = xd3_decode_section (stream, & stream->addr_sect, DEC_EMIT, copy))) { return ret; } } XD3_ASSERT (stream->dec_winbytes == need); if ((ret = xd3_decode_secondary_sections (stream))) { return ret; } if (stream->flags & XD3_SKIP_EMIT) { return xd3_decode_finish_window (stream); } /* OPT: A possible optimization is to avoid allocating memory in * decode_setup_buffers and to avoid a large memcpy when the window * consists of a single VCD_SOURCE copy instruction. */ if ((ret = xd3_decode_setup_buffers (stream))) { return ret; } return 0; } static int xd3_decode_emit (xd3_stream *stream) { int ret; /* Produce output: originally structured to allow reentrant code * that fills as much of the output buffer as possible, but VCDIFF * semantics allows to copy from anywhere from the target window, so * instead allocate a sufficiently sized buffer after the target * window length is decoded. * * This code still needs to be reentrant to allow XD3_GETSRCBLK to * return control. This is handled by setting the * stream->dec_currentN instruction types to XD3_NOOP after they * have been processed. */ XD3_ASSERT (! (stream->flags & XD3_SKIP_EMIT)); XD3_ASSERT (stream->dec_tgtlen <= stream->space_out); while (stream->inst_sect.buf != stream->inst_sect.buf_max || stream->dec_current1.type != XD3_NOOP || stream->dec_current2.type != XD3_NOOP) { /* Decode next instruction pair. */ if ((stream->dec_current1.type == XD3_NOOP) && (stream->dec_current2.type == XD3_NOOP) && (ret = xd3_decode_instruction (stream))) { return ret; } /* Output dec_current1 */ while ((stream->dec_current1.type != XD3_NOOP)) { if ((ret = xd3_decode_output_halfinst (stream, & stream->dec_current1))) { return ret; } } /* Output dec_current2 */ while (stream->dec_current2.type != XD3_NOOP) { if ((ret = xd3_decode_output_halfinst (stream, & stream->dec_current2))) { return ret; } } } if (stream->avail_out != stream->dec_tgtlen) { IF_DEBUG2 (DP(RINT "AVAIL_OUT(%d) != DEC_TGTLEN(%d)\n", stream->avail_out, stream->dec_tgtlen)); stream->msg = "wrong window length"; return XD3_INVALID_INPUT; } if (stream->data_sect.buf != stream->data_sect.buf_max) { stream->msg = "extra data section"; return XD3_INVALID_INPUT; } if (stream->addr_sect.buf != stream->addr_sect.buf_max) { stream->msg = "extra address section"; return XD3_INVALID_INPUT; } /* OPT: Should cksum computation be combined with the above loop? */ if ((stream->dec_win_ind & VCD_ADLER32) != 0 && (stream->flags & XD3_ADLER32_NOVER) == 0) { uint32_t a32 = adler32 (1L, stream->next_out, stream->avail_out); if (a32 != stream->dec_adler32) { stream->msg = "target window checksum mismatch"; return XD3_INVALID_INPUT; } } /* Finished with a window. */ return xd3_decode_finish_window (stream); } int xd3_decode_input (xd3_stream *stream) { int ret; if (stream->enc_state != 0) { stream->msg = "encoder/decoder transition"; return XD3_INVALID_INPUT; } #define BYTE_CASE(expr,x,nstate) \ do { \ if ( (expr) && \ ((ret = xd3_decode_byte (stream, & (x))) != 0) ) { return ret; } \ stream->dec_state = (nstate); \ } while (0) #define OFFSET_CASE(expr,x,nstate) \ do { \ if ( (expr) && \ ((ret = xd3_decode_offset (stream, & (x))) != 0) ) { return ret; } \ stream->dec_state = (nstate); \ } while (0) #define SIZE_CASE(expr,x,nstate) \ do { \ if ( (expr) && \ ((ret = xd3_decode_size (stream, & (x))) != 0) ) { return ret; } \ stream->dec_state = (nstate); \ } while (0) switch (stream->dec_state) { case DEC_VCHEAD: { if ((ret = xd3_decode_bytes (stream, stream->dec_magic, & stream->dec_magicbytes, 4))) { return ret; } if (stream->dec_magic[0] != VCDIFF_MAGIC1 || stream->dec_magic[1] != VCDIFF_MAGIC2 || stream->dec_magic[2] != VCDIFF_MAGIC3) { stream->msg = "not a VCDIFF input"; return XD3_INVALID_INPUT; } if (stream->dec_magic[3] != 0) { stream->msg = "VCDIFF input version > 0 is not supported"; return XD3_INVALID_INPUT; } stream->dec_state = DEC_HDRIND; } case DEC_HDRIND: { if ((ret = xd3_decode_byte (stream, & stream->dec_hdr_ind))) { return ret; } if ((stream->dec_hdr_ind & VCD_INVHDR) != 0) { stream->msg = "unrecognized header indicator bits set"; return XD3_INVALID_INPUT; } stream->dec_state = DEC_SECONDID; } case DEC_SECONDID: /* Secondary compressor ID: only if VCD_SECONDARY is set */ if ((stream->dec_hdr_ind & VCD_SECONDARY) != 0) { BYTE_CASE (1, stream->dec_secondid, DEC_TABLEN); switch (stream->dec_secondid) { case VCD_FGK_ID: FGK_CASE (stream); case VCD_DJW_ID: DJW_CASE (stream); default: stream->msg = "unknown secondary compressor ID"; return XD3_INVALID_INPUT; } } case DEC_TABLEN: /* Length of code table data: only if VCD_CODETABLE is set */ SIZE_CASE ((stream->dec_hdr_ind & VCD_CODETABLE) != 0, stream->dec_codetblsz, DEC_NEAR); /* The codetblsz counts the two NEAR/SAME bytes */ if ((stream->dec_hdr_ind & VCD_CODETABLE) != 0) { if (stream->dec_codetblsz <= 2) { stream->msg = "invalid code table size"; return ENOMEM; } stream->dec_codetblsz -= 2; } case DEC_NEAR: /* Near modes: only if VCD_CODETABLE is set */ BYTE_CASE((stream->dec_hdr_ind & VCD_CODETABLE) != 0, stream->acache.s_near, DEC_SAME); case DEC_SAME: /* Same modes: only if VCD_CODETABLE is set */ BYTE_CASE((stream->dec_hdr_ind & VCD_CODETABLE) != 0, stream->acache.s_same, DEC_TABDAT); case DEC_TABDAT: /* Compressed code table data */ if ((stream->dec_hdr_ind & VCD_CODETABLE) != 0) { /* Get the code table data. */ if ((stream->dec_codetbl == NULL) && (stream->dec_codetbl = (uint8_t*) xd3_alloc (stream, stream->dec_codetblsz, 1)) == NULL) { return ENOMEM; } if ((ret = xd3_decode_bytes (stream, stream->dec_codetbl, & stream->dec_codetblbytes, stream->dec_codetblsz))) { return ret; } if ((ret = xd3_apply_table_encoding (stream, stream->dec_codetbl, stream->dec_codetblbytes))) { return ret; } } else { /* Use the default table. */ stream->acache.s_near = __rfc3284_code_table_desc.near_modes; stream->acache.s_same = __rfc3284_code_table_desc.same_modes; stream->code_table = xd3_rfc3284_code_table (); } if ((ret = xd3_alloc_cache (stream))) { return ret; } stream->dec_state = DEC_APPLEN; case DEC_APPLEN: /* Length of application data */ SIZE_CASE((stream->dec_hdr_ind & VCD_APPHEADER) != 0, stream->dec_appheadsz, DEC_APPDAT); case DEC_APPDAT: /* Application data */ if (stream->dec_hdr_ind & VCD_APPHEADER) { /* Note: we add an additional byte for padding, to allow 0-termination. */ if ((stream->dec_appheader == NULL) && (stream->dec_appheader = (uint8_t*) xd3_alloc (stream, stream->dec_appheadsz+1, 1)) == NULL) { return ENOMEM; } stream->dec_appheader[stream->dec_appheadsz] = 0; if ((ret = xd3_decode_bytes (stream, stream->dec_appheader, & stream->dec_appheadbytes, stream->dec_appheadsz))) { return ret; } } /* xoff_t -> usize_t is safe because this is the first block. */ stream->dec_hdrsize = (usize_t) stream->total_in; stream->dec_state = DEC_WININD; case DEC_WININD: { /* Start of a window: the window indicator */ if ((ret = xd3_decode_byte (stream, & stream->dec_win_ind))) { return ret; } stream->current_window = stream->dec_window_count; if (XOFF_T_OVERFLOW (stream->dec_winstart, stream->dec_tgtlen)) { stream->msg = "decoder file offset overflow"; return XD3_INVALID_INPUT; } stream->dec_winstart += stream->dec_tgtlen; if ((stream->dec_win_ind & VCD_INVWIN) != 0) { stream->msg = "unrecognized window indicator bits set"; return XD3_INVALID_INPUT; } if ((ret = xd3_decode_init_window (stream))) { return ret; } stream->dec_state = DEC_CPYLEN; IF_DEBUG2 (DP(RINT "--------- TARGET WINDOW %"Q"u -----------\n", stream->current_window)); } case DEC_CPYLEN: /* Copy window length: only if VCD_SOURCE or VCD_TARGET is set */ SIZE_CASE(SRCORTGT (stream->dec_win_ind), stream->dec_cpylen, DEC_CPYOFF); /* Set the initial, logical decoder position (HERE address) in * dec_position. This is set to just after the source/copy * window, as we are just about to output the first byte of * target window. */ stream->dec_position = stream->dec_cpylen; case DEC_CPYOFF: /* Copy window offset: only if VCD_SOURCE or VCD_TARGET is set */ OFFSET_CASE(SRCORTGT (stream->dec_win_ind), stream->dec_cpyoff, DEC_ENCLEN); /* Copy offset and copy length may not overflow. */ if (XOFF_T_OVERFLOW (stream->dec_cpyoff, stream->dec_cpylen)) { stream->msg = "decoder copy window overflows a file offset"; return XD3_INVALID_INPUT; } /* Check copy window bounds: VCD_TARGET window may not exceed current position. */ if ((stream->dec_win_ind & VCD_TARGET) && (stream->dec_cpyoff + (xoff_t) stream->dec_cpylen > stream->dec_winstart)) { stream->msg = "VCD_TARGET window out of bounds"; return XD3_INVALID_INPUT; } case DEC_ENCLEN: /* Length of the delta encoding */ SIZE_CASE(1, stream->dec_enclen, DEC_TGTLEN); case DEC_TGTLEN: /* Length of target window */ SIZE_CASE(1, stream->dec_tgtlen, DEC_DELIND); /* Set the maximum decoder position, beyond which we should not * decode any data. This is the maximum value for dec_position. * This may not exceed the size of a usize_t. */ if (USIZE_T_OVERFLOW (stream->dec_cpylen, stream->dec_tgtlen)) { stream->msg = "decoder target window overflows a usize_t"; return XD3_INVALID_INPUT; } /* Check for malicious files. */ if (stream->dec_tgtlen > XD3_HARDMAXWINSIZE) { stream->msg = "hard window size exceeded"; return XD3_INVALID_INPUT; } stream->dec_maxpos = stream->dec_cpylen + stream->dec_tgtlen; case DEC_DELIND: /* Delta indicator */ BYTE_CASE(1, stream->dec_del_ind, DEC_DATALEN); if ((stream->dec_del_ind & VCD_INVDEL) != 0) { stream->msg = "unrecognized delta indicator bits set"; return XD3_INVALID_INPUT; } /* Delta indicator is only used with secondary compression. */ if ((stream->dec_del_ind != 0) && (stream->sec_type == NULL)) { stream->msg = "invalid delta indicator bits set"; return XD3_INVALID_INPUT; } /* Section lengths */ case DEC_DATALEN: SIZE_CASE(1, stream->data_sect.size, DEC_INSTLEN); case DEC_INSTLEN: SIZE_CASE(1, stream->inst_sect.size, DEC_ADDRLEN); case DEC_ADDRLEN: SIZE_CASE(1, stream->addr_sect.size, DEC_CKSUM); case DEC_CKSUM: /* Window checksum. */ if ((stream->dec_win_ind & VCD_ADLER32) != 0) { int i; if ((ret = xd3_decode_bytes (stream, stream->dec_cksum, & stream->dec_cksumbytes, 4))) { return ret; } for (i = 0; i < 4; i += 1) { stream->dec_adler32 = (stream->dec_adler32 << 8) | stream->dec_cksum[i]; } } stream->dec_state = DEC_DATA; /* Check dec_enclen for redundency, otherwise it is not really used. */ { usize_t enclen_check = (1 + (xd3_sizeof_size (stream->dec_tgtlen) + xd3_sizeof_size (stream->data_sect.size) + xd3_sizeof_size (stream->inst_sect.size) + xd3_sizeof_size (stream->addr_sect.size)) + stream->data_sect.size + stream->inst_sect.size + stream->addr_sect.size + ((stream->dec_win_ind & VCD_ADLER32) ? 4 : 0)); if (stream->dec_enclen != enclen_check) { stream->msg = "incorrect encoding length (redundent)"; return XD3_INVALID_INPUT; } } /* Returning here gives the application a chance to inspect the * header, skip the window, etc. */ if (stream->current_window == 0) { return XD3_GOTHEADER; } else { return XD3_WINSTART; } case DEC_DATA: case DEC_INST: case DEC_ADDR: /* Next read the three sections. */ if ((ret = xd3_decode_sections (stream))) { return ret; } case DEC_EMIT: /* To speed VCD_SOURCE block-address calculations, the source * cpyoff_blocks and cpyoff_blkoff are pre-computed. */ if (stream->dec_win_ind & VCD_SOURCE) { xd3_source *src = stream->src; if (src == NULL) { stream->msg = "source input required"; return XD3_INVALID_INPUT; } xd3_blksize_div(stream->dec_cpyoff, src, &src->cpyoff_blocks, &src->cpyoff_blkoff); IF_DEBUG1(DP(RINT "decode cpyoff %"Q"u " "cpyblkno %"Q"u " "cpyblkoff %u " "blksize %u\n", stream->dec_cpyoff, src->cpyoff_blocks, src->cpyoff_blkoff, src->blksize)); } /* xd3_decode_emit returns XD3_OUTPUT on every success. */ if ((ret = xd3_decode_emit (stream)) == XD3_OUTPUT) { stream->total_out += (xoff_t) stream->avail_out; } return ret; case DEC_FINISH: { if (stream->dec_win_ind & VCD_TARGET) { if (stream->dec_lastwin == NULL) { stream->dec_lastwin = stream->next_out; stream->dec_lastspace = stream->space_out; } else { xd3_swap_uint8p (& stream->dec_lastwin, & stream->next_out); xd3_swap_usize_t (& stream->dec_lastspace, & stream->space_out); } } stream->dec_lastlen = stream->dec_tgtlen; stream->dec_laststart = stream->dec_winstart; stream->dec_window_count += 1; /* Note: the updates to dec_winstart & current_window are * deferred until after the next DEC_WININD byte is read. */ stream->dec_state = DEC_WININD; return XD3_WINFINISH; } default: stream->msg = "invalid state"; return XD3_INVALID_INPUT; } } #endif // _XDELTA3_DECODE_H_