summaryrefslogtreecommitdiffstats
path: root/rbutil/rbutilqt/mspack/cabd.c
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/rbutilqt/mspack/cabd.c')
-rw-r--r--rbutil/rbutilqt/mspack/cabd.c541
1 files changed, 302 insertions, 239 deletions
diff --git a/rbutil/rbutilqt/mspack/cabd.c b/rbutil/rbutilqt/mspack/cabd.c
index 6549d7b8cf..ae66769b24 100644
--- a/rbutil/rbutilqt/mspack/cabd.c
+++ b/rbutil/rbutilqt/mspack/cabd.c
@@ -1,5 +1,5 @@
/* This file is part of libmspack.
- * (C) 2003-2011 Stuart Caie.
+ * (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
@@ -23,7 +23,9 @@
#include "system-mspack.h"
#include "cab.h"
-#include <assert.h>
+#include "mszip.h"
+#include "lzx.h"
+#include "qtm.h"
/* Notes on compliance with cabinet specification:
*
@@ -72,10 +74,9 @@ 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,
- struct mscabd_cabinet_p *cab, int *error);
+ struct mspack_system *sys, struct mspack_file *fh, int *error);
static struct mscabd_cabinet *cabd_search(
struct mscab_decompressor *base, const char *filename);
@@ -110,7 +111,7 @@ static int cabd_sys_write(
struct mspack_file *file, void *buffer, int bytes);
static int cabd_sys_read_block(
struct mspack_system *sys, struct mscabd_decompress_state *d, int *out,
- int ignore_cksum);
+ int ignore_cksum, int ignore_blocksize);
static unsigned int cabd_checksum(
unsigned char *data, unsigned int bytes, unsigned int cksum);
static struct noned_state *noned_init(
@@ -155,9 +156,10 @@ struct mscab_decompressor *
self->d = NULL;
self->error = MSPACK_ERR_OK;
- self->param[MSCABD_PARAM_SEARCHBUF] = 32768;
- self->param[MSCABD_PARAM_FIXMSZIP] = 0;
- self->param[MSCABD_PARAM_DECOMPBUF] = 4096;
+ self->searchbuf_size = 32768;
+ self->fix_mszip = 0;
+ self->buf_size = 4096;
+ self->salvage = 0;
}
return (struct mscab_decompressor *) self;
}
@@ -171,9 +173,9 @@ void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) {
struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
if (self) {
struct mspack_system *sys = self->system;
- cabd_free_decomp(self);
if (self->d) {
if (self->d->infh) sys->close(self->d->infh);
+ cabd_free_decomp(self);
sys->free(self->d);
}
sys->free(self);
@@ -187,7 +189,7 @@ void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) {
* opens a file and tries to read it as a cabinet file
*/
static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base,
- const char *filename)
+ const char *filename)
{
struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
struct mscabd_cabinet_p *cab = NULL;
@@ -201,10 +203,10 @@ 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->salvage, 0);
if (error) {
- cabd_close(base, (struct mscabd_cabinet *) cab);
- cab = NULL;
+ cabd_close(base, (struct mscabd_cabinet *) cab);
+ cab = NULL;
}
self->error = error;
}
@@ -225,7 +227,7 @@ static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base,
* frees all memory associated with a given mscabd_cabinet.
*/
static void cabd_close(struct mscab_decompressor *base,
- struct mscabd_cabinet *origcab)
+ struct mscabd_cabinet *origcab)
{
struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
struct mscabd_folder_data *dat, *ndat;
@@ -253,16 +255,16 @@ static void cabd_close(struct mscab_decompressor *base,
/* free folder decompression state if it has been decompressed */
if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) {
- if (self->d->infh) sys->close(self->d->infh);
- cabd_free_decomp(self);
- sys->free(self->d);
- self->d = NULL;
+ if (self->d->infh) sys->close(self->d->infh);
+ cabd_free_decomp(self);
+ sys->free(self->d);
+ self->d = NULL;
}
/* free folder data segments */
for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) {
- ndat = dat->next;
- sys->free(dat);
+ ndat = dat->next;
+ sys->free(dat);
}
sys->free(fol);
}
@@ -304,11 +306,11 @@ static void cabd_close(struct mscab_decompressor *base,
* for folders and files as necessary
*/
static int cabd_read_headers(struct mspack_system *sys,
- struct mspack_file *fh,
- struct mscabd_cabinet_p *cab,
- off_t offset, int quiet)
+ struct mspack_file *fh,
+ struct mscabd_cabinet_p *cab,
+ off_t offset, int salvage, int quiet)
{
- int num_folders, num_files, folder_resv, i, x;
+ int num_folders, num_files, folder_resv, i, x, err, fidx;
struct mscabd_folder_p *fol, *linkfol = NULL;
struct mscabd_file *file, *linkfile = NULL;
unsigned char buf[64];
@@ -364,6 +366,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;
@@ -379,7 +382,7 @@ static int cabd_read_headers(struct mspack_system *sys,
/* skip the reserved header */
if (cab->base.header_resv) {
if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) {
- return MSPACK_ERR_SEEK;
+ return MSPACK_ERR_SEEK;
}
}
}
@@ -391,14 +394,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, cab, &x); if (x) return x;
- cab->base.previnfo = cabd_read_string(sys, fh, cab, &x); if (x) return x;
+ cab->base.prevname = cabd_read_string(sys, fh, &err);
+ if (err) return err;
+ cab->base.previnfo = cabd_read_string(sys, fh, &err);
+ if (err) return err;
}
/* 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, cab, &x); if (x) return x;
- cab->base.nextinfo = cabd_read_string(sys, fh, cab, &x); if (x) return x;
+ cab->base.nextname = cabd_read_string(sys, fh, &err);
+ if (err) return err;
+ cab->base.nextinfo = cabd_read_string(sys, fh, &err);
+ if (err) return err;
}
/* read folders */
@@ -408,7 +415,7 @@ static int cabd_read_headers(struct mspack_system *sys,
}
if (folder_resv) {
if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) {
- return MSPACK_ERR_SEEK;
+ return MSPACK_ERR_SEEK;
}
}
@@ -447,45 +454,44 @@ 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 = 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 {
+ D(("invalid folder index"))
+ file->folder = NULL;
}
}
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;
- while (ifol->next) ifol = ifol->next;
- file->folder = ifol;
-
- /* set "merge next" pointer */
- fol = (struct mscabd_folder_p *) ifol;
- if (!fol->merge_next) fol->merge_next = file;
+ /* get last folder */
+ struct mscabd_folder *ifol = cab->base.folders;
+ while (ifol->next) ifol = ifol->next;
+ file->folder = ifol;
+
+ /* set "merge next" pointer */
+ fol = (struct mscabd_folder_p *) ifol;
+ 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;
+ /* get first folder */
+ file->folder = cab->base.folders;
- /* set "merge prev" pointer */
- fol = (struct mscabd_folder_p *) file->folder;
- if (!fol->merge_prev) fol->merge_prev = file;
+ /* set "merge prev" pointer */
+ fol = (struct mscabd_folder_p *) file->folder;
+ if (!fol->merge_prev) fol->merge_prev = file;
}
}
@@ -502,10 +508,14 @@ static int cabd_read_headers(struct mspack_system *sys,
file->date_y = (x >> 9) + 1980;
/* get filename */
- file->filename = cabd_read_string(sys, fh, cab, &x);
- if (x) {
+ file->filename = cabd_read_string(sys, fh, &err);
+
+ /* if folder index or filename are bad, either skip it or fail */
+ if (err || !file->folder) {
+ sys->free(file->filename);
sys->free(file);
- return x;
+ if (salvage) continue;
+ return err ? err : MSPACK_ERR_DATAFORMAT;
}
/* link file entry into file list */
@@ -514,23 +524,34 @@ 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;
}
static char *cabd_read_string(struct mspack_system *sys,
- struct mspack_file *fh,
- struct mscabd_cabinet_p *cab, int *error)
+ struct mspack_file *fh, int *error)
{
off_t base = sys->tell(fh);
char buf[256], *str;
- unsigned int len, i, ok;
- (void)cab;
+ int len, i, ok;
/* read up to 256 bytes */
- len = sys->read(fh, &buf[0], 256);
+ if ((len = sys->read(fh, &buf[0], 256)) <= 0) {
+ *error = MSPACK_ERR_READ;
+ return NULL;
+ }
/* search for a null terminator in the buffer */
for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; }
+ /* reject empty strings */
+ if (i == 0) ok = 0;
+
if (!ok) {
*error = MSPACK_ERR_DATAFORMAT;
return NULL;
@@ -566,7 +587,7 @@ static char *cabd_read_string(struct mspack_system *sys,
* break out of the loop and be sure that all resources are freed
*/
static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base,
- const char *filename)
+ const char *filename)
{
struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
struct mscabd_cabinet_p *cab = NULL;
@@ -579,7 +600,7 @@ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base,
sys = self->system;
/* allocate a search buffer */
- search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->param[MSCABD_PARAM_SEARCHBUF]);
+ search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->searchbuf_size);
if (!search_buf) {
self->error = MSPACK_ERR_NOMEMORY;
return NULL;
@@ -589,21 +610,21 @@ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base,
if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) {
self->error = cabd_find(self, search_buf, fh, filename,
- filelen, &firstlen, &cab);
+ filelen, &firstlen, &cab);
}
/* truncated / extraneous data warning: */
if (firstlen && (firstlen != filelen) &&
- (!cab || (cab->base.base_offset == 0)))
+ (!cab || (cab->base.base_offset == 0)))
{
if (firstlen < filelen) {
- sys->message(fh, "WARNING; possible %" LD
- " extra bytes at end of file.",
- filelen - firstlen);
+ sys->message(fh, "WARNING; possible %" LD
+ " extra bytes at end of file.",
+ filelen - firstlen);
}
else {
- sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.",
- firstlen - filelen);
+ sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.",
+ firstlen - filelen);
}
}
@@ -620,8 +641,8 @@ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base,
}
static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
- struct mspack_file *fh, const char *filename, off_t flen,
- off_t *firstlen, struct mscabd_cabinet_p **firstcab)
+ struct mspack_file *fh, const char *filename, off_t flen,
+ off_t *firstlen, struct mscabd_cabinet_p **firstcab)
{
struct mscabd_cabinet_p *cab, *link = NULL;
off_t caboff, offset, length;
@@ -630,7 +651,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
unsigned int cablen_u32 = 0, foffset_u32 = 0;
int false_cabs = 0;
-#ifndef LARGEFILE_SUPPORT
+#if !LARGEFILE_SUPPORT
/* detect 32-bit off_t overflow */
if (flen < 0) {
sys->message(fh, largefile_msg);
@@ -643,8 +664,8 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
/* search length is either the full length of the search buffer, or the
* amount of data remaining to the end of the file, whichever is less. */
length = flen - offset;
- if (length > self->param[MSCABD_PARAM_SEARCHBUF]) {
- length = self->param[MSCABD_PARAM_SEARCHBUF];
+ if (length > self->searchbuf_size) {
+ length = self->searchbuf_size;
}
/* fill the search buffer with data from disk */
@@ -654,22 +675,21 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
/* FAQ avoidance strategy */
if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) {
- sys->message(fh, "WARNING; found InstallShield header. "
- "This is probably an InstallShield file. "
- "Use UNSHIELD from www.synce.org to unpack it.");
+ sys->message(fh, "WARNING; found InstallShield header. Use unshield "
+ "(https://github.com/twogood/unshield) to unpack this file");
}
/* read through the entire buffer. */
for (p = &buf[0], pend = &buf[length]; p < pend; ) {
switch (state) {
- /* starting state */
+ /* starting state */
case 0:
- /* we spend most of our time in this while loop, looking for
- * a leading 'M' of the 'MSCF' signature */
- while (p < pend && *p != 0x4D) p++;
- /* if we found tht 'M', advance state */
- if (p++ < pend) state = 1;
- break;
+ /* we spend most of our time in this while loop, looking for
+ * a leading 'M' of the 'MSCF' signature */
+ while (p < pend && *p != 0x4D) p++;
+ /* if we found tht 'M', advance state */
+ if (p++ < pend) state = 1;
+ break;
/* verify that the next 3 bytes are 'S', 'C' and 'F' */
case 1: state = (*p++ == 0x53) ? 2 : 0; break;
@@ -691,70 +711,71 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
case 17: foffset_u32 |= *p++ << 8; state++; break;
case 18: foffset_u32 |= *p++ << 16; state++; break;
case 19: foffset_u32 |= *p++ << 24;
- /* now we have recieved 20 bytes of potential cab header. work out
- * the offset in the file of this potential cabinet */
- caboff = offset + (p - &buf[0]) - 20;
-
- /* should reading cabinet fail, restart search just after 'MSCF' */
- offset = caboff + 4;
-
- /* capture the "length of cabinet" field if there is a cabinet at
- * offset 0 in the file, regardless of whether the cabinet can be
- * read correctly or not */
- if (caboff == 0) *firstlen = (off_t) cablen_u32;
-
- /* check that the files offset is less than the alleged length of
- * the cabinet, and that the offset + the alleged length are
- * 'roughly' within the end of overall file length */
- if ((foffset_u32 < cablen_u32) &&
- ((caboff + (off_t) foffset_u32) < (flen + 32)) &&
- ((caboff + (off_t) cablen_u32) < (flen + 32)) )
- {
- /* likely cabinet found -- try reading it */
- if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
- return MSPACK_ERR_NOMEMORY;
- }
- cab->base.filename = filename;
- if (cabd_read_headers(sys, fh, cab, caboff, 1)) {
- /* destroy the failed cabinet */
- cabd_close((struct mscab_decompressor *) self,
- (struct mscabd_cabinet *) cab);
- false_cabs++;
- }
- else {
- /* cabinet read correctly! */
-
- /* link the cab into the list */
- if (!link) *firstcab = cab;
- else link->base.next = (struct mscabd_cabinet *) cab;
- link = cab;
-
- /* cause the search to restart after this cab's data. */
- offset = caboff + (off_t) cablen_u32;
-
-#ifndef LARGEFILE_SUPPORT
- /* detect 32-bit off_t overflow */
- if (offset < caboff) {
- sys->message(fh, largefile_msg);
- return MSPACK_ERR_OK;
- }
-#endif
- }
- }
-
- /* restart search */
- if (offset >= flen) return MSPACK_ERR_OK;
- if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) {
- return MSPACK_ERR_SEEK;
- }
- length = 0;
- p = pend;
- state = 0;
- break;
+ /* now we have recieved 20 bytes of potential cab header. work out
+ * the offset in the file of this potential cabinet */
+ caboff = offset + (p - &buf[0]) - 20;
+
+ /* should reading cabinet fail, restart search just after 'MSCF' */
+ offset = caboff + 4;
+
+ /* capture the "length of cabinet" field if there is a cabinet at
+ * offset 0 in the file, regardless of whether the cabinet can be
+ * read correctly or not */
+ if (caboff == 0) *firstlen = (off_t) cablen_u32;
+
+ /* check that the files offset is less than the alleged length of
+ * the cabinet, and that the offset + the alleged length are
+ * 'roughly' within the end of overall file length. In salvage
+ * mode, don't check the alleged length, allow it to be garbage */
+ if ((foffset_u32 < cablen_u32) &&
+ ((caboff + (off_t) foffset_u32) < (flen + 32)) &&
+ (((caboff + (off_t) cablen_u32) < (flen + 32)) || self->salvage))
+ {
+ /* likely cabinet found -- try reading it */
+ if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
+ return MSPACK_ERR_NOMEMORY;
+ }
+ cab->base.filename = filename;
+ if (cabd_read_headers(sys, fh, cab, caboff, self->salvage, 1)) {
+ /* destroy the failed cabinet */
+ cabd_close((struct mscab_decompressor *) self,
+ (struct mscabd_cabinet *) cab);
+ false_cabs++;
+ }
+ else {
+ /* cabinet read correctly! */
+
+ /* link the cab into the list */
+ if (!link) *firstcab = cab;
+ else link->base.next = (struct mscabd_cabinet *) cab;
+ link = cab;
+
+ /* cause the search to restart after this cab's data. */
+ offset = caboff + (off_t) cablen_u32;
+
+#if !LARGEFILE_SUPPORT
+ /* detect 32-bit off_t overflow */
+ if (offset < caboff) {
+ sys->message(fh, largefile_msg);
+ return MSPACK_ERR_OK;
+ }
+#endif
+ }
+ }
+
+ /* restart search */
+ if (offset >= flen) return MSPACK_ERR_OK;
+ if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) {
+ return MSPACK_ERR_SEEK;
+ }
+ length = 0;
+ p = pend;
+ state = 0;
+ break;
/* for bytes 4-7 and 12-15, just advance state/pointer */
default:
- p++, state++;
+ p++, state++;
} /* switch(state) */
} /* for (... p < pend ...) */
} /* for (... offset < length ...) */
@@ -765,7 +786,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
return MSPACK_ERR_OK;
}
-
+
/***************************************
* CABD_MERGE, CABD_PREPEND, CABD_APPEND
***************************************
@@ -775,22 +796,22 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
* merged folder's data parts list.
*/
static int cabd_prepend(struct mscab_decompressor *base,
- struct mscabd_cabinet *cab,
- struct mscabd_cabinet *prevcab)
+ struct mscabd_cabinet *cab,
+ struct mscabd_cabinet *prevcab)
{
return cabd_merge(base, prevcab, cab);
}
static int cabd_append(struct mscab_decompressor *base,
- struct mscabd_cabinet *cab,
- struct mscabd_cabinet *nextcab)
+ struct mscabd_cabinet *cab,
+ struct mscabd_cabinet *nextcab)
{
return cabd_merge(base, cab, nextcab);
}
static int cabd_merge(struct mscab_decompressor *base,
- struct mscabd_cabinet *lcab,
- struct mscabd_cabinet *rcab)
+ struct mscabd_cabinet *lcab,
+ struct mscabd_cabinet *rcab)
{
struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base;
struct mscabd_folder_data *data, *ndata;
@@ -880,7 +901,7 @@ static int cabd_merge(struct mscab_decompressor *base,
* instead */
lfol->base.num_blocks += rfol->base.num_blocks - 1;
if ((rfol->merge_next == NULL) ||
- (rfol->merge_next->folder != (struct mscabd_folder *) rfol))
+ (rfol->merge_next->folder != (struct mscabd_folder *) rfol))
{
lfol->merge_next = rfol->merge_next;
}
@@ -903,9 +924,9 @@ static int cabd_merge(struct mscab_decompressor *base,
rfi = fi->next;
/* if file's folder matches the merge folder, unlink and free it */
if (fi->folder == (struct mscabd_folder *) rfol) {
- if (lfi) lfi->next = rfi; else lcab->files = rfi;
- sys->free(fi->filename);
- sys->free(fi);
+ if (lfi) lfi->next = rfi; else lcab->files = rfi;
+ sys->free(fi->filename);
+ sys->free(fi);
}
else lfi = fi;
}
@@ -940,6 +961,12 @@ static int cabd_can_merge_folders(struct mspack_system *sys,
return 0;
}
+ /* check there are not too many data blocks after merging */
+ if ((lfol->base.num_blocks + rfol->base.num_blocks) > CAB_FOLDERMAX) {
+ D(("folder merge: too many data blocks in merged folders"))
+ return 0;
+ }
+
if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) {
D(("folder merge: one cabinet has no files to merge"))
return 0;
@@ -950,10 +977,10 @@ static int cabd_can_merge_folders(struct mspack_system *sys,
* should be identical in number and order. to verify this, check the
* offset and length of each file. */
for (l=lfi, r=rfi; l; l=l->next, r=r->next) {
- if (!r || (l->offset != r->offset) || (l->length != r->length)) {
- matching = 0;
- break;
- }
+ if (!r || (l->offset != r->offset) || (l->length != r->length)) {
+ matching = 0;
+ break;
+ }
}
if (matching) return 1;
@@ -963,9 +990,9 @@ static int cabd_can_merge_folders(struct mspack_system *sys,
* the merge with a warning about missing files. */
matching = 0;
for (l = lfi; l; l = l->next) {
- for (r = rfi; r; r = r->next) {
- if (l->offset == r->offset && l->length == r->length) break;
- }
+ for (r = rfi; r; r = r->next) {
+ if (l->offset == r->offset && l->length == r->length) break;
+ }
if (r) matching = 1; else sys->message(NULL,
"WARNING; merged file %s not listed in both cabinets", l->filename);
}
@@ -985,6 +1012,7 @@ static int cabd_extract(struct mscab_decompressor *base,
struct mscabd_folder_p *fol;
struct mspack_system *sys;
struct mspack_file *fh;
+ off_t filelen;
if (!self) return MSPACK_ERR_ARGS;
if (!file) return self->error = MSPACK_ERR_ARGS;
@@ -992,15 +1020,43 @@ static int cabd_extract(struct mscab_decompressor *base,
sys = self->system;
fol = (struct mscabd_folder_p *) file->folder;
- /* check if file can be extracted */
- if ((!fol) || (fol->merge_prev) ||
- (((file->offset + file->length) / CAB_BLOCKMAX) > fol->base.num_blocks))
- {
- sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, "
- "cabinet set is incomplete.", file->filename);
+ /* if offset is beyond 2GB, nothing can be extracted */
+ if (file->offset > CAB_LENGTHMAX) {
return self->error = MSPACK_ERR_DATAFORMAT;
}
+ /* 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->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_DECRUNCH;
+ }
+
+ /* if file goes beyond what can be decoded, given an error.
+ * In salvage mode, don't assume block sizes, just try decoding
+ */
+ if (!self->salvage) {
+ off_t maxlen = fol->base.num_blocks * CAB_BLOCKMAX;
+ if ((file->offset + filelen) > maxlen) {
+ 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 */
if (!self->d) {
self->d = (struct mscabd_decompress_state *) sys->alloc(sys, sizeof(struct mscabd_decompress_state));
@@ -1016,14 +1072,19 @@ static int cabd_extract(struct mscab_decompressor *base,
}
/* do we need to change folder or reset the current folder? */
- if ((self->d->folder != fol) || (self->d->offset > file->offset)) {
+ if ((self->d->folder != fol) || (self->d->offset > file->offset) ||
+ !self->d->state)
+ {
+ /* free any existing decompressor */
+ cabd_free_decomp(self);
+
/* do we need to open a new cab file? */
if (!self->d->infh || (fol->data.cab != self->d->incab)) {
/* close previous file handle if from a different cab */
if (self->d->infh) sys->close(self->d->infh);
self->d->incab = fol->data.cab;
self->d->infh = sys->open(sys, fol->data.cab->base.filename,
- MSPACK_SYS_OPEN_READ);
+ MSPACK_SYS_OPEN_READ);
if (!self->d->infh) return self->error = MSPACK_ERR_OPEN;
}
/* seek to start of data blocks */
@@ -1041,6 +1102,7 @@ static int cabd_extract(struct mscab_decompressor *base,
self->d->data = &fol->data;
self->d->offset = 0;
self->d->block = 0;
+ self->d->outlen = 0;
self->d->i_ptr = self->d->i_end = &self->d->input[0];
/* read_error lasts for the lifetime of a decompressor */
@@ -1055,7 +1117,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.
@@ -1065,14 +1127,14 @@ static int cabd_extract(struct mscab_decompressor *base,
*/
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;
+ 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;
}
}
@@ -1098,34 +1160,27 @@ static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct)
{
struct mspack_file *fh = (struct mspack_file *) self;
- assert(self && self->d);
-
- /* free any existing decompressor */
- cabd_free_decomp(self);
-
self->d->comp_type = ct;
switch (ct & cffoldCOMPTYPE_MASK) {
case cffoldCOMPTYPE_NONE:
self->d->decompress = (int (*)(void *, off_t)) &noned_decompress;
- self->d->state = noned_init(&self->d->sys, fh, fh,
- self->param[MSCABD_PARAM_DECOMPBUF]);
+ self->d->state = noned_init(&self->d->sys, fh, fh, self->buf_size);
break;
case cffoldCOMPTYPE_MSZIP:
self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress;
- self->d->state = mszipd_init(&self->d->sys, fh, fh,
- self->param[MSCABD_PARAM_DECOMPBUF],
- self->param[MSCABD_PARAM_FIXMSZIP]);
+ self->d->state = mszipd_init(&self->d->sys, fh, fh, self->buf_size,
+ self->fix_mszip);
break;
case cffoldCOMPTYPE_QUANTUM:
self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress;
self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f,
- self->param[MSCABD_PARAM_DECOMPBUF]);
+ self->buf_size);
break;
case cffoldCOMPTYPE_LZX:
self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress;
self->d->state = lzxd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0,
- self->param[MSCABD_PARAM_DECOMPBUF], (off_t) 0);
+ self->buf_size, (off_t)0,0);
break;
default:
return self->error = MSPACK_ERR_DATAFORMAT;
@@ -1134,7 +1189,7 @@ static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct)
}
static void cabd_free_decomp(struct mscab_decompressor_p *self) {
- if (!self || !self->d || !self->d->folder || !self->d->state) return;
+ if (!self || !self->d || !self->d->state) return;
switch (self->d->comp_type & cffoldCOMPTYPE_MASK) {
case cffoldCOMPTYPE_NONE: noned_free((struct noned_state *) self->d->state); break;
@@ -1162,10 +1217,12 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) {
struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file;
unsigned char *buf = (unsigned char *) buffer;
struct mspack_system *sys = self->system;
- int avail, todo, outlen, ignore_cksum;
+ int avail, todo, outlen, ignore_cksum, ignore_blocksize;
- ignore_cksum = self->param[MSCABD_PARAM_FIXMSZIP] &&
- ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP);
+ ignore_cksum = self->salvage ||
+ (self->fix_mszip &&
+ ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP));
+ ignore_blocksize = self->salvage;
todo = bytes;
while (todo > 0) {
@@ -1185,37 +1242,35 @@ 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->salvage) {
+ self->read_error = MSPACK_ERR_DATAFORMAT;
+ }
+ else {
+ D(("Ran out of CAB input blocks prematurely"))
+ }
+ break;
}
/* read a block */
- self->read_error = cabd_sys_read_block(sys, self->d, &outlen, ignore_cksum);
+ self->read_error = cabd_sys_read_block(sys, self->d, &outlen,
+ ignore_cksum, ignore_blocksize);
if (self->read_error) return -1;
+ self->d->outlen += outlen;
/* special Quantum hack -- trailer byte to allow the decompressor
* to realign itself. CAB Quantum blocks, unlike LZX blocks, can have
* anything from 0 to 4 trailing null bytes. */
if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) {
- *self->d->i_end++ = 0xFF;
+ *self->d->i_end++ = 0xFF;
}
/* is this the last block? */
if (self->d->block >= self->d->folder->base.num_blocks) {
- /* last block */
- if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) {
- /* 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));
- }
- }
- else {
- /* not the last block */
- if (outlen != CAB_BLOCKMAX) {
- self->system->message(self->d->infh,
- "WARNING; non-maximal data block");
- }
+ if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) {
+ /* 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, self->d->outlen);
+ }
}
} /* if (avail) */
} /* while (todo > 0) */
@@ -1238,12 +1293,13 @@ static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) {
* one cab file, if it does then the fragments will be reassembled
*/
static int cabd_sys_read_block(struct mspack_system *sys,
- struct mscabd_decompress_state *d,
- int *out, int ignore_cksum)
+ struct mscabd_decompress_state *d,
+ int *out, int ignore_cksum,
+ int ignore_blocksize)
{
unsigned char hdr[cfdata_SIZEOF];
unsigned int cksum;
- int len;
+ int len, full_len;
/* reset the input block pointer and end of block pointer */
d->i_ptr = d->i_end = &d->input[0];
@@ -1256,23 +1312,27 @@ static int cabd_sys_read_block(struct mspack_system *sys,
/* skip any reserved block headers */
if (d->data->cab->block_resv &&
- sys->seek(d->infh, (off_t) d->data->cab->block_resv,
- MSPACK_SYS_SEEK_CUR))
+ sys->seek(d->infh, (off_t) d->data->cab->block_resv,
+ MSPACK_SYS_SEEK_CUR))
{
return MSPACK_ERR_SEEK;
}
/* blocks must not be over CAB_INPUTMAX in size */
len = EndGetI16(&hdr[cfdata_CompressedSize]);
- if (((d->i_end - d->i_ptr) + len) > CAB_INPUTMAX) {
- D(("block size > CAB_INPUTMAX (%ld + %d)", d->i_end - d->i_ptr, len))
- return MSPACK_ERR_DATAFORMAT;
+ full_len = (d->i_end - d->i_ptr) + len; /* include cab-spanning blocks */
+ if (full_len > CAB_INPUTMAX) {
+ D(("block size %d > CAB_INPUTMAX", full_len));
+ /* in salvage mode, blocks can be 65535 bytes but no more than that */
+ if (!ignore_blocksize || full_len > CAB_INPUTMAX_SALVAGE) {
+ return MSPACK_ERR_DATAFORMAT;
+ }
}
/* blocks must not expand to more than CAB_BLOCKMAX */
if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) {
D(("block size > CAB_BLOCKMAX"))
- return MSPACK_ERR_DATAFORMAT;
+ if (!ignore_blocksize) return MSPACK_ERR_DATAFORMAT;
}
/* read the block data */
@@ -1284,8 +1344,8 @@ static int cabd_sys_read_block(struct mspack_system *sys,
if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) {
unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0);
if (cabd_checksum(&hdr[4], 4, sum2) != cksum) {
- if (!ignore_cksum) return MSPACK_ERR_CHECKSUM;
- sys->message(d->infh, "WARNING; bad block checksum found");
+ if (!ignore_cksum) return MSPACK_ERR_CHECKSUM;
+ sys->message(d->infh, "WARNING; bad block checksum found");
}
}
@@ -1310,14 +1370,14 @@ 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;
}
/* open next cab file */
d->incab = d->data->cab;
if (!(d->infh = sys->open(sys, d->incab->base.filename,
- MSPACK_SYS_OPEN_READ)))
+ MSPACK_SYS_OPEN_READ)))
{
return MSPACK_ERR_OPEN;
}
@@ -1333,7 +1393,7 @@ static int cabd_sys_read_block(struct mspack_system *sys,
}
static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes,
- unsigned int cksum)
+ unsigned int cksum)
{
unsigned int len, ul = 0;
@@ -1342,8 +1402,8 @@ static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes,
}
switch (bytes & 3) {
- case 3: ul |= *data++ << 16;
- case 2: ul |= *data++ << 8;
+ case 3: ul |= *data++ << 16; /*@fallthrough@*/
+ case 2: ul |= *data++ << 8; /*@fallthrough@*/
case 1: ul |= *data;
}
cksum ^= ul;
@@ -1365,9 +1425,9 @@ struct noned_state {
};
static struct noned_state *noned_init(struct mspack_system *sys,
- struct mspack_file *in,
- struct mspack_file *out,
- int bufsize)
+ struct mspack_file *in,
+ struct mspack_file *out,
+ int bufsize)
{
struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state));
unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) bufsize);
@@ -1419,14 +1479,17 @@ static int cabd_param(struct mscab_decompressor *base, int param, int value) {
switch (param) {
case MSCABD_PARAM_SEARCHBUF:
if (value < 4) return MSPACK_ERR_ARGS;
- self->param[MSCABD_PARAM_SEARCHBUF] = value;
+ self->searchbuf_size = value;
break;
case MSCABD_PARAM_FIXMSZIP:
- self->param[MSCABD_PARAM_FIXMSZIP] = value;
+ self->fix_mszip = value;
break;
case MSCABD_PARAM_DECOMPBUF:
if (value < 4) return MSPACK_ERR_ARGS;
- self->param[MSCABD_PARAM_DECOMPBUF] = value;
+ self->buf_size = value;
+ break;
+ case MSCABD_PARAM_SALVAGE:
+ self->salvage = value;
break;
default:
return MSPACK_ERR_ARGS;