Browse code

Updated from libmspack-0.7.1alpha to libmspack-0.8alpha.

Micah Snyder authored on 2018/10/22 22:32:03
Showing 19 changed files
... ...
@@ -1,17 +1,64 @@
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.
1
+2018-10-20  Stuart Caie <kyzer@cabextract.org.uk>
2
+
3
+	* src/chmextract.c: add anti "../" and leading slash protection to
4
+	chmextract. I'm not pleased about this. All the sample code provided
5
+	with libmspack is meant to be simple examples of library use, not
6
+	"productised" binaries. Making the "useful" code samples install
7
+	as binaries was a mistake. They were never intended to protect you
8
+	from unpacking archive files with relative/absolute paths, and I
9
+	would prefer that they never will be.
10
+
11
+2018-10-17  Stuart Caie <kyzer@cabextract.org.uk>
12
+
13
+	* cab.h: Make the CAB block input buffer one byte larger, to allow
14
+	a maximum-allowed-size input block and the special extra byte added
15
+	after the block by cabd_sys_read_block to help Quantum alignment.
16
+	Thanks to Henri Salo for reporting this.
17
+
18
+2018-10-17  Stuart Caie <kyzer@cabextract.org.uk>
19
+
20
+	* chmd_read_headers(): again reject files with blank filenames, this
21
+	time because their 1st or 2nd byte is null, not because their length
22
+	is zero.  Thanks again to Hanno Böck for finding the issue.
23
+
24
+2018-10-16  Stuart Caie <kyzer@cabextract.org.uk>
25
+
26
+	* Makefile.am: using automake _DEPENDENCIES for chmd_test appears to
27
+	override the default dependencies (e.g. sources), so libchmd.la was no
28
+	longer considered a dependency of chmd_test. This breaks parallel
29
+	builds like "make -j4". Added libchmd.la explicitly to dependencies.
30
+	Thanks to Thomas Deutschmann for reporting this.
31
+
32
+2018-10-16  Stuart Caie <kyzer@cabextract.org.uk>
33
+
34
+	* cabd.c: add new parameter, MSCABD_PARAM_SALVAGE, which makes CAB file
35
+	reading and extraction more lenient, to allow damaged or mangled CABs
36
+	to be extracted. When enabled:
37
+	- cabd->open() won't reject cabinets with files that have invalid
38
+	  folder indices or filenames. These files will simply be skipped
39
+	- cabd->extract() won't reject files with invalid lengths, but will
40
+	  limit them to the maximum possible
41
+	- block output sizes over 32768 bytes won't be rejected
42
+	- invalid data block checksums won't be rejected
43
+	
44
+	It's still possible for corrupted files to fail extraction, but more
45
+	data can be extracted before they do.
46
+	
47
+	This new parameter doesn't affect the existing MSCABD_PARAM_FIXMSZIP
48
+	parameter, which ignores MSZIP decompression failures. You can enable
49
+	both at once.
50
+	
51
+	Thanks to Micah Snyder from ClamAV for working with me to get this
52
+	feature into libmspack. This also helps ClamAV move towards using a
53
+	vanilla copy of libmspack without needing their own patchset.
54
+
55
+2018-08-13  Stuart Caie <kyzer@cabextract.org.uk>
56
+
57
+	* mspack.h: clarify that mspack_system.free() should allow NULL. If your
58
+	mspack_system implementation doesn't, it would already have crashed, as
59
+	there are several places where libmspack calls sys->free(NULL). This
60
+	change makes it official, and amends a few "if (x) sys->free(x)" cases
61
+	to the simpler "sys->free(x)" to make it clearer.
15 62
 
16 63
 2018-08-09  Stuart Caie <kyzer@cabextract.org.uk>
17 64
 
... ...
@@ -89,7 +89,7 @@ test_chmd_order_SOURCES =	test/chmd_order.c test/md5.c test/md5.h \
89 89
 test_chmd_order_LDADD =		libmschmd.la
90 90
 test_chmd_test_SOURCES =	test/chmd_test.c libmschmd.la
91 91
 test_chmd_test_LDADD =		libmschmd.la
92
-test_chmd_test_DEPENDENCIES = 	test/test_files/chmd/cve-2015-4467-reset-interval-zero.chm
92
+test_chmd_test_DEPENDENCIES = 	libmschmd.la test/test_files/chmd/cve-2015-4467-reset-interval-zero.chm
93 93
 test_chminfo_SOURCES =		test/chminfo.c libmschmd.la
94 94
 test_chminfo_LDADD =		libmschmd.la
95 95
 test_kwajd_test_SOURCES =	test/kwajd_test.c libmspack.la
... ...
@@ -515,7 +515,7 @@ test_chmd_order_SOURCES = test/chmd_order.c test/md5.c test/md5.h \
515 515
 test_chmd_order_LDADD = libmschmd.la
516 516
 test_chmd_test_SOURCES = test/chmd_test.c libmschmd.la
517 517
 test_chmd_test_LDADD = libmschmd.la
518
-test_chmd_test_DEPENDENCIES = test/test_files/chmd/cve-2015-4467-reset-interval-zero.chm
518
+test_chmd_test_DEPENDENCIES = libmschmd.la test/test_files/chmd/cve-2015-4467-reset-interval-zero.chm
519 519
 test_chminfo_SOURCES = test/chminfo.c libmschmd.la
520 520
 test_chminfo_LDADD = libmschmd.la
521 521
 test_kwajd_test_SOURCES = test/kwajd_test.c libmspack.la
... ...
@@ -1,4 +1,4 @@
1
-libmspack 0.7.1alpha
1
+libmspack 0.8alpha
2 2
 
3 3
 The purpose of libmspack is to provide compressors and decompressors,
4 4
 archivers and dearchivers for Microsoft compression formats: CAB, CHM, WIM,
... ...
@@ -36,12 +36,11 @@ make install
36 36
 This will install the main libmspack library and mspack.h header file.
37 37
 Some other libraries and executables are built, but not installed.
38 38
 
39
-If building from the Subversion repository, running rebuild.sh will create
40
-all the automatically generated files like the configure script, and will
41
-then ./configure, make and make distcheck. Running cleanup.sh will perform
42
-a thorough clean, deleting all automatically generated files.
39
+If building from the Git repository, running rebuild.sh will create all the
40
+auto-generated files, then run ./configure && make. Running cleanup.sh will
41
+perform a thorough clean, deleting all auto-generated files.
43 42
 
44
-In addition to gcc, you also need the following for building from Subversion:
43
+In addition to gcc, you also need the following for building from repository:
45 44
 
46 45
 - at least autoconf 2.57
47 46
 - at least automake 1.7
... ...
@@ -1,6 +1,6 @@
1 1
 #! /bin/sh
2 2
 # Guess values for system-dependent variables and create Makefiles.
3
-# Generated by GNU Autoconf 2.69 for libmspack 0.7.1alpha.
3
+# Generated by GNU Autoconf 2.69 for libmspack 0.8alpha.
4 4
 #
5 5
 # Report bugs to <kyzer@cabextract.org.uk>.
6 6
 #
... ...
@@ -590,8 +590,8 @@ MAKEFLAGS=
590 590
 # Identity of this package.
591 591
 PACKAGE_NAME='libmspack'
592 592
 PACKAGE_TARNAME='libmspack'
593
-PACKAGE_VERSION='0.7.1alpha'
594
-PACKAGE_STRING='libmspack 0.7.1alpha'
593
+PACKAGE_VERSION='0.8alpha'
594
+PACKAGE_STRING='libmspack 0.8alpha'
595 595
 PACKAGE_BUGREPORT='kyzer@cabextract.org.uk'
596 596
 PACKAGE_URL=''
597 597
 
... ...
@@ -1330,7 +1330,7 @@ if test "$ac_init_help" = "long"; then
1330 1330
   # Omit some internal or obsolete options to make the list less imposing.
1331 1331
   # This message is too long to be a string in the A/UX 3.1 sh.
1332 1332
   cat <<_ACEOF
1333
-\`configure' configures libmspack 0.7.1alpha to adapt to many kinds of systems.
1333
+\`configure' configures libmspack 0.8alpha to adapt to many kinds of systems.
1334 1334
 
1335 1335
 Usage: $0 [OPTION]... [VAR=VALUE]...
1336 1336
 
... ...
@@ -1401,7 +1401,7 @@ fi
1401 1401
 
1402 1402
 if test -n "$ac_init_help"; then
1403 1403
   case $ac_init_help in
1404
-     short | recursive ) echo "Configuration of libmspack 0.7.1alpha:";;
1404
+     short | recursive ) echo "Configuration of libmspack 0.8alpha:";;
1405 1405
    esac
1406 1406
   cat <<\_ACEOF
1407 1407
 
... ...
@@ -1513,7 +1513,7 @@ fi
1513 1513
 test -n "$ac_init_help" && exit $ac_status
1514 1514
 if $ac_init_version; then
1515 1515
   cat <<\_ACEOF
1516
-libmspack configure 0.7.1alpha
1516
+libmspack configure 0.8alpha
1517 1517
 generated by GNU Autoconf 2.69
1518 1518
 
1519 1519
 Copyright (C) 2012 Free Software Foundation, Inc.
... ...
@@ -2119,7 +2119,7 @@ cat >config.log <<_ACEOF
2119 2119
 This file contains any messages produced by compilers while
2120 2120
 running configure, to aid debugging if configure makes a mistake.
2121 2121
 
2122
-It was created by libmspack $as_me 0.7.1alpha, which was
2122
+It was created by libmspack $as_me 0.8alpha, which was
2123 2123
 generated by GNU Autoconf 2.69.  Invocation command line was
2124 2124
 
2125 2125
   $ $0 $@
... ...
@@ -2983,7 +2983,7 @@ fi
2983 2983
 
2984 2984
 # Define the identity of the package.
2985 2985
  PACKAGE='libmspack'
2986
- VERSION='0.7.1alpha'
2986
+ VERSION='0.8alpha'
2987 2987
 
2988 2988
 
2989 2989
 cat >>confdefs.h <<_ACEOF
... ...
@@ -13640,7 +13640,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
13640 13640
 # report actual input values of CONFIG_FILES etc. instead of their
13641 13641
 # values after options handling.
13642 13642
 ac_log="
13643
-This file was extended by libmspack $as_me 0.7.1alpha, which was
13643
+This file was extended by libmspack $as_me 0.8alpha, which was
13644 13644
 generated by GNU Autoconf 2.69.  Invocation command line was
13645 13645
 
13646 13646
   CONFIG_FILES    = $CONFIG_FILES
... ...
@@ -13706,7 +13706,7 @@ _ACEOF
13706 13706
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
13707 13707
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
13708 13708
 ac_cs_version="\\
13709
-libmspack config.status 0.7.1alpha
13709
+libmspack config.status 0.8alpha
13710 13710
 configured by $0, generated by GNU Autoconf 2.69,
13711 13711
   with options \\"\$ac_cs_config\\"
13712 13712
 
... ...
@@ -1,7 +1,7 @@
1 1
 # -*- Autoconf -*-
2 2
 # Process this file with autoconf to produce a configure script.
3 3
 AC_PREREQ(2.59)
4
-AC_INIT([libmspack],[0.7.1alpha],[kyzer@cabextract.org.uk])
4
+AC_INIT([libmspack],[0.8alpha],[kyzer@cabextract.org.uk])
5 5
 AC_CONFIG_MACRO_DIR([m4])
6 6
 AM_INIT_AUTOMAKE
7 7
 AC_CONFIG_SRCDIR([mspack/mspack.h])
... ...
@@ -1,5 +1,5 @@
1 1
 /* This file is part of libmspack.
2
- * (C) 2003-2004 Stuart Caie.
2
+ * (C) 2003-2018 Stuart Caie.
3 3
  *
4 4
  * libmspack is free software; you can redistribute it and/or modify it under
5 5
  * the terms of the GNU Lesser General Public License (LGPL) version 2.1
... ...
@@ -67,16 +67,24 @@
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 (65535)
71
-#define CAB_BLOCKSTD (32768)
70
+#define CAB_BLOCKMAX (32768)
72 71
 #define CAB_INPUTMAX (CAB_BLOCKMAX+6144)
73 72
 
73
+/* input buffer needs to be CAB_INPUTMAX + 1 byte to allow for max-sized block
74
+ * plus 1 trailer byte added by cabd_sys_read_block() for Quantum alignment.
75
+ *
76
+ * When MSCABD_PARAM_SALVAGE is set, block size is not checked so can be
77
+ * up to 65535 bytes, so max input buffer size needed is 65535 + 1
78
+ */
79
+#define CAB_INPUTMAX_SALVAGE (65535)
80
+#define CAB_INPUTBUF (CAB_INPUTMAX_SALVAGE + 1)
81
+
74 82
 /* There are no more than 65535 data blocks per folder, so a folder cannot
75 83
  * be more than 32768*65535 bytes in length. As files cannot span more than
76 84
  * one folder, this is also their max offset, length and offset+length limit.
77 85
  */
78 86
 #define CAB_FOLDERMAX (65535)
79
-#define CAB_LENGTHMAX UINT_MAX
87
+#define CAB_LENGTHMAX (CAB_BLOCKMAX * CAB_FOLDERMAX)
80 88
 
81 89
 /* CAB compression definitions */
82 90
 
... ...
@@ -93,6 +101,7 @@ struct mscabd_decompress_state {
93 93
   struct mscabd_folder_data *data;   /* current folder split we're in        */
94 94
   unsigned int offset;               /* uncompressed offset within folder    */
95 95
   unsigned int block;                /* which block are we decompressing?    */
96
+  off_t outlen;                      /* cumulative sum of block output sizes */
96 97
   struct mspack_system sys;          /* special I/O code for decompressor    */
97 98
   int comp_type;                     /* type of compression used by folder   */
98 99
   int (*decompress)(void *, off_t);  /* decompressor code                    */
... ...
@@ -101,7 +110,7 @@ struct mscabd_decompress_state {
101 101
   struct mspack_file *infh;          /* input file handle                    */
102 102
   struct mspack_file *outfh;         /* output file handle                   */
103 103
   unsigned char *i_ptr, *i_end;      /* input data consumed, end             */
104
-  unsigned char input[CAB_INPUTMAX]; /* one input block of data              */
104
+  unsigned char input[CAB_INPUTBUF]; /* one input block of data              */
105 105
 };
106 106
 
107 107
 struct mscab_decompressor_p {
... ...
@@ -1,5 +1,5 @@
1 1
 /* This file is part of libmspack.
2
- * (C) 2003-2011 Stuart Caie.
2
+ * (C) 2003-2018 Stuart Caie.
3 3
  *
4 4
  * libmspack is free software; you can redistribute it and/or modify it under
5 5
  * the terms of the GNU Lesser General Public License (LGPL) version 2.1
... ...
@@ -109,7 +109,7 @@ static int cabd_sys_write(
109 109
   struct mspack_file *file, void *buffer, int bytes);
110 110
 static int cabd_sys_read_block(
111 111
   struct mspack_system *sys, struct mscabd_decompress_state *d, int *out,
112
-  int ignore_cksum);
112
+  int ignore_cksum, int ignore_blocksize);
113 113
 static unsigned int cabd_checksum(
114 114
   unsigned char *data, unsigned int bytes, unsigned int cksum);
115 115
 static struct noned_state *noned_init(
... ...
@@ -306,9 +306,9 @@ static void cabd_close(struct mscab_decompressor *base,
306 306
 static int cabd_read_headers(struct mspack_system *sys,
307 307
 			     struct mspack_file *fh,
308 308
 			     struct mscabd_cabinet_p *cab,
309
-			     off_t offset, int quiet, int salvage)
309
+			     off_t offset, int salvage, int quiet)
310 310
 {
311
-  int num_folders, num_files, folder_resv, i, x, fidx, fidx_ok, read_string_errno = 0;
311
+  int num_folders, num_files, folder_resv, i, x, err, fidx;
312 312
   struct mscabd_folder_p *fol, *linkfol = NULL;
313 313
   struct mscabd_file *file, *linkfile = NULL;
314 314
   unsigned char buf[64];
... ...
@@ -392,18 +392,18 @@ static int cabd_read_headers(struct mspack_system *sys,
392 392
 
393 393
   /* read name and info of preceeding cabinet in set, if present */
394 394
   if (cab->base.flags & cfheadPREV_CABINET) {
395
-    cab->base.prevname = cabd_read_string(sys, fh, &read_string_errno);
396
-    if (read_string_errno) return read_string_errno;
397
-    cab->base.previnfo = cabd_read_string(sys, fh, &read_string_errno);
398
-    if (read_string_errno) return read_string_errno;
395
+    cab->base.prevname = cabd_read_string(sys, fh, &err);
396
+    if (err) return err;
397
+    cab->base.previnfo = cabd_read_string(sys, fh, &err);
398
+    if (err) return err;
399 399
   }
400 400
 
401 401
   /* read name and info of next cabinet in set, if present */
402 402
   if (cab->base.flags & cfheadNEXT_CABINET) {
403
-    cab->base.nextname = cabd_read_string(sys, fh, &read_string_errno);
404
-    if (read_string_errno) return read_string_errno;
405
-    cab->base.nextinfo = cabd_read_string(sys, fh, &read_string_errno);
406
-    if (read_string_errno) return read_string_errno;
403
+    cab->base.nextname = cabd_read_string(sys, fh, &err);
404
+    if (err) return err;
405
+    cab->base.nextinfo = cabd_read_string(sys, fh, &err);
406
+    if (err) return err;
407 407
   }
408 408
 
409 409
   /* read folders */
... ...
@@ -452,7 +452,6 @@ static int cabd_read_headers(struct mspack_system *sys,
452 452
     file->offset   = EndGetI32(&buf[cffile_FolderOffset]);
453 453
 
454 454
     /* set folder pointer */
455
-    fidx_ok = 1;
456 455
     fidx = EndGetI16(&buf[cffile_FolderIndex]);
457 456
     if (fidx < cffileCONTINUED_FROM_PREV) {
458 457
       /* normal folder index; count up to the correct folder */
... ...
@@ -462,12 +461,8 @@ static int cabd_read_headers(struct mspack_system *sys,
462 462
         file->folder = ifol;
463 463
       }
464 464
       else {
465
-        file->folder = NULL;
466
-      }
467
-
468
-      if (!file->folder) {
469 465
         D(("invalid folder index"))
470
-        fidx_ok = 0;
466
+        file->folder = NULL;
471 467
       }
472 468
     }
473 469
     else {
... ...
@@ -511,11 +506,14 @@ static int cabd_read_headers(struct mspack_system *sys,
511 511
     file->date_y = (x >> 9) + 1980;
512 512
 
513 513
     /* get filename */
514
-    file->filename = cabd_read_string(sys, fh, &read_string_errno);
515
-    if (read_string_errno || !fidx_ok) {
514
+    file->filename = cabd_read_string(sys, fh, &err);
515
+
516
+    /* if folder index or filename are bad, either skip it or fail */
517
+    if (err || !file->folder) {
516 518
       sys->free(file->filename);
517 519
       sys->free(file);
518
-      if (salvage) continue; else return read_string_errno;
520
+      if (salvage) continue;
521
+      return err ? err : MSPACK_ERR_DATAFORMAT;
519 522
     }
520 523
 
521 524
     /* link file entry into file list */
... ...
@@ -726,10 +724,11 @@ static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf,
726 726
 
727 727
 	/* check that the files offset is less than the alleged length of
728 728
 	 * the cabinet, and that the offset + the alleged length are
729
-	 * 'roughly' within the end of overall file length */
729
+	 * 'roughly' within the end of overall file length. In salvage
730
+	 * mode, don't check the alleged length, allow it to be garbage */
730 731
 	if ((foffset_u32 < cablen_u32) &&
731 732
 	    ((caboff + (off_t) foffset_u32) < (flen + 32)) &&
732
-	    ((caboff + (off_t) cablen_u32)  < (flen + 32)) )
733
+	    (((caboff + (off_t) cablen_u32)  < (flen + 32)) || salvage))
733 734
 	{
734 735
 	  /* likely cabinet found -- try reading it */
735 736
 	  if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) {
... ...
@@ -1012,7 +1011,7 @@ static int cabd_extract(struct mscab_decompressor *base,
1012 1012
   struct mscabd_folder_p *fol;
1013 1013
   struct mspack_system *sys;
1014 1014
   struct mspack_file *fh;
1015
-  off_t filelen, maxlen;
1015
+  off_t filelen;
1016 1016
 
1017 1017
   if (!self) return MSPACK_ERR_ARGS;
1018 1018
   if (!file) return self->error = MSPACK_ERR_ARGS;
... ...
@@ -1045,17 +1044,12 @@ static int cabd_extract(struct mscab_decompressor *base,
1045 1045
     return self->error = MSPACK_ERR_DECRUNCH;
1046 1046
   }
1047 1047
 
1048
-  /* if file goes beyond what can be decoded, either error,
1049
-   * or in salvage mode reduce file length to what can be decoded
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 1050
    */
1051
-  maxlen = fol->base.num_blocks * CAB_BLOCKMAX;
1052
-  if ((file->offset + filelen) > maxlen) {
1053
-    if (self->param[MSCABD_PARAM_SALVAGE]) {
1054
-      sys->message(NULL, "WARNING: can only extract first %"LD" bytes "
1055
-                   " of file \"%s\"", maxlen, file->filename);
1056
-      filelen = maxlen;
1057
-    }
1058
-    else {
1051
+  if (!self->param[MSCABD_PARAM_SALVAGE]) {
1052
+    off_t maxlen = fol->base.num_blocks * CAB_BLOCKMAX;
1053
+    if ((file->offset + filelen) > maxlen) {
1059 1054
       sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, "
1060 1055
                    "cabinet set is incomplete", file->filename);
1061 1056
       return self->error = MSPACK_ERR_DECRUNCH;
... ...
@@ -1107,6 +1101,7 @@ static int cabd_extract(struct mscab_decompressor *base,
1107 1107
     self->d->data   = &fol->data;
1108 1108
     self->d->offset = 0;
1109 1109
     self->d->block  = 0;
1110
+    self->d->outlen = 0;
1110 1111
     self->d->i_ptr = self->d->i_end = &self->d->input[0];
1111 1112
 
1112 1113
     /* read_error lasts for the lifetime of a decompressor */
... ...
@@ -1227,11 +1222,12 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) {
1227 1227
   struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file;
1228 1228
   unsigned char *buf = (unsigned char *) buffer;
1229 1229
   struct mspack_system *sys = self->system;
1230
-  int avail, todo, outlen, ignore_cksum;
1230
+  int avail, todo, outlen, ignore_cksum, ignore_blocksize;
1231 1231
 
1232 1232
   ignore_cksum = self->param[MSCABD_PARAM_SALVAGE] ||
1233 1233
     (self->param[MSCABD_PARAM_FIXMSZIP] && 
1234 1234
      ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP));
1235
+  ignore_blocksize = self->param[MSCABD_PARAM_SALVAGE];
1235 1236
 
1236 1237
   todo = bytes;
1237 1238
   while (todo > 0) {
... ...
@@ -1251,16 +1247,20 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) {
1251 1251
 
1252 1252
       /* check if we're out of input blocks, advance block counter */
1253 1253
       if (self->d->block++ >= self->d->folder->base.num_blocks) {
1254
-        if (!self->param[MSCABD_PARAM_SALVAGE])
1254
+        if (!self->param[MSCABD_PARAM_SALVAGE]) {
1255 1255
           self->read_error = MSPACK_ERR_DATAFORMAT;
1256
-        else
1256
+        }
1257
+        else {
1257 1258
           D(("Ran out of CAB input blocks prematurely"))
1259
+        }
1258 1260
         break;
1259 1261
       }
1260 1262
 
1261 1263
       /* read a block */
1262
-      self->read_error = cabd_sys_read_block(sys, self->d, &outlen, ignore_cksum);
1264
+      self->read_error = cabd_sys_read_block(sys, self->d, &outlen,
1265
+        ignore_cksum, ignore_blocksize);
1263 1266
       if (self->read_error) return -1;
1267
+      self->d->outlen += outlen;
1264 1268
 
1265 1269
       /* special Quantum hack -- trailer byte to allow the decompressor
1266 1270
        * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have
... ...
@@ -1271,19 +1271,10 @@ static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) {
1271 1271
 
1272 1272
       /* is this the last block? */
1273 1273
       if (self->d->block >= self->d->folder->base.num_blocks) {
1274
-	/* last block */
1275 1274
 	if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) {
1276 1275
 	  /* special LZX hack -- on the last block, inform LZX of the
1277 1276
 	   * size of the output data stream. */
1278
-	  lzxd_set_output_length((struct lzxd_stream *) self->d->state, (off_t)
1279
-				 ((self->d->block-1) * CAB_BLOCKSTD + outlen));
1280
-	}
1281
-      }
1282
-      else {
1283
-	/* not the last block */
1284
-	if (outlen != CAB_BLOCKMAX) {
1285
-	  self->system->message(self->d->infh,
1286
-				"WARNING; non-maximal data block");
1277
+	  lzxd_set_output_length((struct lzxd_stream *) self->d->state, self->d->outlen);
1287 1278
 	}
1288 1279
       }
1289 1280
     } /* if (avail) */
... ...
@@ -1308,11 +1299,12 @@ static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) {
1308 1308
  */
1309 1309
 static int cabd_sys_read_block(struct mspack_system *sys,
1310 1310
 			       struct mscabd_decompress_state *d,
1311
-			       int *out, int ignore_cksum)
1311
+			       int *out, int ignore_cksum,
1312
+                               int ignore_blocksize)
1312 1313
 {
1313 1314
   unsigned char hdr[cfdata_SIZEOF];
1314 1315
   unsigned int cksum;
1315
-  int len;
1316
+  int len, full_len;
1316 1317
 
1317 1318
   /* reset the input block pointer and end of block pointer */
1318 1319
   d->i_ptr = d->i_end = &d->input[0];
... ...
@@ -1333,16 +1325,19 @@ static int cabd_sys_read_block(struct mspack_system *sys,
1333 1333
 
1334 1334
     /* blocks must not be over CAB_INPUTMAX in size */
1335 1335
     len = EndGetI16(&hdr[cfdata_CompressedSize]);
1336
-    if (((d->i_end - d->i_ptr) + len) > CAB_INPUTMAX) {
1337
-      D(("block size > CAB_INPUTMAX (%ld + %d)",
1338
-          (long)(d->i_end - d->i_ptr), len))
1339
-      return MSPACK_ERR_DATAFORMAT;
1336
+    full_len = (d->i_end - d->i_ptr) + len; /* include cab-spanning blocks */
1337
+    if (full_len > CAB_INPUTMAX) {
1338
+      D(("block size %d > CAB_INPUTMAX", full_len));
1339
+      /* in salvage mode, blocks can be 65535 bytes but no more than that */
1340
+      if (!ignore_blocksize || full_len > CAB_INPUTMAX_SALVAGE) {
1341
+          return MSPACK_ERR_DATAFORMAT;
1342
+      }
1340 1343
     }
1341 1344
 
1342 1345
      /* blocks must not expand to more than CAB_BLOCKMAX */
1343 1346
     if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) {
1344 1347
       D(("block size > CAB_BLOCKMAX"))
1345
-      return MSPACK_ERR_DATAFORMAT;
1348
+      if (!ignore_blocksize) return MSPACK_ERR_DATAFORMAT;
1346 1349
     }
1347 1350
 
1348 1351
     /* read the block data */
... ...
@@ -447,14 +447,14 @@ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
447 447
     while (num_entries--) {
448 448
       READ_ENCINT(name_len);
449 449
       if (name_len > (unsigned int) (end - p)) goto chunk_end;
450
-      /* consider blank filenames to be an error */
451
-      if (name_len == 0) goto chunk_end;
452 450
       name = p; p += name_len;
453
-
454 451
       READ_ENCINT(section);
455 452
       READ_ENCINT(offset);
456 453
       READ_ENCINT(length);
457 454
 
455
+      /* ignore blank or one-char (e.g. "/") filenames we'd return as blank */
456
+      if (name_len < 2 || !name[0] || !name[1]) continue;
457
+
458 458
       /* empty files and directory names are stored as a file entry at
459 459
        * offset 0 with length 0. We want to keep empty files, but not
460 460
        * directory names, which end with a "/" */
... ...
@@ -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 = NULL, *end = NULL;
539
+    const unsigned char *chunk, *p, *end;
540 540
     int err = MSPACK_ERR_OK, result = -1;
541 541
     unsigned int n, sec;
542 542
 
... ...
@@ -113,8 +113,8 @@ static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base,
113 113
     }
114 114
     
115 115
     if (self->error) {
116
-	if (fh)  sys->close(fh);
117
-	if (hdr) sys->free(hdr);
116
+	if (fh) sys->close(fh);
117
+	sys->free(hdr);
118 118
 	hdr = NULL;
119 119
     }
120 120
 
... ...
@@ -424,7 +424,7 @@ struct mspack_system {
424 424
   /**
425 425
    * Frees memory.
426 426
    * 
427
-   * @param ptr the memory to be freed.
427
+   * @param ptr the memory to be freed. NULL is accepted and ignored.
428 428
    * @see alloc()
429 429
    */
430 430
   void (*free)(void *ptr);
... ...
@@ -236,14 +236,10 @@ static int oabd_decompress(struct msoab_decompressor *_self, const char *input,
236 236
   }
237 237
 
238 238
  out:
239
-  if (lzx)
240
-    lzxd_free(lzx);
241
-  if (buf)
242
-    sys->free(buf);
243
-  if (outfh)
244
-    sys->close(outfh);
245
-  if (infh)
246
-    sys->close(infh);
239
+  if (lzx) lzxd_free(lzx);
240
+  if (outfh) sys->close(outfh);
241
+  if (infh) sys->close(infh);
242
+  sys->free(buf);
247 243
 
248 244
   return ret;
249 245
 }
... ...
@@ -390,16 +386,11 @@ static int oabd_decompress_incremental(struct msoab_decompressor *_self,
390 390
   }
391 391
 
392 392
  out:
393
-  if (lzx)
394
-    lzxd_free(lzx);
395
-  if (buf)
396
-    sys->free(buf);
397
-  if (outfh)
398
-    sys->close(outfh);
399
-  if (basefh)
400
-    sys->close(basefh);
401
-  if (infh)
402
-    sys->close(infh);
393
+  if (lzx) lzxd_free(lzx);
394
+  if (outfh) sys->close(outfh);
395
+  if (basefh) sys->close(basefh);
396
+  if (infh) sys->close(infh);
397
+  sys->free(buf);
403 398
 
404 399
   return ret;
405 400
 }
... ...
@@ -99,8 +99,8 @@ static struct msszddd_header *szddd_open(struct msszdd_decompressor *base,
99 99
     }
100 100
     
101 101
     if (self->error) {
102
-	if (fh)  sys->close(fh);
103
-	if (hdr) sys->free(hdr);
102
+	if (fh) sys->close(fh);
103
+	sys->free(hdr);
104 104
 	hdr = NULL;
105 105
     }
106 106
 
... ...
@@ -8,74 +8,78 @@
8 8
 #include <mspack.h>
9 9
 #include <system.h>
10 10
 
11
+#if HAVE_FSEEKO
12
+# define fseek fseeko
13
+#endif
14
+
11 15
 #define BUF_SIZE (1024*4096)
12 16
 char buf[BUF_SIZE];
13 17
 
14 18
 void rip(char *fname, off_t offset, unsigned int length) {
15
-  static unsigned int counter = 1;
16
-   struct stat st_buf;
17
-  char outname[13];
18
-  FILE *in, *out;
19
+    static unsigned int counter = 1;
20
+    struct stat st_buf;
21
+    char outname[13];
22
+    FILE *in = NULL, *out = NULL;
19 23
 
20
-  do {
21
-    snprintf(outname, 13, "%08u.cab", counter++);
22
-  } while (stat(outname, &st_buf) == 0);
24
+    /* find an unused output filename */
25
+    do {
26
+        snprintf(outname, 13, "%08u.cab", counter++);
27
+    } while (stat(outname, &st_buf) == 0);
23 28
 
24
-  printf("ripping %s offset %" LD " length %u to %s\n",
25
-	 fname, offset, length, outname);
29
+    printf("ripping %s offset %" LD " length %u to %s\n",
30
+           fname, offset, length, outname);
26 31
 
27
-  if ((in = fopen(fname, "rb"))) {
28
-#if HAVE_FSEEKO
29
-    if (!fseeko(in, offset, SEEK_SET)) {
30
-#else
31
-    if (!fseek(in, offset, SEEK_SET)) {
32
-#endif
33
-      if ((out = fopen(outname, "wb"))) {
34
-	while (length > 0) {
35
-	  unsigned int run = BUF_SIZE;
36
-	  if (run > length) run = length;
37
-	  if (fread(&buf[0], 1, run, in) != run) {
38
-	    perror(fname);
39
-	    break;
40
-	  }
41
-	  if (fwrite(&buf[0], 1, run, out) != run) {
42
-	    perror(outname);
43
-	    break;
44
-	  }
45
-	  length -= run;
46
-	}
47
-	fclose(out);
48
-      }
49
-      else {
50
-	perror(outname);
51
-      }
32
+    if (!(in = fopen(fname, "rb"))) {
33
+        perror(fname);
34
+        goto cleanup;
35
+    }
36
+    if (!(out = fopen(outname, "wb"))) {
37
+        perror(outname);
38
+        goto cleanup;
52 39
     }
53
-    else {
54
-      perror(fname);
40
+    if (fseek(in, offset, SEEK_SET)) {
41
+        fprintf(stderr, "%s: can't seek to cab offset %"LD"\n", fname, offset);
42
+        goto cleanup;
55 43
     }
56
-    fclose(in);
57
-  }
58
-  else {
59
-    perror(fname);
60
-  }
44
+    while (length) {
45
+        size_t run = (length > BUF_SIZE) ? BUF_SIZE : length;
46
+        size_t actual = fread(&buf[0], 1, run, in);
47
+        if (actual < run) {
48
+            fprintf(stderr, "%s: file %u bytes shorter than expected\n",
49
+                    fname, length - (run - actual));
50
+            length = run = actual;
51
+        }
52
+        if (fwrite(&buf[0], 1, run, out) != run) {
53
+            perror(outname);
54
+            break;
55
+        }
56
+        length -= run;
57
+    }
58
+
59
+cleanup:
60
+    if (in) fclose(in);
61
+    if (out) fclose(out);
61 62
 }
62 63
 
63 64
 int main(int argc, char *argv[]) {
64
-  struct mscab_decompressor *cabd;
65
-  struct mscabd_cabinet *cab, *c;
66
-  int err;
65
+    struct mscab_decompressor *cabd;
66
+    struct mscabd_cabinet *cab, *c;
67
+    int err;
67 68
 
68
-  MSPACK_SYS_SELFTEST(err);
69
-  if (err) return 0;
69
+    MSPACK_SYS_SELFTEST(err);
70
+    if (err) return 0;
70 71
 
71
-  if ((cabd = mspack_create_cab_decompressor(NULL))) {
72
-    for (argv++; *argv; argv++) {
73
-      if ((cab = cabd->search(cabd, *argv))) {
74
-	for (c = cab; c; c = c->next) rip(*argv, c->base_offset, c->length);
75
-	cabd->close(cabd, cab);
76
-      }
72
+    if ((cabd = mspack_create_cab_decompressor(NULL))) {
73
+        cabd->set_param(cabd, MSCABD_PARAM_SALVAGE, 1);
74
+        for (argv++; *argv; argv++) {
75
+            if ((cab = cabd->search(cabd, *argv))) {
76
+                for (c = cab; c; c = c->next) {
77
+                    rip(*argv, c->base_offset, c->length);
78
+                }
79
+                cabd->close(cabd, cab);
80
+            }
81
+        }
82
+        mspack_destroy_cab_decompressor(cabd);
77 83
     }
78
-    mspack_destroy_cab_decompressor(cabd);
79
-  }
80
-  return 0;
84
+    return 0;
81 85
 }
... ...
@@ -25,8 +25,6 @@
25 25
 
26 26
 mode_t user_umask;
27 27
 
28
-#define FILENAME ".test.chmx"
29
-
30 28
 /**
31 29
  * Ensures that all directory components in a filepath exist. New directory
32 30
  * components are created, if necessary.
... ...
@@ -51,126 +49,22 @@ static int ensure_filepath(char *path) {
51 51
   return 1;
52 52
 }
53 53
 
54
-/**
55
- * Creates a UNIX filename from the internal CAB filename and the given
56
- * parameters.
57
- *
58
- * @param fname  the internal CAB filename.
59
- * @param dir    a directory path to prepend to the output filename.
60
- * @param lower  if non-zero, filename should be made lower-case.
61
- * @param isunix if zero, MS-DOS path seperators are used in the internal
62
- *               CAB filename. If non-zero, UNIX path seperators are used.
63
- * @param utf8   if non-zero, the internal CAB filename is encoded in UTF8.
64
- * @return a freshly allocated and created filename, or NULL if there was
65
- *         not enough memory.
66
- * @see unix_path_seperators()
67
- */
68
-static char *create_output_name(unsigned char *fname, unsigned char *dir,
69
-			 int lower, int isunix, int utf8)
70
-{
71
-  unsigned char *p, *name, c, *fe, sep, slash;
72
-  unsigned int x;
73
-
74
-  sep   = (isunix) ? '/'  : '\\'; /* the path-seperator */
75
-  slash = (isunix) ? '\\' : '/';  /* the other slash */
76
-
77
-  /* length of filename */
78
-  x = strlen((char *) fname);
79
-  /* UTF8 worst case scenario: tolower() expands all chars from 1 to 3 bytes */
80
-  if (utf8) x *= 3;
81
-  /* length of output directory */
82
-  if (dir) x += strlen((char *) dir);
83
-
84
-  if (!(name = (unsigned char *) malloc(x + 2))) {
85
-    fprintf(stderr, "out of memory!\n");
86
-    return NULL;
87
-  }
88
-  
89
-  /* start with blank name */
90
-  *name = '\0';
91
-
92
-  /* add output directory if needed */
93
-  if (dir) {
94
-    strcpy((char *) name, (char *) dir);
95
-    strcat((char *) name, "/");
96
-  }
97
-
98
-  /* remove leading slashes */
99
-  while (*fname == sep) fname++;
100
-
101
-  /* copy from fi->filename to new name, converting MS-DOS slashes to UNIX
102
-   * slashes as we go. Also lowercases characters if needed.
103
-   */
104
-  p = &name[strlen((char *)name)];
105
-  fe = &fname[strlen((char *)fname)];
106
-
107
-  if (utf8) {
108
-    /* UTF8 translates two-byte unicode characters into 1, 2 or 3 bytes.
109
-     * %000000000xxxxxxx -> %0xxxxxxx
110
-     * %00000xxxxxyyyyyy -> %110xxxxx %10yyyyyy
111
-     * %xxxxyyyyyyzzzzzz -> %1110xxxx %10yyyyyy %10zzzzzz
112
-     *
113
-     * Therefore, the inverse is as follows:
114
-     * First char:
115
-     *  0x00 - 0x7F = one byte char
116
-     *  0x80 - 0xBF = invalid
117
-     *  0xC0 - 0xDF = 2 byte char (next char only 0x80-0xBF is valid)
118
-     *  0xE0 - 0xEF = 3 byte char (next 2 chars only 0x80-0xBF is valid)
119
-     *  0xF0 - 0xFF = invalid
120
-     */
121
-    do {
122
-      if (fname >= fe) {
123
-	free(name);
124
-	return NULL;
125
-      }
126
-
127
-      /* get next UTF8 char */
128
-      if ((c = *fname++) < 0x80) x = c;
129
-      else {
130
-	if ((c >= 0xC0) && (c < 0xE0)) {
131
-	  x = (c & 0x1F) << 6;
132
-	  x |= *fname++ & 0x3F;
133
-	}
134
-	else if ((c >= 0xE0) && (c < 0xF0)) {
135
-	  x = (c & 0xF) << 12;
136
-	  x |= (*fname++ & 0x3F) << 6;
137
-	  x |= *fname++ & 0x3F;
138
-	}
139
-	else x = '?';
140
-      }
141
-
142
-      /* whatever is the path seperator -> '/'
143
-       * whatever is the other slash    -> '\\'
144
-       * otherwise, if lower is set, the lowercase version */
145
-      if      (x == sep)   x = '/';
146
-      else if (x == slash) x = '\\';
147
-      else if (lower)      x = (unsigned int) tolower((int) x);
148
-
149
-      /* integer back to UTF8 */
150
-      if (x < 0x80) {
151
-	*p++ = (unsigned char) x;
152
-      }
153
-      else if (x < 0x800) {
154
-	*p++ = 0xC0 | (x >> 6);   
155
-	*p++ = 0x80 | (x & 0x3F);
156
-      }
157
-      else {
158
-	*p++ = 0xE0 | (x >> 12);
159
-	*p++ = 0x80 | ((x >> 6) & 0x3F);
160
-	*p++ = 0x80 | (x & 0x3F);
161
-      }
162
-    } while (x);
163
-  }
164
-  else {
165
-    /* regular non-utf8 version */
166
-    do {
167
-      c = *fname++;
168
-      if      (c == sep)   c = '/';
169
-      else if (c == slash) c = '\\';
170
-      else if (lower)      c = (unsigned char) tolower((int) c);
171
-    } while ((*p++ = c));
172
-  }
173
-  return (char *) name;
54
+char *create_output_name(char *fname) {
55
+    char *out, *p;
56
+    if ((out = malloc(strlen(fname) + 1))) {
57
+        /* remove leading slashes */
58
+        while (*fname == '/' || *fname == '\\') fname++;
59
+        /* if that removes all characters, just call it "x" */
60
+        strcpy(out, (*fname) ? fname : "x");
61
+
62
+        /* change "../" to "xx/" */
63
+        for (p = out; *p; p++) {
64
+            if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\\')) {
65
+               p[0] = p[1] = 'x';
66
+            }
67
+        }
68
+    }
69
+    return out;
174 70
 }
175 71
 
176 72
 static int sortfunc(const void *a, const void *b) {
... ...
@@ -205,7 +99,7 @@ int main(int argc, char *argv[]) {
205 205
 	  qsort(f, numf, sizeof(struct mschmd_file *), &sortfunc);
206 206
 
207 207
 	  for (i = 0; i < numf; i++) {
208
-	    char *outname = create_output_name((unsigned char *)f[i]->filename,NULL,0,1,0);
208
+	    char *outname = create_output_name(f[i]->filename);
209 209
 	    printf("Extracting %s\n", outname);
210 210
 	    ensure_filepath(outname);
211 211
 	    if (chmd->extract(chmd, f[i], outname)) {
... ...
@@ -380,7 +380,8 @@ void cabd_extract_test_01() {
380 380
         "test_files/cabd/filename-read-violation-3.cab",
381 381
         "test_files/cabd/filename-read-violation-4.cab",
382 382
         "test_files/cabd/lzx-main-tree-no-lengths.cab",
383
-        "test_files/cabd/lzx-premature-matches.cab"
383
+        "test_files/cabd/lzx-premature-matches.cab",
384
+        "test_files/cabd/qtm-max-size-block.cab"
384 385
     };
385 386
 
386 387
     cabd = mspack_create_cab_decompressor(NULL);
... ...
@@ -34,6 +34,32 @@ void chmd_open_test_01() {
34 34
     mspack_destroy_chm_decompressor(chmd);
35 35
 }
36 36
 
37
+/* check no files are returned with blank filenames */
38
+void chmd_open_test_02() {
39
+    struct mschm_decompressor *chmd;
40
+    struct mschmd_header *chm;
41
+    struct mschmd_file *f;
42
+    unsigned int i;
43
+    const char *files[] = {
44
+        "test_files/chmd/blank-filenames.chm"
45
+    };
46
+
47
+    chmd =  mspack_create_chm_decompressor(NULL);
48
+    TEST(chmd != NULL);
49
+    for (i = 0; i < (sizeof(files)/sizeof(char *)); i++) {
50
+        chm = chmd->open(chmd, files[i]);
51
+        TEST(chm != NULL);
52
+        for (f = chm->files; f; f = f->next) {
53
+            TEST(f->filename && f->filename[0]);
54
+        }
55
+        for (f = chm->sysfiles; f; f = f->next) {
56
+            TEST(f->filename && f->filename[0]);
57
+        }
58
+    }
59
+    mspack_destroy_chm_decompressor(chmd);
60
+}
61
+
62
+
37 63
 /* check searching bad files doesn't crash */
38 64
 void chmd_search_test_01() {
39 65
     struct mschm_decompressor *chmd;
... ...
@@ -95,6 +121,7 @@ int main() {
95 95
   TEST(selftest == MSPACK_ERR_OK);
96 96
 
97 97
   chmd_open_test_01();
98
+  chmd_open_test_02();
98 99
   chmd_search_test_01();
99 100
   chmd_extract_test_01();
100 101
 
101 102
new file mode 100644
102 103
Binary files /dev/null and b/libclammspack/test/test_files/cabd/qtm-max-size-block.cab differ
103 104
new file mode 100644
104 105
Binary files /dev/null and b/libclammspack/test/test_files/chmd/blank-filenames.chm differ