... | ... |
@@ -177,7 +177,7 @@ endif |
177 | 177 |
# libmspack version: |
178 | 178 |
LIBMSPACK_VERSION = 0:7:1 |
179 | 179 |
|
180 |
-libclammspack_la_CFLAGS = -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I@top_srcdir@/libclammspack/mspack |
|
180 |
+libclammspack_la_CFLAGS = -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wno-unused-parameter -I@top_srcdir@/libclammspack/mspack |
|
181 | 181 |
libclammspack_la_LDFLAGS = -version-info $(LIBMSPACK_VERSION) -no-undefined -export-symbols-regex '^mspack_' |
182 | 182 |
|
183 | 183 |
# if VERSIONSCRIPT |
... | ... |
@@ -1177,7 +1177,7 @@ SUBDIRS = $(am__append_6) |
1177 | 1177 |
|
1178 | 1178 |
# libmspack version: |
1179 | 1179 |
LIBMSPACK_VERSION = 0:5:0 |
1180 |
-libclammspack_la_CFLAGS = -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I@top_srcdir@/libclammspack/mspack |
|
1180 |
+libclammspack_la_CFLAGS = -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wno-unused-parameter -I@top_srcdir@/libclammspack/mspack |
|
1181 | 1181 |
libclammspack_la_LDFLAGS = -version-info $(LIBMSPACK_VERSION) -no-undefined -export-symbols-regex '^mspack_' |
1182 | 1182 |
|
1183 | 1183 |
# if VERSIONSCRIPT |
... | ... |
@@ -369,6 +369,9 @@ int cli_scanmscab(cli_ctx *ctx, off_t sfx_offset) |
369 | 369 |
return CL_EUNPACK; |
370 | 370 |
} |
371 | 371 |
|
372 |
+ cab_d->set_param(cab_d, MSCABD_PARAM_FIXMSZIP, 1); |
|
373 |
+ cab_d->set_param(cab_d, MSCABD_PARAM_SALVAGE, 1); |
|
374 |
+ |
|
372 | 375 |
cab_h = cab_d->open(cab_d, (char *)&mspack_fmap); |
373 | 376 |
if (!cab_h) { |
374 | 377 |
ret = CL_EFORMAT; |
... | ... |
@@ -1,3 +1,18 @@ |
1 |
+18-09-16 Stuart Caie <kyzer@cabextract.org.uk> |
|
2 |
+ |
|
3 |
+ * cabd.c: add new parameter, MSCABD_PARAM_SALVAGE, which makes CAB file |
|
4 |
+ reading and extraction more lenient, to allow damaged or mangled CABs |
|
5 |
+ to be extracted. When enabled, cabd->open() will no longer reject |
|
6 |
+ cabinets entirely if they have files with invalid folder indicies |
|
7 |
+ or filenames, but will simply ignore those files. Also, cabd->extract() |
|
8 |
+ will limit files with invalid lengths to the maximum possible, rather |
|
9 |
+ than reject them. Finally, invalid data block checksums will produce |
|
10 |
+ warnings, but won't cause decompression to fail. It's still possible |
|
11 |
+ for corrupted files to fail extraction, but hopefully more data can |
|
12 |
+ be extracted before they do. This new parameter doesn't affect the |
|
13 |
+ existing MSCABD_PARAM_FIXMSZIP parameter, which ignores MSZIP |
|
14 |
+ decompression failures. |
|
15 |
+ |
|
1 | 16 |
2018-08-09 Stuart Caie <kyzer@cabextract.org.uk> |
2 | 17 |
|
3 | 18 |
* Makefile.am: the test file cve-2015-4467-reset-interval-zero.chm is |
... | ... |
@@ -67,7 +67,8 @@ |
67 | 67 |
* more than 6144 bytes. Quantum has no documentation, but the largest |
68 | 68 |
* block seen in the wild is 337 bytes above uncompressed size. |
69 | 69 |
*/ |
70 |
-#define CAB_BLOCKMAX (32768) |
|
70 |
+#define CAB_BLOCKMAX (65535) |
|
71 |
+#define CAB_BLOCKSTD (32768) |
|
71 | 72 |
#define CAB_INPUTMAX (CAB_BLOCKMAX+6144) |
72 | 73 |
|
73 | 74 |
/* There are no more than 65535 data blocks per folder, so a folder cannot |
... | ... |
@@ -75,7 +76,7 @@ |
75 | 75 |
* one folder, this is also their max offset, length and offset+length limit. |
76 | 76 |
*/ |
77 | 77 |
#define CAB_FOLDERMAX (65535) |
78 |
-#define CAB_LENGTHMAX (CAB_BLOCKMAX * CAB_FOLDERMAX) |
|
78 |
+#define CAB_LENGTHMAX UINT_MAX |
|
79 | 79 |
|
80 | 80 |
/* CAB compression definitions */ |
81 | 81 |
|
... | ... |
@@ -107,7 +108,7 @@ struct mscab_decompressor_p { |
107 | 107 |
struct mscab_decompressor base; |
108 | 108 |
struct mscabd_decompress_state *d; |
109 | 109 |
struct mspack_system *system; |
110 |
- int param[3]; /* !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!! */ |
|
110 |
+ int param[4]; /* !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!! */ |
|
111 | 111 |
int error, read_error; |
112 | 112 |
}; |
113 | 113 |
|
... | ... |
@@ -72,7 +72,7 @@ static void cabd_close( |
72 | 72 |
struct mscab_decompressor *base, struct mscabd_cabinet *origcab); |
73 | 73 |
static int cabd_read_headers( |
74 | 74 |
struct mspack_system *sys, struct mspack_file *fh, |
75 |
- struct mscabd_cabinet_p *cab, off_t offset, int quiet); |
|
75 |
+ struct mscabd_cabinet_p *cab, off_t offset, int salvage, int quiet); |
|
76 | 76 |
static char *cabd_read_string( |
77 | 77 |
struct mspack_system *sys, struct mspack_file *fh, int *error); |
78 | 78 |
|
... | ... |
@@ -157,6 +157,7 @@ struct mscab_decompressor * |
157 | 157 |
self->param[MSCABD_PARAM_SEARCHBUF] = 32768; |
158 | 158 |
self->param[MSCABD_PARAM_FIXMSZIP] = 0; |
159 | 159 |
self->param[MSCABD_PARAM_DECOMPBUF] = 4096; |
160 |
+ self->param[MSCABD_PARAM_SALVAGE] = 0; |
|
160 | 161 |
} |
161 | 162 |
return (struct mscab_decompressor *) self; |
162 | 163 |
} |
... | ... |
@@ -200,7 +201,7 @@ static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, |
200 | 200 |
if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { |
201 | 201 |
if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { |
202 | 202 |
cab->base.filename = filename; |
203 |
- error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0); |
|
203 |
+ error = cabd_read_headers(sys, fh, cab, (off_t) 0, self->param[MSCABD_PARAM_SALVAGE], 0); |
|
204 | 204 |
if (error) { |
205 | 205 |
cabd_close(base, (struct mscabd_cabinet *) cab); |
206 | 206 |
cab = NULL; |
... | ... |
@@ -305,9 +306,9 @@ static void cabd_close(struct mscab_decompressor *base, |
305 | 305 |
static int cabd_read_headers(struct mspack_system *sys, |
306 | 306 |
struct mspack_file *fh, |
307 | 307 |
struct mscabd_cabinet_p *cab, |
308 |
- off_t offset, int quiet) |
|
308 |
+ off_t offset, int quiet, int salvage) |
|
309 | 309 |
{ |
310 |
- int num_folders, num_files, folder_resv, i, x; |
|
310 |
+ int num_folders, num_files, folder_resv, i, x, fidx, fidx_ok, read_string_errno = 0; |
|
311 | 311 |
struct mscabd_folder_p *fol, *linkfol = NULL; |
312 | 312 |
struct mscabd_file *file, *linkfile = NULL; |
313 | 313 |
unsigned char buf[64]; |
... | ... |
@@ -363,6 +364,7 @@ static int cabd_read_headers(struct mspack_system *sys, |
363 | 363 |
|
364 | 364 |
/* read the reserved-sizes part of header, if present */ |
365 | 365 |
cab->base.flags = EndGetI16(&buf[cfhead_Flags]); |
366 |
+ |
|
366 | 367 |
if (cab->base.flags & cfheadRESERVE_PRESENT) { |
367 | 368 |
if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { |
368 | 369 |
return MSPACK_ERR_READ; |
... | ... |
@@ -390,14 +392,18 @@ static int cabd_read_headers(struct mspack_system *sys, |
390 | 390 |
|
391 | 391 |
/* read name and info of preceeding cabinet in set, if present */ |
392 | 392 |
if (cab->base.flags & cfheadPREV_CABINET) { |
393 |
- cab->base.prevname = cabd_read_string(sys, fh, &x); if (x) return x; |
|
394 |
- cab->base.previnfo = cabd_read_string(sys, fh, &x); if (x) return x; |
|
393 |
+ cab->base.prevname = cabd_read_string(sys, fh, &read_string_errno); |
|
394 |
+ if (read_string_errno) return read_string_errno; |
|
395 |
+ cab->base.previnfo = cabd_read_string(sys, fh, &read_string_errno); |
|
396 |
+ if (read_string_errno) return read_string_errno; |
|
395 | 397 |
} |
396 | 398 |
|
397 | 399 |
/* read name and info of next cabinet in set, if present */ |
398 | 400 |
if (cab->base.flags & cfheadNEXT_CABINET) { |
399 |
- cab->base.nextname = cabd_read_string(sys, fh, &x); if (x) return x; |
|
400 |
- cab->base.nextinfo = cabd_read_string(sys, fh, &x); if (x) return x; |
|
401 |
+ cab->base.nextname = cabd_read_string(sys, fh, &read_string_errno); |
|
402 |
+ if (read_string_errno) return read_string_errno; |
|
403 |
+ cab->base.nextinfo = cabd_read_string(sys, fh, &read_string_errno); |
|
404 |
+ if (read_string_errno) return read_string_errno; |
|
401 | 405 |
} |
402 | 406 |
|
403 | 407 |
/* read folders */ |
... | ... |
@@ -446,25 +452,29 @@ static int cabd_read_headers(struct mspack_system *sys, |
446 | 446 |
file->offset = EndGetI32(&buf[cffile_FolderOffset]); |
447 | 447 |
|
448 | 448 |
/* set folder pointer */ |
449 |
- x = EndGetI16(&buf[cffile_FolderIndex]); |
|
450 |
- if (x < cffileCONTINUED_FROM_PREV) { |
|
451 |
- /* normal folder index; count up to the correct folder. the folder |
|
452 |
- * pointer will be NULL if folder index is invalid */ |
|
453 |
- struct mscabd_folder *ifol = cab->base.folders; |
|
454 |
- while (x--) if (ifol) ifol = ifol->next; |
|
455 |
- file->folder = ifol; |
|
456 |
- |
|
457 |
- if (!ifol) { |
|
458 |
- sys->free(file); |
|
459 |
- D(("invalid folder index")) |
|
460 |
- return MSPACK_ERR_DATAFORMAT; |
|
449 |
+ fidx_ok = 1; |
|
450 |
+ fidx = EndGetI16(&buf[cffile_FolderIndex]); |
|
451 |
+ if (fidx < cffileCONTINUED_FROM_PREV) { |
|
452 |
+ /* normal folder index; count up to the correct folder */ |
|
453 |
+ if (fidx < num_folders) { |
|
454 |
+ struct mscabd_folder *ifol = cab->base.folders; |
|
455 |
+ while (fidx--) if (ifol) ifol = ifol->next; |
|
456 |
+ file->folder = ifol; |
|
457 |
+ } |
|
458 |
+ else { |
|
459 |
+ file->folder = NULL; |
|
460 |
+ } |
|
461 |
+ |
|
462 |
+ if (!file->folder) { |
|
463 |
+ D(("invalid folder index")) |
|
464 |
+ fidx_ok = 0; |
|
461 | 465 |
} |
462 | 466 |
} |
463 | 467 |
else { |
464 | 468 |
/* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or |
465 | 469 |
* CONTINUED_PREV_AND_NEXT */ |
466 |
- if ((x == cffileCONTINUED_TO_NEXT) || |
|
467 |
- (x == cffileCONTINUED_PREV_AND_NEXT)) |
|
470 |
+ if ((fidx == cffileCONTINUED_TO_NEXT) || |
|
471 |
+ (fidx == cffileCONTINUED_PREV_AND_NEXT)) |
|
468 | 472 |
{ |
469 | 473 |
/* get last folder */ |
470 | 474 |
struct mscabd_folder *ifol = cab->base.folders; |
... | ... |
@@ -476,8 +486,8 @@ static int cabd_read_headers(struct mspack_system *sys, |
476 | 476 |
if (!fol->merge_next) fol->merge_next = file; |
477 | 477 |
} |
478 | 478 |
|
479 |
- if ((x == cffileCONTINUED_FROM_PREV) || |
|
480 |
- (x == cffileCONTINUED_PREV_AND_NEXT)) |
|
479 |
+ if ((fidx == cffileCONTINUED_FROM_PREV) || |
|
480 |
+ (fidx == cffileCONTINUED_PREV_AND_NEXT)) |
|
481 | 481 |
{ |
482 | 482 |
/* get first folder */ |
483 | 483 |
file->folder = cab->base.folders; |
... | ... |
@@ -501,10 +511,11 @@ static int cabd_read_headers(struct mspack_system *sys, |
501 | 501 |
file->date_y = (x >> 9) + 1980; |
502 | 502 |
|
503 | 503 |
/* get filename */ |
504 |
- file->filename = cabd_read_string(sys, fh, &x); |
|
505 |
- if (x) { |
|
504 |
+ file->filename = cabd_read_string(sys, fh, &read_string_errno); |
|
505 |
+ if (read_string_errno || !fidx_ok) { |
|
506 |
+ sys->free(file->filename); |
|
506 | 507 |
sys->free(file); |
507 |
- return x; |
|
508 |
+ if (salvage) continue; else return read_string_errno; |
|
508 | 509 |
} |
509 | 510 |
|
510 | 511 |
/* link file entry into file list */ |
... | ... |
@@ -513,6 +524,13 @@ static int cabd_read_headers(struct mspack_system *sys, |
513 | 513 |
linkfile = file; |
514 | 514 |
} |
515 | 515 |
|
516 |
+ if (cab->base.files == NULL) { |
|
517 |
+ /* We never actually added any files to the file list. Something went wrong. |
|
518 |
+ * The file header may have been invalid */ |
|
519 |
+ D(("No files found, even though header claimed to have %d files", num_files)) |
|
520 |
+ return MSPACK_ERR_DATAFORMAT; |
|
521 |
+ } |
|
522 |
+ |
|
516 | 523 |
return MSPACK_ERR_OK; |
517 | 524 |
} |
518 | 525 |
|
... | ... |
@@ -631,7 +649,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, |
631 | 631 |
struct mspack_system *sys = self->system; |
632 | 632 |
unsigned char *p, *pend, state = 0; |
633 | 633 |
unsigned int cablen_u32 = 0, foffset_u32 = 0; |
634 |
- int false_cabs = 0; |
|
634 |
+ int false_cabs = 0, salvage = self->param[MSCABD_PARAM_SALVAGE]; |
|
635 | 635 |
|
636 | 636 |
#if !LARGEFILE_SUPPORT |
637 | 637 |
/* detect 32-bit off_t overflow */ |
... | ... |
@@ -718,7 +736,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, |
718 | 718 |
return MSPACK_ERR_NOMEMORY; |
719 | 719 |
} |
720 | 720 |
cab->base.filename = filename; |
721 |
- if (cabd_read_headers(sys, fh, cab, caboff, 1)) { |
|
721 |
+ if (cabd_read_headers(sys, fh, cab, caboff, salvage, 1)) { |
|
722 | 722 |
/* destroy the failed cabinet */ |
723 | 723 |
cabd_close((struct mscab_decompressor *) self, |
724 | 724 |
(struct mscabd_cabinet *) cab); |
... | ... |
@@ -994,6 +1012,7 @@ static int cabd_extract(struct mscab_decompressor *base, |
994 | 994 |
struct mscabd_folder_p *fol; |
995 | 995 |
struct mspack_system *sys; |
996 | 996 |
struct mspack_file *fh; |
997 |
+ off_t filelen, maxlen; |
|
997 | 998 |
|
998 | 999 |
if (!self) return MSPACK_ERR_ARGS; |
999 | 1000 |
if (!file) return self->error = MSPACK_ERR_ARGS; |
... | ... |
@@ -1001,20 +1020,46 @@ static int cabd_extract(struct mscab_decompressor *base, |
1001 | 1001 |
sys = self->system; |
1002 | 1002 |
fol = (struct mscabd_folder_p *) file->folder; |
1003 | 1003 |
|
1004 |
- /* validate the file's offset and length */ |
|
1005 |
- if ( (file->offset > CAB_LENGTHMAX) || (file->length > CAB_LENGTHMAX) || |
|
1006 |
- ((file->offset + file->length) > CAB_LENGTHMAX)) |
|
1007 |
- { |
|
1004 |
+ /* if offset is beyond 2GB, nothing can be extracted */ |
|
1005 |
+ if (file->offset > CAB_LENGTHMAX) { |
|
1008 | 1006 |
return self->error = MSPACK_ERR_DATAFORMAT; |
1009 | 1007 |
} |
1010 | 1008 |
|
1011 |
- /* check if file can be extracted */ |
|
1012 |
- if ((!fol) || (fol->merge_prev) || |
|
1013 |
- (((file->offset + file->length) / CAB_BLOCKMAX) > fol->base.num_blocks)) |
|
1014 |
- { |
|
1009 |
+ /* if file claims to go beyond 2GB either error out, |
|
1010 |
+ * or in salvage mode reduce file length so it fits 2GB limit |
|
1011 |
+ */ |
|
1012 |
+ filelen = file->length; |
|
1013 |
+ if (filelen > CAB_LENGTHMAX || (file->offset + filelen) > CAB_LENGTHMAX) { |
|
1014 |
+ if (self->param[MSCABD_PARAM_SALVAGE]) { |
|
1015 |
+ filelen = CAB_LENGTHMAX - file->offset; |
|
1016 |
+ } |
|
1017 |
+ else { |
|
1018 |
+ return self->error = MSPACK_ERR_DATAFORMAT; |
|
1019 |
+ } |
|
1020 |
+ } |
|
1021 |
+ |
|
1022 |
+ /* extraction impossible if no folder, or folder needs predecessor */ |
|
1023 |
+ if (!fol || fol->merge_prev) { |
|
1015 | 1024 |
sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " |
1016 |
- "cabinet set is incomplete.", file->filename); |
|
1017 |
- return self->error = MSPACK_ERR_DATAFORMAT; |
|
1025 |
+ "cabinet set is incomplete", file->filename); |
|
1026 |
+ return self->error = MSPACK_ERR_DECRUNCH; |
|
1027 |
+ } |
|
1028 |
+ |
|
1029 |
+ /* if file goes beyond what can be decoded, either error, |
|
1030 |
+ * or in salvage mode reduce file length to what can be decoded |
|
1031 |
+ */ |
|
1032 |
+ maxlen = fol->base.num_blocks * CAB_BLOCKMAX; |
|
1033 |
+ if ((file->offset + filelen) > maxlen) { |
|
1034 |
+ if (self->param[MSCABD_PARAM_SALVAGE]) { |
|
1035 |
+ sys->message(NULL, "WARNING: can only extract first %"LD" bytes " |
|
1036 |
+ " of file \"%s\"", maxlen, file->filename); |
|
1037 |
+ filelen = maxlen; |
|
1038 |
+ } |
|
1039 |
+ else { |
|
1040 |
+ sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " |
|
1041 |
+ "cabinet set is incomplete", file->filename); |
|
1042 |
+ return self->error = MSPACK_ERR_DECRUNCH; |
|
1043 |
+ } |
|
1018 | 1044 |
} |
1019 | 1045 |
|
1020 | 1046 |
/* allocate generic decompression state */ |
... | ... |
@@ -1076,7 +1121,7 @@ static int cabd_extract(struct mscab_decompressor *base, |
1076 | 1076 |
self->error = MSPACK_ERR_OK; |
1077 | 1077 |
|
1078 | 1078 |
/* if file has more than 0 bytes */ |
1079 |
- if (file->length) { |
|
1079 |
+ if (filelen) { |
|
1080 | 1080 |
off_t bytes; |
1081 | 1081 |
int error; |
1082 | 1082 |
/* get to correct offset. |
... | ... |
@@ -1085,15 +1130,17 @@ static int cabd_extract(struct mscab_decompressor *base, |
1085 | 1085 |
* and pass back MSPACK_ERR_READ |
1086 | 1086 |
*/ |
1087 | 1087 |
self->d->outfh = NULL; |
1088 |
- if ((bytes = file->offset - self->d->offset)) { |
|
1089 |
- error = self->d->decompress(self->d->state, bytes); |
|
1090 |
- self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; |
|
1088 |
+ if ((self->d->comp_type & cffoldCOMPTYPE_MASK) != cffoldCOMPTYPE_LZX) { |
|
1089 |
+ if ((bytes = file->offset - self->d->offset)) { |
|
1090 |
+ error = self->d->decompress(self->d->state, bytes); |
|
1091 |
+ self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; |
|
1092 |
+ } |
|
1091 | 1093 |
} |
1092 | 1094 |
|
1093 | 1095 |
/* if getting to the correct offset was error free, unpack file */ |
1094 | 1096 |
if (!self->error) { |
1095 | 1097 |
self->d->outfh = fh; |
1096 |
- error = self->d->decompress(self->d->state, (off_t) file->length); |
|
1098 |
+ error = self->d->decompress(self->d->state, filelen); |
|
1097 | 1099 |
self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; |
1098 | 1100 |
} |
1099 | 1101 |
} |
... | ... |
@@ -1182,8 +1229,9 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { |
1182 | 1182 |
struct mspack_system *sys = self->system; |
1183 | 1183 |
int avail, todo, outlen, ignore_cksum; |
1184 | 1184 |
|
1185 |
- ignore_cksum = self->param[MSCABD_PARAM_FIXMSZIP] && |
|
1186 |
- ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP); |
|
1185 |
+ ignore_cksum = self->param[MSCABD_PARAM_SALVAGE] || |
|
1186 |
+ (self->param[MSCABD_PARAM_FIXMSZIP] && |
|
1187 |
+ ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP)); |
|
1187 | 1188 |
|
1188 | 1189 |
todo = bytes; |
1189 | 1190 |
while (todo > 0) { |
... | ... |
@@ -1203,8 +1251,11 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { |
1203 | 1203 |
|
1204 | 1204 |
/* check if we're out of input blocks, advance block counter */ |
1205 | 1205 |
if (self->d->block++ >= self->d->folder->base.num_blocks) { |
1206 |
- self->read_error = MSPACK_ERR_DATAFORMAT; |
|
1207 |
- break; |
|
1206 |
+ if (!self->param[MSCABD_PARAM_SALVAGE]) |
|
1207 |
+ self->read_error = MSPACK_ERR_DATAFORMAT; |
|
1208 |
+ else |
|
1209 |
+ D(("Ran out of CAB input blocks prematurely")) |
|
1210 |
+ break; |
|
1208 | 1211 |
} |
1209 | 1212 |
|
1210 | 1213 |
/* read a block */ |
... | ... |
@@ -1225,7 +1276,7 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { |
1225 | 1225 |
/* special LZX hack -- on the last block, inform LZX of the |
1226 | 1226 |
* size of the output data stream. */ |
1227 | 1227 |
lzxd_set_output_length((struct lzxd_stream *) self->d->state, (off_t) |
1228 |
- ((self->d->block-1) * CAB_BLOCKMAX + outlen)); |
|
1228 |
+ ((self->d->block-1) * CAB_BLOCKSTD + outlen)); |
|
1229 | 1229 |
} |
1230 | 1230 |
} |
1231 | 1231 |
else { |
... | ... |
@@ -1329,7 +1380,7 @@ static int cabd_sys_read_block(struct mspack_system *sys, |
1329 | 1329 |
|
1330 | 1330 |
/* advance to next member in the cabinet set */ |
1331 | 1331 |
if (!(d->data = d->data->next)) { |
1332 |
- D(("ran out of splits in cabinet set")) |
|
1332 |
+ sys->message(d->infh, "WARNING; ran out of cabinets in set. Are any missing?"); |
|
1333 | 1333 |
return MSPACK_ERR_DATAFORMAT; |
1334 | 1334 |
} |
1335 | 1335 |
|
... | ... |
@@ -1447,6 +1498,9 @@ static int cabd_param(struct mscab_decompressor *base, int param, int value) { |
1447 | 1447 |
if (value < 4) return MSPACK_ERR_ARGS; |
1448 | 1448 |
self->param[MSCABD_PARAM_DECOMPBUF] = value; |
1449 | 1449 |
break; |
1450 |
+ case MSCABD_PARAM_SALVAGE: |
|
1451 |
+ self->param[MSCABD_PARAM_SALVAGE] = value; |
|
1452 |
+ break; |
|
1450 | 1453 |
default: |
1451 | 1454 |
return MSPACK_ERR_ARGS; |
1452 | 1455 |
} |
... | ... |
@@ -536,7 +536,7 @@ static int chmd_fast_find(struct mschm_decompressor *base, |
536 | 536 |
struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base; |
537 | 537 |
struct mspack_system *sys; |
538 | 538 |
struct mspack_file *fh; |
539 |
- const unsigned char *chunk, *p, *end; |
|
539 |
+ const unsigned char *chunk, *p = NULL, *end = NULL; |
|
540 | 540 |
int err = MSPACK_ERR_OK, result = -1; |
541 | 541 |
unsigned int n, sec; |
542 | 542 |
|
... | ... |
@@ -399,7 +399,7 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { |
399 | 399 |
register unsigned short sym; |
400 | 400 |
|
401 | 401 |
int match_length, length_footer, extra, verbatim_bits, bytes_todo; |
402 |
- int this_run, main_element, aligned_bits, j; |
|
402 |
+ int this_run, main_element, aligned_bits, j, warned = 0; |
|
403 | 403 |
unsigned char *window, *runsrc, *rundest, buf[12]; |
404 | 404 |
unsigned int frame_size=0, end_frame, match_offset, window_posn; |
405 | 405 |
unsigned int R0, R1, R2; |
... | ... |
@@ -435,8 +435,12 @@ int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { |
435 | 435 |
/* have we reached the reset interval? (if there is one?) */ |
436 | 436 |
if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) { |
437 | 437 |
if (lzx->block_remaining) { |
438 |
- D(("%d bytes remaining at reset interval", lzx->block_remaining)) |
|
439 |
- return lzx->error = MSPACK_ERR_DECRUNCH; |
|
438 |
+ /* this is a file format error, we can make a best effort to extract what we can */ |
|
439 |
+ D(("%d bytes remaining at reset interval", lzx->block_remaining)) |
|
440 |
+ if (!warned) { |
|
441 |
+ lzx->sys->message(NULL, "WARNING; invalid reset interval detected during LZX decompression"); |
|
442 |
+ warned++; |
|
443 |
+ } |
|
440 | 444 |
} |
441 | 445 |
|
442 | 446 |
/* re-read the intel header and reset the huffman lengths */ |
... | ... |
@@ -934,6 +934,13 @@ struct mscabd_file { |
934 | 934 |
#define MSCABD_PARAM_FIXMSZIP (1) |
935 | 935 |
/** mscab_decompressor::set_param() parameter: size of decompression buffer */ |
936 | 936 |
#define MSCABD_PARAM_DECOMPBUF (2) |
937 |
+/** mscab_decompressor::set_param() parameter: salvage data from bad cabinets? |
|
938 |
+ * If enabled, open() will skip file with bad folder indices or filenames |
|
939 |
+ * rather than reject the whole cabinet, and extract() will limit rather than |
|
940 |
+ * reject files with invalid offsets and lengths, and bad data block checksums |
|
941 |
+ * will be ignored. Available only in CAB decoder version 2 and above. |
|
942 |
+ */ |
|
943 |
+#define MSCABD_PARAM_SALVAGE (3) |
|
937 | 944 |
|
938 | 945 |
/** TODO */ |
939 | 946 |
struct mscab_compressor { |
... | ... |
@@ -27,10 +27,13 @@ int mspack_version(int entity) { |
27 | 27 |
* - added mschmd_header::chunk_cache; |
28 | 28 |
*/ |
29 | 29 |
case MSPACK_VER_MSCHMD: |
30 |
+ /* CAB decoder version 1 -> 2 changes: |
|
31 |
+ * - added MSCABD_PARAM_SALVAGE |
|
32 |
+ */ |
|
33 |
+ case MSPACK_VER_MSCABD: |
|
30 | 34 |
return 2; |
31 | 35 |
case MSPACK_VER_LIBRARY: |
32 | 36 |
case MSPACK_VER_SYSTEM: |
33 |
- case MSPACK_VER_MSCABD: |
|
34 | 37 |
case MSPACK_VER_MSSZDDD: |
35 | 38 |
case MSPACK_VER_MSKWAJD: |
36 | 39 |
case MSPACK_VER_MSOABD: |