diff options
Diffstat (limited to 'rbutil/rbutilqt/mspack/cabd.c')
-rw-r--r-- | rbutil/rbutilqt/mspack/cabd.c | 541 |
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 @@ | |||
1 | /* This file is part of libmspack. | 1 | /* This file is part of libmspack. |
2 | * (C) 2003-2011 Stuart Caie. | 2 | * (C) 2003-2018 Stuart Caie. |
3 | * | 3 | * |
4 | * libmspack is free software; you can redistribute it and/or modify it under | 4 | * libmspack is free software; you can redistribute it and/or modify it under |
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | 5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
@@ -23,7 +23,9 @@ | |||
23 | 23 | ||
24 | #include "system-mspack.h" | 24 | #include "system-mspack.h" |
25 | #include "cab.h" | 25 | #include "cab.h" |
26 | #include <assert.h> | 26 | #include "mszip.h" |
27 | #include "lzx.h" | ||
28 | #include "qtm.h" | ||
27 | 29 | ||
28 | /* Notes on compliance with cabinet specification: | 30 | /* Notes on compliance with cabinet specification: |
29 | * | 31 | * |
@@ -72,10 +74,9 @@ static void cabd_close( | |||
72 | struct mscab_decompressor *base, struct mscabd_cabinet *origcab); | 74 | struct mscab_decompressor *base, struct mscabd_cabinet *origcab); |
73 | static int cabd_read_headers( | 75 | static int cabd_read_headers( |
74 | struct mspack_system *sys, struct mspack_file *fh, | 76 | struct mspack_system *sys, struct mspack_file *fh, |
75 | struct mscabd_cabinet_p *cab, off_t offset, int quiet); | 77 | struct mscabd_cabinet_p *cab, off_t offset, int salvage, int quiet); |
76 | static char *cabd_read_string( | 78 | static char *cabd_read_string( |
77 | struct mspack_system *sys, struct mspack_file *fh, | 79 | struct mspack_system *sys, struct mspack_file *fh, int *error); |
78 | struct mscabd_cabinet_p *cab, int *error); | ||
79 | 80 | ||
80 | static struct mscabd_cabinet *cabd_search( | 81 | static struct mscabd_cabinet *cabd_search( |
81 | struct mscab_decompressor *base, const char *filename); | 82 | struct mscab_decompressor *base, const char *filename); |
@@ -110,7 +111,7 @@ static int cabd_sys_write( | |||
110 | struct mspack_file *file, void *buffer, int bytes); | 111 | struct mspack_file *file, void *buffer, int bytes); |
111 | static int cabd_sys_read_block( | 112 | static int cabd_sys_read_block( |
112 | struct mspack_system *sys, struct mscabd_decompress_state *d, int *out, | 113 | struct mspack_system *sys, struct mscabd_decompress_state *d, int *out, |
113 | int ignore_cksum); | 114 | int ignore_cksum, int ignore_blocksize); |
114 | static unsigned int cabd_checksum( | 115 | static unsigned int cabd_checksum( |
115 | unsigned char *data, unsigned int bytes, unsigned int cksum); | 116 | unsigned char *data, unsigned int bytes, unsigned int cksum); |
116 | static struct noned_state *noned_init( | 117 | static struct noned_state *noned_init( |
@@ -155,9 +156,10 @@ struct mscab_decompressor * | |||
155 | self->d = NULL; | 156 | self->d = NULL; |
156 | self->error = MSPACK_ERR_OK; | 157 | self->error = MSPACK_ERR_OK; |
157 | 158 | ||
158 | self->param[MSCABD_PARAM_SEARCHBUF] = 32768; | 159 | self->searchbuf_size = 32768; |
159 | self->param[MSCABD_PARAM_FIXMSZIP] = 0; | 160 | self->fix_mszip = 0; |
160 | self->param[MSCABD_PARAM_DECOMPBUF] = 4096; | 161 | self->buf_size = 4096; |
162 | self->salvage = 0; | ||
161 | } | 163 | } |
162 | return (struct mscab_decompressor *) self; | 164 | return (struct mscab_decompressor *) self; |
163 | } | 165 | } |
@@ -171,9 +173,9 @@ void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) { | |||
171 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | 173 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
172 | if (self) { | 174 | if (self) { |
173 | struct mspack_system *sys = self->system; | 175 | struct mspack_system *sys = self->system; |
174 | cabd_free_decomp(self); | ||
175 | if (self->d) { | 176 | if (self->d) { |
176 | if (self->d->infh) sys->close(self->d->infh); | 177 | if (self->d->infh) sys->close(self->d->infh); |
178 | cabd_free_decomp(self); | ||
177 | sys->free(self->d); | 179 | sys->free(self->d); |
178 | } | 180 | } |
179 | sys->free(self); | 181 | sys->free(self); |
@@ -187,7 +189,7 @@ void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) { | |||
187 | * opens a file and tries to read it as a cabinet file | 189 | * opens a file and tries to read it as a cabinet file |
188 | */ | 190 | */ |
189 | static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, | 191 | static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, |
190 | const char *filename) | 192 | const char *filename) |
191 | { | 193 | { |
192 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | 194 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
193 | struct mscabd_cabinet_p *cab = NULL; | 195 | struct mscabd_cabinet_p *cab = NULL; |
@@ -201,10 +203,10 @@ static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, | |||
201 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { | 203 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { |
202 | if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { | 204 | if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { |
203 | cab->base.filename = filename; | 205 | cab->base.filename = filename; |
204 | error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0); | 206 | error = cabd_read_headers(sys, fh, cab, (off_t) 0, self->salvage, 0); |
205 | if (error) { | 207 | if (error) { |
206 | cabd_close(base, (struct mscabd_cabinet *) cab); | 208 | cabd_close(base, (struct mscabd_cabinet *) cab); |
207 | cab = NULL; | 209 | cab = NULL; |
208 | } | 210 | } |
209 | self->error = error; | 211 | self->error = error; |
210 | } | 212 | } |
@@ -225,7 +227,7 @@ static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, | |||
225 | * frees all memory associated with a given mscabd_cabinet. | 227 | * frees all memory associated with a given mscabd_cabinet. |
226 | */ | 228 | */ |
227 | static void cabd_close(struct mscab_decompressor *base, | 229 | static void cabd_close(struct mscab_decompressor *base, |
228 | struct mscabd_cabinet *origcab) | 230 | struct mscabd_cabinet *origcab) |
229 | { | 231 | { |
230 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | 232 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
231 | struct mscabd_folder_data *dat, *ndat; | 233 | struct mscabd_folder_data *dat, *ndat; |
@@ -253,16 +255,16 @@ static void cabd_close(struct mscab_decompressor *base, | |||
253 | 255 | ||
254 | /* free folder decompression state if it has been decompressed */ | 256 | /* free folder decompression state if it has been decompressed */ |
255 | if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) { | 257 | if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) { |
256 | if (self->d->infh) sys->close(self->d->infh); | 258 | if (self->d->infh) sys->close(self->d->infh); |
257 | cabd_free_decomp(self); | 259 | cabd_free_decomp(self); |
258 | sys->free(self->d); | 260 | sys->free(self->d); |
259 | self->d = NULL; | 261 | self->d = NULL; |
260 | } | 262 | } |
261 | 263 | ||
262 | /* free folder data segments */ | 264 | /* free folder data segments */ |
263 | for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { | 265 | for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { |
264 | ndat = dat->next; | 266 | ndat = dat->next; |
265 | sys->free(dat); | 267 | sys->free(dat); |
266 | } | 268 | } |
267 | sys->free(fol); | 269 | sys->free(fol); |
268 | } | 270 | } |
@@ -304,11 +306,11 @@ static void cabd_close(struct mscab_decompressor *base, | |||
304 | * for folders and files as necessary | 306 | * for folders and files as necessary |
305 | */ | 307 | */ |
306 | static int cabd_read_headers(struct mspack_system *sys, | 308 | static int cabd_read_headers(struct mspack_system *sys, |
307 | struct mspack_file *fh, | 309 | struct mspack_file *fh, |
308 | struct mscabd_cabinet_p *cab, | 310 | struct mscabd_cabinet_p *cab, |
309 | off_t offset, int quiet) | 311 | off_t offset, int salvage, int quiet) |
310 | { | 312 | { |
311 | int num_folders, num_files, folder_resv, i, x; | 313 | int num_folders, num_files, folder_resv, i, x, err, fidx; |
312 | struct mscabd_folder_p *fol, *linkfol = NULL; | 314 | struct mscabd_folder_p *fol, *linkfol = NULL; |
313 | struct mscabd_file *file, *linkfile = NULL; | 315 | struct mscabd_file *file, *linkfile = NULL; |
314 | unsigned char buf[64]; | 316 | unsigned char buf[64]; |
@@ -364,6 +366,7 @@ static int cabd_read_headers(struct mspack_system *sys, | |||
364 | 366 | ||
365 | /* read the reserved-sizes part of header, if present */ | 367 | /* read the reserved-sizes part of header, if present */ |
366 | cab->base.flags = EndGetI16(&buf[cfhead_Flags]); | 368 | cab->base.flags = EndGetI16(&buf[cfhead_Flags]); |
369 | |||
367 | if (cab->base.flags & cfheadRESERVE_PRESENT) { | 370 | if (cab->base.flags & cfheadRESERVE_PRESENT) { |
368 | if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { | 371 | if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { |
369 | return MSPACK_ERR_READ; | 372 | return MSPACK_ERR_READ; |
@@ -379,7 +382,7 @@ static int cabd_read_headers(struct mspack_system *sys, | |||
379 | /* skip the reserved header */ | 382 | /* skip the reserved header */ |
380 | if (cab->base.header_resv) { | 383 | if (cab->base.header_resv) { |
381 | if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) { | 384 | if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) { |
382 | return MSPACK_ERR_SEEK; | 385 | return MSPACK_ERR_SEEK; |
383 | } | 386 | } |
384 | } | 387 | } |
385 | } | 388 | } |
@@ -391,14 +394,18 @@ static int cabd_read_headers(struct mspack_system *sys, | |||
391 | 394 | ||
392 | /* read name and info of preceeding cabinet in set, if present */ | 395 | /* read name and info of preceeding cabinet in set, if present */ |
393 | if (cab->base.flags & cfheadPREV_CABINET) { | 396 | if (cab->base.flags & cfheadPREV_CABINET) { |
394 | cab->base.prevname = cabd_read_string(sys, fh, cab, &x); if (x) return x; | 397 | cab->base.prevname = cabd_read_string(sys, fh, &err); |
395 | cab->base.previnfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; | 398 | if (err) return err; |
399 | cab->base.previnfo = cabd_read_string(sys, fh, &err); | ||
400 | if (err) return err; | ||
396 | } | 401 | } |
397 | 402 | ||
398 | /* read name and info of next cabinet in set, if present */ | 403 | /* read name and info of next cabinet in set, if present */ |
399 | if (cab->base.flags & cfheadNEXT_CABINET) { | 404 | if (cab->base.flags & cfheadNEXT_CABINET) { |
400 | cab->base.nextname = cabd_read_string(sys, fh, cab, &x); if (x) return x; | 405 | cab->base.nextname = cabd_read_string(sys, fh, &err); |
401 | cab->base.nextinfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; | 406 | if (err) return err; |
407 | cab->base.nextinfo = cabd_read_string(sys, fh, &err); | ||
408 | if (err) return err; | ||
402 | } | 409 | } |
403 | 410 | ||
404 | /* read folders */ | 411 | /* read folders */ |
@@ -408,7 +415,7 @@ static int cabd_read_headers(struct mspack_system *sys, | |||
408 | } | 415 | } |
409 | if (folder_resv) { | 416 | if (folder_resv) { |
410 | if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { | 417 | if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { |
411 | return MSPACK_ERR_SEEK; | 418 | return MSPACK_ERR_SEEK; |
412 | } | 419 | } |
413 | } | 420 | } |
414 | 421 | ||
@@ -447,45 +454,44 @@ static int cabd_read_headers(struct mspack_system *sys, | |||
447 | file->offset = EndGetI32(&buf[cffile_FolderOffset]); | 454 | file->offset = EndGetI32(&buf[cffile_FolderOffset]); |
448 | 455 | ||
449 | /* set folder pointer */ | 456 | /* set folder pointer */ |
450 | x = EndGetI16(&buf[cffile_FolderIndex]); | 457 | fidx = EndGetI16(&buf[cffile_FolderIndex]); |
451 | if (x < cffileCONTINUED_FROM_PREV) { | 458 | if (fidx < cffileCONTINUED_FROM_PREV) { |
452 | /* normal folder index; count up to the correct folder. the folder | 459 | /* normal folder index; count up to the correct folder */ |
453 | * pointer will be NULL if folder index is invalid */ | 460 | if (fidx < num_folders) { |
454 | struct mscabd_folder *ifol = cab->base.folders; | 461 | struct mscabd_folder *ifol = cab->base.folders; |
455 | while (x--) if (ifol) ifol = ifol->next; | 462 | while (fidx--) if (ifol) ifol = ifol->next; |
456 | file->folder = ifol; | 463 | file->folder = ifol; |
457 | 464 | } | |
458 | if (!ifol) { | 465 | else { |
459 | sys->free(file); | 466 | D(("invalid folder index")) |
460 | D(("invalid folder index")) | 467 | file->folder = NULL; |
461 | return MSPACK_ERR_DATAFORMAT; | ||
462 | } | 468 | } |
463 | } | 469 | } |
464 | else { | 470 | else { |
465 | /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or | 471 | /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or |
466 | * CONTINUED_PREV_AND_NEXT */ | 472 | * CONTINUED_PREV_AND_NEXT */ |
467 | if ((x == cffileCONTINUED_TO_NEXT) || | 473 | if ((fidx == cffileCONTINUED_TO_NEXT) || |
468 | (x == cffileCONTINUED_PREV_AND_NEXT)) | 474 | (fidx == cffileCONTINUED_PREV_AND_NEXT)) |
469 | { | 475 | { |
470 | /* get last folder */ | 476 | /* get last folder */ |
471 | struct mscabd_folder *ifol = cab->base.folders; | 477 | struct mscabd_folder *ifol = cab->base.folders; |
472 | while (ifol->next) ifol = ifol->next; | 478 | while (ifol->next) ifol = ifol->next; |
473 | file->folder = ifol; | 479 | file->folder = ifol; |
474 | 480 | ||
475 | /* set "merge next" pointer */ | 481 | /* set "merge next" pointer */ |
476 | fol = (struct mscabd_folder_p *) ifol; | 482 | fol = (struct mscabd_folder_p *) ifol; |
477 | if (!fol->merge_next) fol->merge_next = file; | 483 | if (!fol->merge_next) fol->merge_next = file; |
478 | } | 484 | } |
479 | 485 | ||
480 | if ((x == cffileCONTINUED_FROM_PREV) || | 486 | if ((fidx == cffileCONTINUED_FROM_PREV) || |
481 | (x == cffileCONTINUED_PREV_AND_NEXT)) | 487 | (fidx == cffileCONTINUED_PREV_AND_NEXT)) |
482 | { | 488 | { |
483 | /* get first folder */ | 489 | /* get first folder */ |
484 | file->folder = cab->base.folders; | 490 | file->folder = cab->base.folders; |
485 | 491 | ||
486 | /* set "merge prev" pointer */ | 492 | /* set "merge prev" pointer */ |
487 | fol = (struct mscabd_folder_p *) file->folder; | 493 | fol = (struct mscabd_folder_p *) file->folder; |
488 | if (!fol->merge_prev) fol->merge_prev = file; | 494 | if (!fol->merge_prev) fol->merge_prev = file; |
489 | } | 495 | } |
490 | } | 496 | } |
491 | 497 | ||
@@ -502,10 +508,14 @@ static int cabd_read_headers(struct mspack_system *sys, | |||
502 | file->date_y = (x >> 9) + 1980; | 508 | file->date_y = (x >> 9) + 1980; |
503 | 509 | ||
504 | /* get filename */ | 510 | /* get filename */ |
505 | file->filename = cabd_read_string(sys, fh, cab, &x); | 511 | file->filename = cabd_read_string(sys, fh, &err); |
506 | if (x) { | 512 | |
513 | /* if folder index or filename are bad, either skip it or fail */ | ||
514 | if (err || !file->folder) { | ||
515 | sys->free(file->filename); | ||
507 | sys->free(file); | 516 | sys->free(file); |
508 | return x; | 517 | if (salvage) continue; |
518 | return err ? err : MSPACK_ERR_DATAFORMAT; | ||
509 | } | 519 | } |
510 | 520 | ||
511 | /* link file entry into file list */ | 521 | /* link file entry into file list */ |
@@ -514,23 +524,34 @@ static int cabd_read_headers(struct mspack_system *sys, | |||
514 | linkfile = file; | 524 | linkfile = file; |
515 | } | 525 | } |
516 | 526 | ||
527 | if (cab->base.files == NULL) { | ||
528 | /* We never actually added any files to the file list. Something went wrong. | ||
529 | * The file header may have been invalid */ | ||
530 | D(("No files found, even though header claimed to have %d files", num_files)) | ||
531 | return MSPACK_ERR_DATAFORMAT; | ||
532 | } | ||
533 | |||
517 | return MSPACK_ERR_OK; | 534 | return MSPACK_ERR_OK; |
518 | } | 535 | } |
519 | 536 | ||
520 | static char *cabd_read_string(struct mspack_system *sys, | 537 | static char *cabd_read_string(struct mspack_system *sys, |
521 | struct mspack_file *fh, | 538 | struct mspack_file *fh, int *error) |
522 | struct mscabd_cabinet_p *cab, int *error) | ||
523 | { | 539 | { |
524 | off_t base = sys->tell(fh); | 540 | off_t base = sys->tell(fh); |
525 | char buf[256], *str; | 541 | char buf[256], *str; |
526 | unsigned int len, i, ok; | 542 | int len, i, ok; |
527 | (void)cab; | ||
528 | 543 | ||
529 | /* read up to 256 bytes */ | 544 | /* read up to 256 bytes */ |
530 | len = sys->read(fh, &buf[0], 256); | 545 | if ((len = sys->read(fh, &buf[0], 256)) <= 0) { |
546 | *error = MSPACK_ERR_READ; | ||
547 | return NULL; | ||
548 | } | ||
531 | 549 | ||
532 | /* search for a null terminator in the buffer */ | 550 | /* search for a null terminator in the buffer */ |
533 | for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; } | 551 | for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; } |
552 | /* reject empty strings */ | ||
553 | if (i == 0) ok = 0; | ||
554 | |||
534 | if (!ok) { | 555 | if (!ok) { |
535 | *error = MSPACK_ERR_DATAFORMAT; | 556 | *error = MSPACK_ERR_DATAFORMAT; |
536 | return NULL; | 557 | return NULL; |
@@ -566,7 +587,7 @@ static char *cabd_read_string(struct mspack_system *sys, | |||
566 | * break out of the loop and be sure that all resources are freed | 587 | * break out of the loop and be sure that all resources are freed |
567 | */ | 588 | */ |
568 | static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, | 589 | static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, |
569 | const char *filename) | 590 | const char *filename) |
570 | { | 591 | { |
571 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | 592 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
572 | struct mscabd_cabinet_p *cab = NULL; | 593 | struct mscabd_cabinet_p *cab = NULL; |
@@ -579,7 +600,7 @@ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, | |||
579 | sys = self->system; | 600 | sys = self->system; |
580 | 601 | ||
581 | /* allocate a search buffer */ | 602 | /* allocate a search buffer */ |
582 | search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->param[MSCABD_PARAM_SEARCHBUF]); | 603 | search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->searchbuf_size); |
583 | if (!search_buf) { | 604 | if (!search_buf) { |
584 | self->error = MSPACK_ERR_NOMEMORY; | 605 | self->error = MSPACK_ERR_NOMEMORY; |
585 | return NULL; | 606 | return NULL; |
@@ -589,21 +610,21 @@ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, | |||
589 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { | 610 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { |
590 | if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) { | 611 | if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) { |
591 | self->error = cabd_find(self, search_buf, fh, filename, | 612 | self->error = cabd_find(self, search_buf, fh, filename, |
592 | filelen, &firstlen, &cab); | 613 | filelen, &firstlen, &cab); |
593 | } | 614 | } |
594 | 615 | ||
595 | /* truncated / extraneous data warning: */ | 616 | /* truncated / extraneous data warning: */ |
596 | if (firstlen && (firstlen != filelen) && | 617 | if (firstlen && (firstlen != filelen) && |
597 | (!cab || (cab->base.base_offset == 0))) | 618 | (!cab || (cab->base.base_offset == 0))) |
598 | { | 619 | { |
599 | if (firstlen < filelen) { | 620 | if (firstlen < filelen) { |
600 | sys->message(fh, "WARNING; possible %" LD | 621 | sys->message(fh, "WARNING; possible %" LD |
601 | " extra bytes at end of file.", | 622 | " extra bytes at end of file.", |
602 | filelen - firstlen); | 623 | filelen - firstlen); |
603 | } | 624 | } |
604 | else { | 625 | else { |
605 | sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.", | 626 | sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.", |
606 | firstlen - filelen); | 627 | firstlen - filelen); |
607 | } | 628 | } |
608 | } | 629 | } |
609 | 630 | ||
@@ -620,8 +641,8 @@ static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, | |||
620 | } | 641 | } |
621 | 642 | ||
622 | static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, | 643 | static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, |
623 | struct mspack_file *fh, const char *filename, off_t flen, | 644 | struct mspack_file *fh, const char *filename, off_t flen, |
624 | off_t *firstlen, struct mscabd_cabinet_p **firstcab) | 645 | off_t *firstlen, struct mscabd_cabinet_p **firstcab) |
625 | { | 646 | { |
626 | struct mscabd_cabinet_p *cab, *link = NULL; | 647 | struct mscabd_cabinet_p *cab, *link = NULL; |
627 | off_t caboff, offset, length; | 648 | off_t caboff, offset, length; |
@@ -630,7 +651,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, | |||
630 | unsigned int cablen_u32 = 0, foffset_u32 = 0; | 651 | unsigned int cablen_u32 = 0, foffset_u32 = 0; |
631 | int false_cabs = 0; | 652 | int false_cabs = 0; |
632 | 653 | ||
633 | #ifndef LARGEFILE_SUPPORT | 654 | #if !LARGEFILE_SUPPORT |
634 | /* detect 32-bit off_t overflow */ | 655 | /* detect 32-bit off_t overflow */ |
635 | if (flen < 0) { | 656 | if (flen < 0) { |
636 | sys->message(fh, largefile_msg); | 657 | sys->message(fh, largefile_msg); |
@@ -643,8 +664,8 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, | |||
643 | /* search length is either the full length of the search buffer, or the | 664 | /* search length is either the full length of the search buffer, or the |
644 | * amount of data remaining to the end of the file, whichever is less. */ | 665 | * amount of data remaining to the end of the file, whichever is less. */ |
645 | length = flen - offset; | 666 | length = flen - offset; |
646 | if (length > self->param[MSCABD_PARAM_SEARCHBUF]) { | 667 | if (length > self->searchbuf_size) { |
647 | length = self->param[MSCABD_PARAM_SEARCHBUF]; | 668 | length = self->searchbuf_size; |
648 | } | 669 | } |
649 | 670 | ||
650 | /* fill the search buffer with data from disk */ | 671 | /* fill the search buffer with data from disk */ |
@@ -654,22 +675,21 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, | |||
654 | 675 | ||
655 | /* FAQ avoidance strategy */ | 676 | /* FAQ avoidance strategy */ |
656 | if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { | 677 | if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { |
657 | sys->message(fh, "WARNING; found InstallShield header. " | 678 | sys->message(fh, "WARNING; found InstallShield header. Use unshield " |
658 | "This is probably an InstallShield file. " | 679 | "(https://github.com/twogood/unshield) to unpack this file"); |
659 | "Use UNSHIELD from www.synce.org to unpack it."); | ||
660 | } | 680 | } |
661 | 681 | ||
662 | /* read through the entire buffer. */ | 682 | /* read through the entire buffer. */ |
663 | for (p = &buf[0], pend = &buf[length]; p < pend; ) { | 683 | for (p = &buf[0], pend = &buf[length]; p < pend; ) { |
664 | switch (state) { | 684 | switch (state) { |
665 | /* starting state */ | 685 | /* starting state */ |
666 | case 0: | 686 | case 0: |
667 | /* we spend most of our time in this while loop, looking for | 687 | /* we spend most of our time in this while loop, looking for |
668 | * a leading 'M' of the 'MSCF' signature */ | 688 | * a leading 'M' of the 'MSCF' signature */ |
669 | while (p < pend && *p != 0x4D) p++; | 689 | while (p < pend && *p != 0x4D) p++; |
670 | /* if we found tht 'M', advance state */ | 690 | /* if we found tht 'M', advance state */ |
671 | if (p++ < pend) state = 1; | 691 | if (p++ < pend) state = 1; |
672 | break; | 692 | break; |
673 | 693 | ||
674 | /* verify that the next 3 bytes are 'S', 'C' and 'F' */ | 694 | /* verify that the next 3 bytes are 'S', 'C' and 'F' */ |
675 | case 1: state = (*p++ == 0x53) ? 2 : 0; break; | 695 | case 1: state = (*p++ == 0x53) ? 2 : 0; break; |
@@ -691,70 +711,71 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, | |||
691 | case 17: foffset_u32 |= *p++ << 8; state++; break; | 711 | case 17: foffset_u32 |= *p++ << 8; state++; break; |
692 | case 18: foffset_u32 |= *p++ << 16; state++; break; | 712 | case 18: foffset_u32 |= *p++ << 16; state++; break; |
693 | case 19: foffset_u32 |= *p++ << 24; | 713 | case 19: foffset_u32 |= *p++ << 24; |
694 | /* now we have recieved 20 bytes of potential cab header. work out | 714 | /* now we have recieved 20 bytes of potential cab header. work out |
695 | * the offset in the file of this potential cabinet */ | 715 | * the offset in the file of this potential cabinet */ |
696 | caboff = offset + (p - &buf[0]) - 20; | 716 | caboff = offset + (p - &buf[0]) - 20; |
697 | 717 | ||
698 | /* should reading cabinet fail, restart search just after 'MSCF' */ | 718 | /* should reading cabinet fail, restart search just after 'MSCF' */ |
699 | offset = caboff + 4; | 719 | offset = caboff + 4; |
700 | 720 | ||
701 | /* capture the "length of cabinet" field if there is a cabinet at | 721 | /* capture the "length of cabinet" field if there is a cabinet at |
702 | * offset 0 in the file, regardless of whether the cabinet can be | 722 | * offset 0 in the file, regardless of whether the cabinet can be |
703 | * read correctly or not */ | 723 | * read correctly or not */ |
704 | if (caboff == 0) *firstlen = (off_t) cablen_u32; | 724 | if (caboff == 0) *firstlen = (off_t) cablen_u32; |
705 | 725 | ||
706 | /* check that the files offset is less than the alleged length of | 726 | /* check that the files offset is less than the alleged length of |
707 | * the cabinet, and that the offset + the alleged length are | 727 | * the cabinet, and that the offset + the alleged length are |
708 | * 'roughly' within the end of overall file length */ | 728 | * 'roughly' within the end of overall file length. In salvage |
709 | if ((foffset_u32 < cablen_u32) && | 729 | * mode, don't check the alleged length, allow it to be garbage */ |
710 | ((caboff + (off_t) foffset_u32) < (flen + 32)) && | 730 | if ((foffset_u32 < cablen_u32) && |
711 | ((caboff + (off_t) cablen_u32) < (flen + 32)) ) | 731 | ((caboff + (off_t) foffset_u32) < (flen + 32)) && |
712 | { | 732 | (((caboff + (off_t) cablen_u32) < (flen + 32)) || self->salvage)) |
713 | /* likely cabinet found -- try reading it */ | 733 | { |
714 | if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { | 734 | /* likely cabinet found -- try reading it */ |
715 | return MSPACK_ERR_NOMEMORY; | 735 | if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { |
716 | } | 736 | return MSPACK_ERR_NOMEMORY; |
717 | cab->base.filename = filename; | 737 | } |
718 | if (cabd_read_headers(sys, fh, cab, caboff, 1)) { | 738 | cab->base.filename = filename; |
719 | /* destroy the failed cabinet */ | 739 | if (cabd_read_headers(sys, fh, cab, caboff, self->salvage, 1)) { |
720 | cabd_close((struct mscab_decompressor *) self, | 740 | /* destroy the failed cabinet */ |
721 | (struct mscabd_cabinet *) cab); | 741 | cabd_close((struct mscab_decompressor *) self, |
722 | false_cabs++; | 742 | (struct mscabd_cabinet *) cab); |
723 | } | 743 | false_cabs++; |
724 | else { | 744 | } |
725 | /* cabinet read correctly! */ | 745 | else { |
726 | 746 | /* cabinet read correctly! */ | |
727 | /* link the cab into the list */ | 747 | |
728 | if (!link) *firstcab = cab; | 748 | /* link the cab into the list */ |
729 | else link->base.next = (struct mscabd_cabinet *) cab; | 749 | if (!link) *firstcab = cab; |
730 | link = cab; | 750 | else link->base.next = (struct mscabd_cabinet *) cab; |
731 | 751 | link = cab; | |
732 | /* cause the search to restart after this cab's data. */ | 752 | |
733 | offset = caboff + (off_t) cablen_u32; | 753 | /* cause the search to restart after this cab's data. */ |
734 | 754 | offset = caboff + (off_t) cablen_u32; | |
735 | #ifndef LARGEFILE_SUPPORT | 755 | |
736 | /* detect 32-bit off_t overflow */ | 756 | #if !LARGEFILE_SUPPORT |
737 | if (offset < caboff) { | 757 | /* detect 32-bit off_t overflow */ |
738 | sys->message(fh, largefile_msg); | 758 | if (offset < caboff) { |
739 | return MSPACK_ERR_OK; | 759 | sys->message(fh, largefile_msg); |
740 | } | 760 | return MSPACK_ERR_OK; |
741 | #endif | 761 | } |
742 | } | 762 | #endif |
743 | } | 763 | } |
744 | 764 | } | |
745 | /* restart search */ | 765 | |
746 | if (offset >= flen) return MSPACK_ERR_OK; | 766 | /* restart search */ |
747 | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { | 767 | if (offset >= flen) return MSPACK_ERR_OK; |
748 | return MSPACK_ERR_SEEK; | 768 | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { |
749 | } | 769 | return MSPACK_ERR_SEEK; |
750 | length = 0; | 770 | } |
751 | p = pend; | 771 | length = 0; |
752 | state = 0; | 772 | p = pend; |
753 | break; | 773 | state = 0; |
774 | break; | ||
754 | 775 | ||
755 | /* for bytes 4-7 and 12-15, just advance state/pointer */ | 776 | /* for bytes 4-7 and 12-15, just advance state/pointer */ |
756 | default: | 777 | default: |
757 | p++, state++; | 778 | p++, state++; |
758 | } /* switch(state) */ | 779 | } /* switch(state) */ |
759 | } /* for (... p < pend ...) */ | 780 | } /* for (... p < pend ...) */ |
760 | } /* for (... offset < length ...) */ | 781 | } /* for (... offset < length ...) */ |
@@ -765,7 +786,7 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, | |||
765 | 786 | ||
766 | return MSPACK_ERR_OK; | 787 | return MSPACK_ERR_OK; |
767 | } | 788 | } |
768 | 789 | ||
769 | /*************************************** | 790 | /*************************************** |
770 | * CABD_MERGE, CABD_PREPEND, CABD_APPEND | 791 | * CABD_MERGE, CABD_PREPEND, CABD_APPEND |
771 | *************************************** | 792 | *************************************** |
@@ -775,22 +796,22 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, | |||
775 | * merged folder's data parts list. | 796 | * merged folder's data parts list. |
776 | */ | 797 | */ |
777 | static int cabd_prepend(struct mscab_decompressor *base, | 798 | static int cabd_prepend(struct mscab_decompressor *base, |
778 | struct mscabd_cabinet *cab, | 799 | struct mscabd_cabinet *cab, |
779 | struct mscabd_cabinet *prevcab) | 800 | struct mscabd_cabinet *prevcab) |
780 | { | 801 | { |
781 | return cabd_merge(base, prevcab, cab); | 802 | return cabd_merge(base, prevcab, cab); |
782 | } | 803 | } |
783 | 804 | ||
784 | static int cabd_append(struct mscab_decompressor *base, | 805 | static int cabd_append(struct mscab_decompressor *base, |
785 | struct mscabd_cabinet *cab, | 806 | struct mscabd_cabinet *cab, |
786 | struct mscabd_cabinet *nextcab) | 807 | struct mscabd_cabinet *nextcab) |
787 | { | 808 | { |
788 | return cabd_merge(base, cab, nextcab); | 809 | return cabd_merge(base, cab, nextcab); |
789 | } | 810 | } |
790 | 811 | ||
791 | static int cabd_merge(struct mscab_decompressor *base, | 812 | static int cabd_merge(struct mscab_decompressor *base, |
792 | struct mscabd_cabinet *lcab, | 813 | struct mscabd_cabinet *lcab, |
793 | struct mscabd_cabinet *rcab) | 814 | struct mscabd_cabinet *rcab) |
794 | { | 815 | { |
795 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | 816 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
796 | struct mscabd_folder_data *data, *ndata; | 817 | struct mscabd_folder_data *data, *ndata; |
@@ -880,7 +901,7 @@ static int cabd_merge(struct mscab_decompressor *base, | |||
880 | * instead */ | 901 | * instead */ |
881 | lfol->base.num_blocks += rfol->base.num_blocks - 1; | 902 | lfol->base.num_blocks += rfol->base.num_blocks - 1; |
882 | if ((rfol->merge_next == NULL) || | 903 | if ((rfol->merge_next == NULL) || |
883 | (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) | 904 | (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) |
884 | { | 905 | { |
885 | lfol->merge_next = rfol->merge_next; | 906 | lfol->merge_next = rfol->merge_next; |
886 | } | 907 | } |
@@ -903,9 +924,9 @@ static int cabd_merge(struct mscab_decompressor *base, | |||
903 | rfi = fi->next; | 924 | rfi = fi->next; |
904 | /* if file's folder matches the merge folder, unlink and free it */ | 925 | /* if file's folder matches the merge folder, unlink and free it */ |
905 | if (fi->folder == (struct mscabd_folder *) rfol) { | 926 | if (fi->folder == (struct mscabd_folder *) rfol) { |
906 | if (lfi) lfi->next = rfi; else lcab->files = rfi; | 927 | if (lfi) lfi->next = rfi; else lcab->files = rfi; |
907 | sys->free(fi->filename); | 928 | sys->free(fi->filename); |
908 | sys->free(fi); | 929 | sys->free(fi); |
909 | } | 930 | } |
910 | else lfi = fi; | 931 | else lfi = fi; |
911 | } | 932 | } |
@@ -940,6 +961,12 @@ static int cabd_can_merge_folders(struct mspack_system *sys, | |||
940 | return 0; | 961 | return 0; |
941 | } | 962 | } |
942 | 963 | ||
964 | /* check there are not too many data blocks after merging */ | ||
965 | if ((lfol->base.num_blocks + rfol->base.num_blocks) > CAB_FOLDERMAX) { | ||
966 | D(("folder merge: too many data blocks in merged folders")) | ||
967 | return 0; | ||
968 | } | ||
969 | |||
943 | if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) { | 970 | if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) { |
944 | D(("folder merge: one cabinet has no files to merge")) | 971 | D(("folder merge: one cabinet has no files to merge")) |
945 | return 0; | 972 | return 0; |
@@ -950,10 +977,10 @@ static int cabd_can_merge_folders(struct mspack_system *sys, | |||
950 | * should be identical in number and order. to verify this, check the | 977 | * should be identical in number and order. to verify this, check the |
951 | * offset and length of each file. */ | 978 | * offset and length of each file. */ |
952 | for (l=lfi, r=rfi; l; l=l->next, r=r->next) { | 979 | for (l=lfi, r=rfi; l; l=l->next, r=r->next) { |
953 | if (!r || (l->offset != r->offset) || (l->length != r->length)) { | 980 | if (!r || (l->offset != r->offset) || (l->length != r->length)) { |
954 | matching = 0; | 981 | matching = 0; |
955 | break; | 982 | break; |
956 | } | 983 | } |
957 | } | 984 | } |
958 | 985 | ||
959 | if (matching) return 1; | 986 | if (matching) return 1; |
@@ -963,9 +990,9 @@ static int cabd_can_merge_folders(struct mspack_system *sys, | |||
963 | * the merge with a warning about missing files. */ | 990 | * the merge with a warning about missing files. */ |
964 | matching = 0; | 991 | matching = 0; |
965 | for (l = lfi; l; l = l->next) { | 992 | for (l = lfi; l; l = l->next) { |
966 | for (r = rfi; r; r = r->next) { | 993 | for (r = rfi; r; r = r->next) { |
967 | if (l->offset == r->offset && l->length == r->length) break; | 994 | if (l->offset == r->offset && l->length == r->length) break; |
968 | } | 995 | } |
969 | if (r) matching = 1; else sys->message(NULL, | 996 | if (r) matching = 1; else sys->message(NULL, |
970 | "WARNING; merged file %s not listed in both cabinets", l->filename); | 997 | "WARNING; merged file %s not listed in both cabinets", l->filename); |
971 | } | 998 | } |
@@ -985,6 +1012,7 @@ static int cabd_extract(struct mscab_decompressor *base, | |||
985 | struct mscabd_folder_p *fol; | 1012 | struct mscabd_folder_p *fol; |
986 | struct mspack_system *sys; | 1013 | struct mspack_system *sys; |
987 | struct mspack_file *fh; | 1014 | struct mspack_file *fh; |
1015 | off_t filelen; | ||
988 | 1016 | ||
989 | if (!self) return MSPACK_ERR_ARGS; | 1017 | if (!self) return MSPACK_ERR_ARGS; |
990 | if (!file) return self->error = MSPACK_ERR_ARGS; | 1018 | if (!file) return self->error = MSPACK_ERR_ARGS; |
@@ -992,15 +1020,43 @@ static int cabd_extract(struct mscab_decompressor *base, | |||
992 | sys = self->system; | 1020 | sys = self->system; |
993 | fol = (struct mscabd_folder_p *) file->folder; | 1021 | fol = (struct mscabd_folder_p *) file->folder; |
994 | 1022 | ||
995 | /* check if file can be extracted */ | 1023 | /* if offset is beyond 2GB, nothing can be extracted */ |
996 | if ((!fol) || (fol->merge_prev) || | 1024 | if (file->offset > CAB_LENGTHMAX) { |
997 | (((file->offset + file->length) / CAB_BLOCKMAX) > fol->base.num_blocks)) | ||
998 | { | ||
999 | sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " | ||
1000 | "cabinet set is incomplete.", file->filename); | ||
1001 | return self->error = MSPACK_ERR_DATAFORMAT; | 1025 | return self->error = MSPACK_ERR_DATAFORMAT; |
1002 | } | 1026 | } |
1003 | 1027 | ||
1028 | /* if file claims to go beyond 2GB either error out, | ||
1029 | * or in salvage mode reduce file length so it fits 2GB limit | ||
1030 | */ | ||
1031 | filelen = file->length; | ||
1032 | if (filelen > CAB_LENGTHMAX || (file->offset + filelen) > CAB_LENGTHMAX) { | ||
1033 | if (self->salvage) { | ||
1034 | filelen = CAB_LENGTHMAX - file->offset; | ||
1035 | } | ||
1036 | else { | ||
1037 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1038 | } | ||
1039 | } | ||
1040 | |||
1041 | /* extraction impossible if no folder, or folder needs predecessor */ | ||
1042 | if (!fol || fol->merge_prev) { | ||
1043 | sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " | ||
1044 | "cabinet set is incomplete", file->filename); | ||
1045 | return self->error = MSPACK_ERR_DECRUNCH; | ||
1046 | } | ||
1047 | |||
1048 | /* if file goes beyond what can be decoded, given an error. | ||
1049 | * In salvage mode, don't assume block sizes, just try decoding | ||
1050 | */ | ||
1051 | if (!self->salvage) { | ||
1052 | off_t maxlen = fol->base.num_blocks * CAB_BLOCKMAX; | ||
1053 | if ((file->offset + filelen) > maxlen) { | ||
1054 | sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " | ||
1055 | "cabinet set is incomplete", file->filename); | ||
1056 | return self->error = MSPACK_ERR_DECRUNCH; | ||
1057 | } | ||
1058 | } | ||
1059 | |||
1004 | /* allocate generic decompression state */ | 1060 | /* allocate generic decompression state */ |
1005 | if (!self->d) { | 1061 | if (!self->d) { |
1006 | self->d = (struct mscabd_decompress_state *) sys->alloc(sys, sizeof(struct mscabd_decompress_state)); | 1062 | 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, | |||
1016 | } | 1072 | } |
1017 | 1073 | ||
1018 | /* do we need to change folder or reset the current folder? */ | 1074 | /* do we need to change folder or reset the current folder? */ |
1019 | if ((self->d->folder != fol) || (self->d->offset > file->offset)) { | 1075 | if ((self->d->folder != fol) || (self->d->offset > file->offset) || |
1076 | !self->d->state) | ||
1077 | { | ||
1078 | /* free any existing decompressor */ | ||
1079 | cabd_free_decomp(self); | ||
1080 | |||
1020 | /* do we need to open a new cab file? */ | 1081 | /* do we need to open a new cab file? */ |
1021 | if (!self->d->infh || (fol->data.cab != self->d->incab)) { | 1082 | if (!self->d->infh || (fol->data.cab != self->d->incab)) { |
1022 | /* close previous file handle if from a different cab */ | 1083 | /* close previous file handle if from a different cab */ |
1023 | if (self->d->infh) sys->close(self->d->infh); | 1084 | if (self->d->infh) sys->close(self->d->infh); |
1024 | self->d->incab = fol->data.cab; | 1085 | self->d->incab = fol->data.cab; |
1025 | self->d->infh = sys->open(sys, fol->data.cab->base.filename, | 1086 | self->d->infh = sys->open(sys, fol->data.cab->base.filename, |
1026 | MSPACK_SYS_OPEN_READ); | 1087 | MSPACK_SYS_OPEN_READ); |
1027 | if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; | 1088 | if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; |
1028 | } | 1089 | } |
1029 | /* seek to start of data blocks */ | 1090 | /* seek to start of data blocks */ |
@@ -1041,6 +1102,7 @@ static int cabd_extract(struct mscab_decompressor *base, | |||
1041 | self->d->data = &fol->data; | 1102 | self->d->data = &fol->data; |
1042 | self->d->offset = 0; | 1103 | self->d->offset = 0; |
1043 | self->d->block = 0; | 1104 | self->d->block = 0; |
1105 | self->d->outlen = 0; | ||
1044 | self->d->i_ptr = self->d->i_end = &self->d->input[0]; | 1106 | self->d->i_ptr = self->d->i_end = &self->d->input[0]; |
1045 | 1107 | ||
1046 | /* read_error lasts for the lifetime of a decompressor */ | 1108 | /* read_error lasts for the lifetime of a decompressor */ |
@@ -1055,7 +1117,7 @@ static int cabd_extract(struct mscab_decompressor *base, | |||
1055 | self->error = MSPACK_ERR_OK; | 1117 | self->error = MSPACK_ERR_OK; |
1056 | 1118 | ||
1057 | /* if file has more than 0 bytes */ | 1119 | /* if file has more than 0 bytes */ |
1058 | if (file->length) { | 1120 | if (filelen) { |
1059 | off_t bytes; | 1121 | off_t bytes; |
1060 | int error; | 1122 | int error; |
1061 | /* get to correct offset. | 1123 | /* get to correct offset. |
@@ -1065,14 +1127,14 @@ static int cabd_extract(struct mscab_decompressor *base, | |||
1065 | */ | 1127 | */ |
1066 | self->d->outfh = NULL; | 1128 | self->d->outfh = NULL; |
1067 | if ((bytes = file->offset - self->d->offset)) { | 1129 | if ((bytes = file->offset - self->d->offset)) { |
1068 | error = self->d->decompress(self->d->state, bytes); | 1130 | error = self->d->decompress(self->d->state, bytes); |
1069 | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; | 1131 | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; |
1070 | } | 1132 | } |
1071 | 1133 | ||
1072 | /* if getting to the correct offset was error free, unpack file */ | 1134 | /* if getting to the correct offset was error free, unpack file */ |
1073 | if (!self->error) { | 1135 | if (!self->error) { |
1074 | self->d->outfh = fh; | 1136 | self->d->outfh = fh; |
1075 | error = self->d->decompress(self->d->state, (off_t) file->length); | 1137 | error = self->d->decompress(self->d->state, filelen); |
1076 | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; | 1138 | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; |
1077 | } | 1139 | } |
1078 | } | 1140 | } |
@@ -1098,34 +1160,27 @@ static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct) | |||
1098 | { | 1160 | { |
1099 | struct mspack_file *fh = (struct mspack_file *) self; | 1161 | struct mspack_file *fh = (struct mspack_file *) self; |
1100 | 1162 | ||
1101 | assert(self && self->d); | ||
1102 | |||
1103 | /* free any existing decompressor */ | ||
1104 | cabd_free_decomp(self); | ||
1105 | |||
1106 | self->d->comp_type = ct; | 1163 | self->d->comp_type = ct; |
1107 | 1164 | ||
1108 | switch (ct & cffoldCOMPTYPE_MASK) { | 1165 | switch (ct & cffoldCOMPTYPE_MASK) { |
1109 | case cffoldCOMPTYPE_NONE: | 1166 | case cffoldCOMPTYPE_NONE: |
1110 | self->d->decompress = (int (*)(void *, off_t)) &noned_decompress; | 1167 | self->d->decompress = (int (*)(void *, off_t)) &noned_decompress; |
1111 | self->d->state = noned_init(&self->d->sys, fh, fh, | 1168 | self->d->state = noned_init(&self->d->sys, fh, fh, self->buf_size); |
1112 | self->param[MSCABD_PARAM_DECOMPBUF]); | ||
1113 | break; | 1169 | break; |
1114 | case cffoldCOMPTYPE_MSZIP: | 1170 | case cffoldCOMPTYPE_MSZIP: |
1115 | self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress; | 1171 | self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress; |
1116 | self->d->state = mszipd_init(&self->d->sys, fh, fh, | 1172 | self->d->state = mszipd_init(&self->d->sys, fh, fh, self->buf_size, |
1117 | self->param[MSCABD_PARAM_DECOMPBUF], | 1173 | self->fix_mszip); |
1118 | self->param[MSCABD_PARAM_FIXMSZIP]); | ||
1119 | break; | 1174 | break; |
1120 | case cffoldCOMPTYPE_QUANTUM: | 1175 | case cffoldCOMPTYPE_QUANTUM: |
1121 | self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress; | 1176 | self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress; |
1122 | self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, | 1177 | self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, |
1123 | self->param[MSCABD_PARAM_DECOMPBUF]); | 1178 | self->buf_size); |
1124 | break; | 1179 | break; |
1125 | case cffoldCOMPTYPE_LZX: | 1180 | case cffoldCOMPTYPE_LZX: |
1126 | self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress; | 1181 | self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress; |
1127 | self->d->state = lzxd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0, | 1182 | self->d->state = lzxd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0, |
1128 | self->param[MSCABD_PARAM_DECOMPBUF], (off_t) 0); | 1183 | self->buf_size, (off_t)0,0); |
1129 | break; | 1184 | break; |
1130 | default: | 1185 | default: |
1131 | return self->error = MSPACK_ERR_DATAFORMAT; | 1186 | return self->error = MSPACK_ERR_DATAFORMAT; |
@@ -1134,7 +1189,7 @@ static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct) | |||
1134 | } | 1189 | } |
1135 | 1190 | ||
1136 | static void cabd_free_decomp(struct mscab_decompressor_p *self) { | 1191 | static void cabd_free_decomp(struct mscab_decompressor_p *self) { |
1137 | if (!self || !self->d || !self->d->folder || !self->d->state) return; | 1192 | if (!self || !self->d || !self->d->state) return; |
1138 | 1193 | ||
1139 | switch (self->d->comp_type & cffoldCOMPTYPE_MASK) { | 1194 | switch (self->d->comp_type & cffoldCOMPTYPE_MASK) { |
1140 | case cffoldCOMPTYPE_NONE: noned_free((struct noned_state *) self->d->state); break; | 1195 | 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) { | |||
1162 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; | 1217 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; |
1163 | unsigned char *buf = (unsigned char *) buffer; | 1218 | unsigned char *buf = (unsigned char *) buffer; |
1164 | struct mspack_system *sys = self->system; | 1219 | struct mspack_system *sys = self->system; |
1165 | int avail, todo, outlen, ignore_cksum; | 1220 | int avail, todo, outlen, ignore_cksum, ignore_blocksize; |
1166 | 1221 | ||
1167 | ignore_cksum = self->param[MSCABD_PARAM_FIXMSZIP] && | 1222 | ignore_cksum = self->salvage || |
1168 | ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP); | 1223 | (self->fix_mszip && |
1224 | ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP)); | ||
1225 | ignore_blocksize = self->salvage; | ||
1169 | 1226 | ||
1170 | todo = bytes; | 1227 | todo = bytes; |
1171 | while (todo > 0) { | 1228 | while (todo > 0) { |
@@ -1185,37 +1242,35 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { | |||
1185 | 1242 | ||
1186 | /* check if we're out of input blocks, advance block counter */ | 1243 | /* check if we're out of input blocks, advance block counter */ |
1187 | if (self->d->block++ >= self->d->folder->base.num_blocks) { | 1244 | if (self->d->block++ >= self->d->folder->base.num_blocks) { |
1188 | self->read_error = MSPACK_ERR_DATAFORMAT; | 1245 | if (!self->salvage) { |
1189 | break; | 1246 | self->read_error = MSPACK_ERR_DATAFORMAT; |
1247 | } | ||
1248 | else { | ||
1249 | D(("Ran out of CAB input blocks prematurely")) | ||
1250 | } | ||
1251 | break; | ||
1190 | } | 1252 | } |
1191 | 1253 | ||
1192 | /* read a block */ | 1254 | /* read a block */ |
1193 | self->read_error = cabd_sys_read_block(sys, self->d, &outlen, ignore_cksum); | 1255 | self->read_error = cabd_sys_read_block(sys, self->d, &outlen, |
1256 | ignore_cksum, ignore_blocksize); | ||
1194 | if (self->read_error) return -1; | 1257 | if (self->read_error) return -1; |
1258 | self->d->outlen += outlen; | ||
1195 | 1259 | ||
1196 | /* special Quantum hack -- trailer byte to allow the decompressor | 1260 | /* special Quantum hack -- trailer byte to allow the decompressor |
1197 | * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have | 1261 | * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have |
1198 | * anything from 0 to 4 trailing null bytes. */ | 1262 | * anything from 0 to 4 trailing null bytes. */ |
1199 | if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) { | 1263 | if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) { |
1200 | *self->d->i_end++ = 0xFF; | 1264 | *self->d->i_end++ = 0xFF; |
1201 | } | 1265 | } |
1202 | 1266 | ||
1203 | /* is this the last block? */ | 1267 | /* is this the last block? */ |
1204 | if (self->d->block >= self->d->folder->base.num_blocks) { | 1268 | if (self->d->block >= self->d->folder->base.num_blocks) { |
1205 | /* last block */ | 1269 | if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { |
1206 | if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { | 1270 | /* special LZX hack -- on the last block, inform LZX of the |
1207 | /* special LZX hack -- on the last block, inform LZX of the | 1271 | * size of the output data stream. */ |
1208 | * size of the output data stream. */ | 1272 | lzxd_set_output_length((struct lzxd_stream *) self->d->state, self->d->outlen); |
1209 | lzxd_set_output_length((struct lzxd_stream *) self->d->state, (off_t) | 1273 | } |
1210 | ((self->d->block-1) * CAB_BLOCKMAX + outlen)); | ||
1211 | } | ||
1212 | } | ||
1213 | else { | ||
1214 | /* not the last block */ | ||
1215 | if (outlen != CAB_BLOCKMAX) { | ||
1216 | self->system->message(self->d->infh, | ||
1217 | "WARNING; non-maximal data block"); | ||
1218 | } | ||
1219 | } | 1274 | } |
1220 | } /* if (avail) */ | 1275 | } /* if (avail) */ |
1221 | } /* while (todo > 0) */ | 1276 | } /* while (todo > 0) */ |
@@ -1238,12 +1293,13 @@ static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) { | |||
1238 | * one cab file, if it does then the fragments will be reassembled | 1293 | * one cab file, if it does then the fragments will be reassembled |
1239 | */ | 1294 | */ |
1240 | static int cabd_sys_read_block(struct mspack_system *sys, | 1295 | static int cabd_sys_read_block(struct mspack_system *sys, |
1241 | struct mscabd_decompress_state *d, | 1296 | struct mscabd_decompress_state *d, |
1242 | int *out, int ignore_cksum) | 1297 | int *out, int ignore_cksum, |
1298 | int ignore_blocksize) | ||
1243 | { | 1299 | { |
1244 | unsigned char hdr[cfdata_SIZEOF]; | 1300 | unsigned char hdr[cfdata_SIZEOF]; |
1245 | unsigned int cksum; | 1301 | unsigned int cksum; |
1246 | int len; | 1302 | int len, full_len; |
1247 | 1303 | ||
1248 | /* reset the input block pointer and end of block pointer */ | 1304 | /* reset the input block pointer and end of block pointer */ |
1249 | d->i_ptr = d->i_end = &d->input[0]; | 1305 | d->i_ptr = d->i_end = &d->input[0]; |
@@ -1256,23 +1312,27 @@ static int cabd_sys_read_block(struct mspack_system *sys, | |||
1256 | 1312 | ||
1257 | /* skip any reserved block headers */ | 1313 | /* skip any reserved block headers */ |
1258 | if (d->data->cab->block_resv && | 1314 | if (d->data->cab->block_resv && |
1259 | sys->seek(d->infh, (off_t) d->data->cab->block_resv, | 1315 | sys->seek(d->infh, (off_t) d->data->cab->block_resv, |
1260 | MSPACK_SYS_SEEK_CUR)) | 1316 | MSPACK_SYS_SEEK_CUR)) |
1261 | { | 1317 | { |
1262 | return MSPACK_ERR_SEEK; | 1318 | return MSPACK_ERR_SEEK; |
1263 | } | 1319 | } |
1264 | 1320 | ||
1265 | /* blocks must not be over CAB_INPUTMAX in size */ | 1321 | /* blocks must not be over CAB_INPUTMAX in size */ |
1266 | len = EndGetI16(&hdr[cfdata_CompressedSize]); | 1322 | len = EndGetI16(&hdr[cfdata_CompressedSize]); |
1267 | if (((d->i_end - d->i_ptr) + len) > CAB_INPUTMAX) { | 1323 | full_len = (d->i_end - d->i_ptr) + len; /* include cab-spanning blocks */ |
1268 | D(("block size > CAB_INPUTMAX (%ld + %d)", d->i_end - d->i_ptr, len)) | 1324 | if (full_len > CAB_INPUTMAX) { |
1269 | return MSPACK_ERR_DATAFORMAT; | 1325 | D(("block size %d > CAB_INPUTMAX", full_len)); |
1326 | /* in salvage mode, blocks can be 65535 bytes but no more than that */ | ||
1327 | if (!ignore_blocksize || full_len > CAB_INPUTMAX_SALVAGE) { | ||
1328 | return MSPACK_ERR_DATAFORMAT; | ||
1329 | } | ||
1270 | } | 1330 | } |
1271 | 1331 | ||
1272 | /* blocks must not expand to more than CAB_BLOCKMAX */ | 1332 | /* blocks must not expand to more than CAB_BLOCKMAX */ |
1273 | if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) { | 1333 | if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) { |
1274 | D(("block size > CAB_BLOCKMAX")) | 1334 | D(("block size > CAB_BLOCKMAX")) |
1275 | return MSPACK_ERR_DATAFORMAT; | 1335 | if (!ignore_blocksize) return MSPACK_ERR_DATAFORMAT; |
1276 | } | 1336 | } |
1277 | 1337 | ||
1278 | /* read the block data */ | 1338 | /* read the block data */ |
@@ -1284,8 +1344,8 @@ static int cabd_sys_read_block(struct mspack_system *sys, | |||
1284 | if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) { | 1344 | if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) { |
1285 | unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0); | 1345 | unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0); |
1286 | if (cabd_checksum(&hdr[4], 4, sum2) != cksum) { | 1346 | if (cabd_checksum(&hdr[4], 4, sum2) != cksum) { |
1287 | if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; | 1347 | if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; |
1288 | sys->message(d->infh, "WARNING; bad block checksum found"); | 1348 | sys->message(d->infh, "WARNING; bad block checksum found"); |
1289 | } | 1349 | } |
1290 | } | 1350 | } |
1291 | 1351 | ||
@@ -1310,14 +1370,14 @@ static int cabd_sys_read_block(struct mspack_system *sys, | |||
1310 | 1370 | ||
1311 | /* advance to next member in the cabinet set */ | 1371 | /* advance to next member in the cabinet set */ |
1312 | if (!(d->data = d->data->next)) { | 1372 | if (!(d->data = d->data->next)) { |
1313 | D(("ran out of splits in cabinet set")) | 1373 | sys->message(d->infh, "WARNING; ran out of cabinets in set. Are any missing?"); |
1314 | return MSPACK_ERR_DATAFORMAT; | 1374 | return MSPACK_ERR_DATAFORMAT; |
1315 | } | 1375 | } |
1316 | 1376 | ||
1317 | /* open next cab file */ | 1377 | /* open next cab file */ |
1318 | d->incab = d->data->cab; | 1378 | d->incab = d->data->cab; |
1319 | if (!(d->infh = sys->open(sys, d->incab->base.filename, | 1379 | if (!(d->infh = sys->open(sys, d->incab->base.filename, |
1320 | MSPACK_SYS_OPEN_READ))) | 1380 | MSPACK_SYS_OPEN_READ))) |
1321 | { | 1381 | { |
1322 | return MSPACK_ERR_OPEN; | 1382 | return MSPACK_ERR_OPEN; |
1323 | } | 1383 | } |
@@ -1333,7 +1393,7 @@ static int cabd_sys_read_block(struct mspack_system *sys, | |||
1333 | } | 1393 | } |
1334 | 1394 | ||
1335 | static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, | 1395 | static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, |
1336 | unsigned int cksum) | 1396 | unsigned int cksum) |
1337 | { | 1397 | { |
1338 | unsigned int len, ul = 0; | 1398 | unsigned int len, ul = 0; |
1339 | 1399 | ||
@@ -1342,8 +1402,8 @@ static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, | |||
1342 | } | 1402 | } |
1343 | 1403 | ||
1344 | switch (bytes & 3) { | 1404 | switch (bytes & 3) { |
1345 | case 3: ul |= *data++ << 16; | 1405 | case 3: ul |= *data++ << 16; /*@fallthrough@*/ |
1346 | case 2: ul |= *data++ << 8; | 1406 | case 2: ul |= *data++ << 8; /*@fallthrough@*/ |
1347 | case 1: ul |= *data; | 1407 | case 1: ul |= *data; |
1348 | } | 1408 | } |
1349 | cksum ^= ul; | 1409 | cksum ^= ul; |
@@ -1365,9 +1425,9 @@ struct noned_state { | |||
1365 | }; | 1425 | }; |
1366 | 1426 | ||
1367 | static struct noned_state *noned_init(struct mspack_system *sys, | 1427 | static struct noned_state *noned_init(struct mspack_system *sys, |
1368 | struct mspack_file *in, | 1428 | struct mspack_file *in, |
1369 | struct mspack_file *out, | 1429 | struct mspack_file *out, |
1370 | int bufsize) | 1430 | int bufsize) |
1371 | { | 1431 | { |
1372 | struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state)); | 1432 | struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state)); |
1373 | unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) bufsize); | 1433 | 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) { | |||
1419 | switch (param) { | 1479 | switch (param) { |
1420 | case MSCABD_PARAM_SEARCHBUF: | 1480 | case MSCABD_PARAM_SEARCHBUF: |
1421 | if (value < 4) return MSPACK_ERR_ARGS; | 1481 | if (value < 4) return MSPACK_ERR_ARGS; |
1422 | self->param[MSCABD_PARAM_SEARCHBUF] = value; | 1482 | self->searchbuf_size = value; |
1423 | break; | 1483 | break; |
1424 | case MSCABD_PARAM_FIXMSZIP: | 1484 | case MSCABD_PARAM_FIXMSZIP: |
1425 | self->param[MSCABD_PARAM_FIXMSZIP] = value; | 1485 | self->fix_mszip = value; |
1426 | break; | 1486 | break; |
1427 | case MSCABD_PARAM_DECOMPBUF: | 1487 | case MSCABD_PARAM_DECOMPBUF: |
1428 | if (value < 4) return MSPACK_ERR_ARGS; | 1488 | if (value < 4) return MSPACK_ERR_ARGS; |
1429 | self->param[MSCABD_PARAM_DECOMPBUF] = value; | 1489 | self->buf_size = value; |
1490 | break; | ||
1491 | case MSCABD_PARAM_SALVAGE: | ||
1492 | self->salvage = value; | ||
1430 | break; | 1493 | break; |
1431 | default: | 1494 | default: |
1432 | return MSPACK_ERR_ARGS; | 1495 | return MSPACK_ERR_ARGS; |