Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added CAB decompression parameter MSCABD_PARAM_SALVAGE #18

Merged
merged 1 commit into from
Oct 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions libmspack/mspack/cab.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,16 @@
* more than 6144 bytes. Quantum has no documentation, but the largest
* block seen in the wild is 337 bytes above uncompressed size.
*/
#define CAB_BLOCKMAX (32768)
#define CAB_BLOCKMAX (65535)
#define CAB_BLOCKSTD (32768)
#define CAB_INPUTMAX (CAB_BLOCKMAX+6144)

/* There are no more than 65535 data blocks per folder, so a folder cannot
* be more than 32768*65535 bytes in length. As files cannot span more than
* one folder, this is also their max offset, length and offset+length limit.
*/
#define CAB_FOLDERMAX (65535)
#define CAB_LENGTHMAX (CAB_BLOCKMAX * CAB_FOLDERMAX)
#define CAB_LENGTHMAX UINT_MAX

/* CAB compression definitions */

Expand Down Expand Up @@ -107,7 +108,7 @@ struct mscab_decompressor_p {
struct mscab_decompressor base;
struct mscabd_decompress_state *d;
struct mspack_system *system;
int param[3]; /* !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!! */
int param[4]; /* !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!! */
int error, read_error;
};

Expand Down
154 changes: 104 additions & 50 deletions libmspack/mspack/cabd.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ static void cabd_close(
struct mscab_decompressor *base, struct mscabd_cabinet *origcab);
static int cabd_read_headers(
struct mspack_system *sys, struct mspack_file *fh,
struct mscabd_cabinet_p *cab, off_t offset, int quiet);
struct mscabd_cabinet_p *cab, off_t offset, int salvage, int quiet);
static char *cabd_read_string(
struct mspack_system *sys, struct mspack_file *fh, int *error);

Expand Down Expand Up @@ -157,6 +157,7 @@ struct mscab_decompressor *
self->param[MSCABD_PARAM_SEARCHBUF] = 32768;
self->param[MSCABD_PARAM_FIXMSZIP] = 0;
self->param[MSCABD_PARAM_DECOMPBUF] = 4096;
self->param[MSCABD_PARAM_SALVAGE] = 0;
}
return (struct mscab_decompressor *) self;
}
Expand Down Expand Up @@ -200,7 +201,7 @@ static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base,
if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
cab->base.filename = filename;
error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0);
error = cabd_read_headers(sys, fh, cab, (off_t) 0, self->param[MSCABD_PARAM_SALVAGE], 0);
if (error) {
cabd_close(base, (struct mscabd_cabinet *) cab);
cab = NULL;
Expand Down Expand Up @@ -305,9 +306,9 @@ static void cabd_close(struct mscab_decompressor *base,
static int cabd_read_headers(struct mspack_system *sys,
struct mspack_file *fh,
struct mscabd_cabinet_p *cab,
off_t offset, int quiet)
off_t offset, int quiet, int salvage)
{
int num_folders, num_files, folder_resv, i, x;
int num_folders, num_files, folder_resv, i, x, fidx, fidx_ok, read_string_errno = 0;
struct mscabd_folder_p *fol, *linkfol = NULL;
struct mscabd_file *file, *linkfile = NULL;
unsigned char buf[64];
Expand Down Expand Up @@ -363,6 +364,7 @@ static int cabd_read_headers(struct mspack_system *sys,

/* read the reserved-sizes part of header, if present */
cab->base.flags = EndGetI16(&buf[cfhead_Flags]);

if (cab->base.flags & cfheadRESERVE_PRESENT) {
if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) {
return MSPACK_ERR_READ;
Expand Down Expand Up @@ -390,14 +392,18 @@ static int cabd_read_headers(struct mspack_system *sys,

/* read name and info of preceeding cabinet in set, if present */
if (cab->base.flags & cfheadPREV_CABINET) {
cab->base.prevname = cabd_read_string(sys, fh, &x); if (x) return x;
cab->base.previnfo = cabd_read_string(sys, fh, &x); if (x) return x;
cab->base.prevname = cabd_read_string(sys, fh, &read_string_errno);
if (read_string_errno) return read_string_errno;
cab->base.previnfo = cabd_read_string(sys, fh, &read_string_errno);
if (read_string_errno) return read_string_errno;
}

/* read name and info of next cabinet in set, if present */
if (cab->base.flags & cfheadNEXT_CABINET) {
cab->base.nextname = cabd_read_string(sys, fh, &x); if (x) return x;
cab->base.nextinfo = cabd_read_string(sys, fh, &x); if (x) return x;
cab->base.nextname = cabd_read_string(sys, fh, &read_string_errno);
if (read_string_errno) return read_string_errno;
cab->base.nextinfo = cabd_read_string(sys, fh, &read_string_errno);
if (read_string_errno) return read_string_errno;
}

/* read folders */
Expand Down Expand Up @@ -446,25 +452,29 @@ static int cabd_read_headers(struct mspack_system *sys,
file->offset = EndGetI32(&buf[cffile_FolderOffset]);

/* set folder pointer */
x = EndGetI16(&buf[cffile_FolderIndex]);
if (x < cffileCONTINUED_FROM_PREV) {
/* normal folder index; count up to the correct folder. the folder
* pointer will be NULL if folder index is invalid */
struct mscabd_folder *ifol = cab->base.folders;
while (x--) if (ifol) ifol = ifol->next;
file->folder = ifol;

if (!ifol) {
sys->free(file);
D(("invalid folder index"))
return MSPACK_ERR_DATAFORMAT;
fidx_ok = 1;
fidx = EndGetI16(&buf[cffile_FolderIndex]);
if (fidx < cffileCONTINUED_FROM_PREV) {
/* normal folder index; count up to the correct folder */
if (fidx < num_folders) {
struct mscabd_folder *ifol = cab->base.folders;
while (fidx--) if (ifol) ifol = ifol->next;
file->folder = ifol;
}
else {
file->folder = NULL;
}

if (!file->folder) {
D(("invalid folder index"))
fidx_ok = 0;
}
}
else {
/* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or
* CONTINUED_PREV_AND_NEXT */
if ((x == cffileCONTINUED_TO_NEXT) ||
(x == cffileCONTINUED_PREV_AND_NEXT))
if ((fidx == cffileCONTINUED_TO_NEXT) ||
(fidx == cffileCONTINUED_PREV_AND_NEXT))
{
/* get last folder */
struct mscabd_folder *ifol = cab->base.folders;
Expand All @@ -476,8 +486,8 @@ static int cabd_read_headers(struct mspack_system *sys,
if (!fol->merge_next) fol->merge_next = file;
}

if ((x == cffileCONTINUED_FROM_PREV) ||
(x == cffileCONTINUED_PREV_AND_NEXT))
if ((fidx == cffileCONTINUED_FROM_PREV) ||
(fidx == cffileCONTINUED_PREV_AND_NEXT))
{
/* get first folder */
file->folder = cab->base.folders;
Expand All @@ -501,10 +511,11 @@ static int cabd_read_headers(struct mspack_system *sys,
file->date_y = (x >> 9) + 1980;

/* get filename */
file->filename = cabd_read_string(sys, fh, &x);
if (x) {
file->filename = cabd_read_string(sys, fh, &read_string_errno);
if (read_string_errno || !fidx_ok) {
sys->free(file->filename);
sys->free(file);
return x;
if (salvage) continue; else return read_string_errno;
}

/* link file entry into file list */
Expand All @@ -513,6 +524,13 @@ static int cabd_read_headers(struct mspack_system *sys,
linkfile = file;
}

if (cab->base.files == NULL) {
/* We never actually added any files to the file list. Something went wrong.
* The file header may have been invalid */
D(("No files found, even though header claimed to have %d files", num_files))
return MSPACK_ERR_DATAFORMAT;
}

return MSPACK_ERR_OK;
}

Expand Down Expand Up @@ -631,7 +649,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
struct mspack_system *sys = self->system;
unsigned char *p, *pend, state = 0;
unsigned int cablen_u32 = 0, foffset_u32 = 0;
int false_cabs = 0;
int false_cabs = 0, salvage = self->param[MSCABD_PARAM_SALVAGE];

#if !LARGEFILE_SUPPORT
/* detect 32-bit off_t overflow */
Expand Down Expand Up @@ -718,7 +736,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
return MSPACK_ERR_NOMEMORY;
}
cab->base.filename = filename;
if (cabd_read_headers(sys, fh, cab, caboff, 1)) {
if (cabd_read_headers(sys, fh, cab, caboff, salvage, 1)) {
/* destroy the failed cabinet */
cabd_close((struct mscab_decompressor *) self,
(struct mscabd_cabinet *) cab);
Expand Down Expand Up @@ -994,27 +1012,54 @@ static int cabd_extract(struct mscab_decompressor *base,
struct mscabd_folder_p *fol;
struct mspack_system *sys;
struct mspack_file *fh;
off_t filelen, maxlen;

if (!self) return MSPACK_ERR_ARGS;
if (!file) return self->error = MSPACK_ERR_ARGS;

sys = self->system;
fol = (struct mscabd_folder_p *) file->folder;

/* validate the file's offset and length */
if ( (file->offset > CAB_LENGTHMAX) || (file->length > CAB_LENGTHMAX) ||
((file->offset + file->length) > CAB_LENGTHMAX))
{
/* if offset is beyond 2GB, nothing can be extracted */
if (file->offset > CAB_LENGTHMAX) {
return self->error = MSPACK_ERR_DATAFORMAT;
}

/* check if file can be extracted */
if ((!fol) || (fol->merge_prev) ||
(((file->offset + file->length) / CAB_BLOCKMAX) > fol->base.num_blocks))
{
/* if file claims to go beyond 2GB either error out,
* or in salvage mode reduce file length so it fits 2GB limit
*/
filelen = file->length;
if (filelen > CAB_LENGTHMAX || (file->offset + filelen) > CAB_LENGTHMAX) {
if (self->param[MSCABD_PARAM_SALVAGE]) {
filelen = CAB_LENGTHMAX - file->offset;
}
else {
return self->error = MSPACK_ERR_DATAFORMAT;
}
}

/* extraction impossible if no folder, or folder needs predecessor */
if (!fol || fol->merge_prev) {
sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, "
"cabinet set is incomplete.", file->filename);
return self->error = MSPACK_ERR_DATAFORMAT;
"cabinet set is incomplete", file->filename);
return self->error = MSPACK_ERR_DECRUNCH;
}

/* if file goes beyond what can be decoded, either error,
* or in salvage mode reduce file length to what can be decoded
*/
maxlen = fol->base.num_blocks * CAB_BLOCKMAX;
if ((file->offset + filelen) > maxlen) {
if (self->param[MSCABD_PARAM_SALVAGE]) {
sys->message(NULL, "WARNING: can only extract first %"LD" bytes "
" of file \"%s\"", maxlen, file->filename);
filelen = maxlen;
}
else {
sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, "
"cabinet set is incomplete", file->filename);
return self->error = MSPACK_ERR_DECRUNCH;
}
}

/* allocate generic decompression state */
Expand Down Expand Up @@ -1076,7 +1121,7 @@ static int cabd_extract(struct mscab_decompressor *base,
self->error = MSPACK_ERR_OK;

/* if file has more than 0 bytes */
if (file->length) {
if (filelen) {
off_t bytes;
int error;
/* get to correct offset.
Expand All @@ -1085,15 +1130,17 @@ static int cabd_extract(struct mscab_decompressor *base,
* and pass back MSPACK_ERR_READ
*/
self->d->outfh = NULL;
if ((bytes = file->offset - self->d->offset)) {
error = self->d->decompress(self->d->state, bytes);
self->error = (error == MSPACK_ERR_READ) ? self->read_error : error;
if ((self->d->comp_type & cffoldCOMPTYPE_MASK) != cffoldCOMPTYPE_LZX) {
if ((bytes = file->offset - self->d->offset)) {
error = self->d->decompress(self->d->state, bytes);
self->error = (error == MSPACK_ERR_READ) ? self->read_error : error;
}
}

/* if getting to the correct offset was error free, unpack file */
if (!self->error) {
self->d->outfh = fh;
error = self->d->decompress(self->d->state, (off_t) file->length);
error = self->d->decompress(self->d->state, filelen);
self->error = (error == MSPACK_ERR_READ) ? self->read_error : error;
}
}
Expand Down Expand Up @@ -1182,8 +1229,9 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) {
struct mspack_system *sys = self->system;
int avail, todo, outlen, ignore_cksum;

ignore_cksum = self->param[MSCABD_PARAM_FIXMSZIP] &&
((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP);
ignore_cksum = self->param[MSCABD_PARAM_SALVAGE] ||
(self->param[MSCABD_PARAM_FIXMSZIP] &&
((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP));

todo = bytes;
while (todo > 0) {
Expand All @@ -1203,8 +1251,11 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) {

/* check if we're out of input blocks, advance block counter */
if (self->d->block++ >= self->d->folder->base.num_blocks) {
self->read_error = MSPACK_ERR_DATAFORMAT;
break;
if (!self->param[MSCABD_PARAM_SALVAGE])
self->read_error = MSPACK_ERR_DATAFORMAT;
else
D(("Ran out of CAB input blocks prematurely"))
break;
}

/* read a block */
Expand All @@ -1225,7 +1276,7 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) {
/* special LZX hack -- on the last block, inform LZX of the
* size of the output data stream. */
lzxd_set_output_length((struct lzxd_stream *) self->d->state, (off_t)
((self->d->block-1) * CAB_BLOCKMAX + outlen));
((self->d->block-1) * CAB_BLOCKSTD + outlen));
}
}
else {
Expand Down Expand Up @@ -1329,7 +1380,7 @@ static int cabd_sys_read_block(struct mspack_system *sys,

/* advance to next member in the cabinet set */
if (!(d->data = d->data->next)) {
D(("ran out of splits in cabinet set"))
sys->message(d->infh, "WARNING; ran out of cabinets in set. Are any missing?");
return MSPACK_ERR_DATAFORMAT;
}

Expand Down Expand Up @@ -1447,6 +1498,9 @@ static int cabd_param(struct mscab_decompressor *base, int param, int value) {
if (value < 4) return MSPACK_ERR_ARGS;
self->param[MSCABD_PARAM_DECOMPBUF] = value;
break;
case MSCABD_PARAM_SALVAGE:
self->param[MSCABD_PARAM_SALVAGE] = value;
break;
default:
return MSPACK_ERR_ARGS;
}
Expand Down
10 changes: 7 additions & 3 deletions libmspack/mspack/lzxd.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) {
register unsigned short sym;

int match_length, length_footer, extra, verbatim_bits, bytes_todo;
int this_run, main_element, aligned_bits, j;
int this_run, main_element, aligned_bits, j, warned = 0;
unsigned char *window, *runsrc, *rundest, buf[12];
unsigned int frame_size=0, end_frame, match_offset, window_posn;
unsigned int R0, R1, R2;
Expand Down Expand Up @@ -435,8 +435,12 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) {
/* have we reached the reset interval? (if there is one?) */
if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) {
if (lzx->block_remaining) {
D(("%d bytes remaining at reset interval", lzx->block_remaining))
return lzx->error = MSPACK_ERR_DECRUNCH;
/* this is a file format error, we can make a best effort to extract what we can */
D(("%d bytes remaining at reset interval", lzx->block_remaining))
if (!warned) {
lzx->sys->message(NULL, "WARNING; invalid reset interval detected during LZX decompression");
warned++;
}
}

/* re-read the intel header and reset the huffman lengths */
Expand Down
7 changes: 7 additions & 0 deletions libmspack/mspack/mspack.h
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,13 @@ struct mscabd_file {
#define MSCABD_PARAM_FIXMSZIP (1)
/** mscab_decompressor::set_param() parameter: size of decompression buffer */
#define MSCABD_PARAM_DECOMPBUF (2)
/** mscab_decompressor::set_param() parameter: salvage data from bad cabinets?
* If enabled, open() will skip file with bad folder indices or filenames
* rather than reject the whole cabinet, and extract() will limit rather than
* reject files with invalid offsets and lengths, and bad data block checksums
* will be ignored. Available only in CAB decoder version 2 and above.
*/
#define MSCABD_PARAM_SALVAGE (3)

/** TODO */
struct mscab_compressor {
Expand Down