ar: hopefully fix out-of-bounds read in get_header_ar()
[oweals/busybox.git] / archival / lzop.c
index 5f2744d91a330041f8c0eba751d0f64ebaa79c7a..fef8cdba368ba6c6e1d83789376741eaf3f54eff 100644 (file)
@@ -14,7 +14,7 @@
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
 
    "Minimalized" for busybox by Alain Knaff
 */
+//config:config LZOP
+//config:      bool "lzop (13 kb)"
+//config:      default y
+//config:      help
+//config:      Lzop compression/decompresion.
+//config:
+//config:config UNLZOP
+//config:      bool "unlzop (13 kb)"
+//config:      default n  # INCOMPAT: upstream lzop does not provide such tool
+//config:      help
+//config:      Lzop decompresion.
+//config:
+//config:config LZOPCAT
+//config:      bool "lzopcat (13 kb)"
+//config:      default n  # INCOMPAT: upstream lzop does not provide such tool
+//config:      help
+//config:      Alias to "lzop -dc".
+//config:
+//config:config LZOP_COMPR_HIGH
+//config:      bool "lzop compression levels 7,8,9 (not very useful)"
+//config:      default n
+//config:      depends on LZOP || UNLZOP || LZOPCAT
+//config:      help
+//config:      High levels (7,8,9) of lzop compression. These levels
+//config:      are actually slower than gzip at equivalent compression ratios
+//config:      and take up 3.2K of code.
+
+//applet:IF_LZOP(APPLET(lzop, BB_DIR_BIN, BB_SUID_DROP))
+//                  APPLET_ODDNAME:name     main  location        suid_type     help
+//applet:IF_UNLZOP( APPLET_ODDNAME(unlzop,  lzop, BB_DIR_USR_BIN, BB_SUID_DROP, unlzop))
+//applet:IF_LZOPCAT(APPLET_ODDNAME(lzopcat, lzop, BB_DIR_USR_BIN, BB_SUID_DROP, lzopcat))
+
+//kbuild:lib-$(CONFIG_LZOP) += lzop.o
+//kbuild:lib-$(CONFIG_UNLZOP) += lzop.o
+//kbuild:lib-$(CONFIG_LZOPCAT) += lzop.o
+
+//usage:#define lzop_trivial_usage
+//usage:       "[-cfUvd123456789CF] [FILE]..."
+//usage:#define lzop_full_usage "\n\n"
+//usage:       "       -1..9   Compression level"
+//usage:     "\n       -d      Decompress"
+//usage:     "\n       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+//usage:     "\n       -U      Delete input files"
+///////:     "\n       -k      Keep input files" (default, so why bother documenting?)
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -F      Don't store or verify checksum"
+//usage:     "\n       -C      Also write checksum of compressed block"
+//usage:
+//usage:#define lzopcat_trivial_usage
+//usage:       "[-vF] [FILE]..."
+//usage:#define lzopcat_full_usage "\n\n"
+//usage:       "       -v      Verbose"
+//usage:     "\n       -F      Don't verify checksum"
+//usage:
+//usage:#define unlzop_trivial_usage
+//usage:       "[-cfUvF] [FILE]..."
+//usage:#define unlzop_full_usage "\n\n"
+//usage:       "       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+//usage:     "\n       -U      Delete input files"
+///////:     "\n       -k      Keep input files" (default, so why bother documenting?)
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -F      Don't verify checksum"
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "common_bufsiz.h"
+#include "bb_archive.h"
 #include "liblzo_interface.h"
 
 /* lzo-2.03/src/lzo_ptr.h */
@@ -76,8 +141,7 @@ static void copy3(uint8_t* ip, const uint8_t* m_pos, unsigned off)
 #define TEST_OP                (op <= op_end)
 
 static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
-               uint8_t *out, unsigned *out_len,
-               void* wrkmem UNUSED_PARAM)
+               uint8_t *out, unsigned *out_len /*, void* wrkmem */)
 {
        uint8_t* op;
        uint8_t* ip;
@@ -91,7 +155,7 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
        unsigned nl;
        unsigned long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0;
 
-//       LZO_UNUSED(wrkmem);
+//     LZO_UNUSED(wrkmem);
 
        *out_len = 0;
 
@@ -166,14 +230,17 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
                                        o_m1_a++;
                                }
                                /* test if a literal run follows */
-                               else if (nl == 0 && ip[0] < 16 && ip[0] != 0 &&
-                                                (lit + 2 + ip[0] < 16))
-                               {
+                               else
+                               if (nl == 0
+                                && ip[0] < 16
+                                && ip[0] != 0
+                                && (lit + 2 + ip[0] < 16)
+                               ) {
                                        t = *ip++;
                                        /* remove short run */
                                        *litp &= ~3;
                                        /* copy over the 2 literals that replace the match */
-                                       copy2(ip-3+1,m_pos,pd(op,m_pos));
+                                       copy2(ip-3+1, m_pos, pd(op, m_pos));
                                        /* move literals 1 byte ahead */
                                        litp += 2;
                                        if (lit > 0)
@@ -183,7 +250,8 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
                                        *litp = (unsigned char)(lit - 3);
 
                                        o_m1_b++;
-                                       *op++ = *m_pos++; *op++ = *m_pos++;
+                                       *op++ = *m_pos++;
+                                       *op++ = *m_pos++;
                                        goto copy_literal_run;
                                }
  copy_m1:
@@ -212,7 +280,7 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
                                        ) {
                                                t = *ip++;
                                                /* copy over the 3 literals that replace the match */
-                                               copy3(ip-1-2,m_pos,pd(op,m_pos));
+                                               copy3(ip-1-2, m_pos, pd(op, m_pos));
                                                /* set new length of previous literal run */
                                                lit += 3 + t + 3;
                                                *litp = (unsigned char)(lit - 3);
@@ -261,7 +329,7 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
                                                lit += 3;
                                                *litp = (unsigned char)((*litp & ~3) | lit);
                                                /* copy over the 3 literals that replace the match */
-                                               copy3(ip-3,m_pos,pd(op,m_pos));
+                                               copy3(ip-3, m_pos, pd(op, m_pos));
                                                o_m3_a++;
                                        }
                                        /* test if a literal run follows */
@@ -272,7 +340,7 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
                                                /* remove short run */
                                                *litp &= ~3;
                                                /* copy over the 3 literals that replace the match */
-                                               copy3(ip-4+1,m_pos,pd(op,m_pos));
+                                               copy3(ip-4+1, m_pos, pd(op, m_pos));
                                                /* move literals 1 byte ahead */
                                                litp += 2;
                                                if (lit > 0)
@@ -317,11 +385,11 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
        return LZO_E_EOF_NOT_FOUND;
 
  eof_found:
-//       LZO_UNUSED(o_m1_a); LZO_UNUSED(o_m1_b); LZO_UNUSED(o_m2);
-//       LZO_UNUSED(o_m3_a); LZO_UNUSED(o_m3_b);
+//     LZO_UNUSED(o_m1_a); LZO_UNUSED(o_m1_b); LZO_UNUSED(o_m2);
+//     LZO_UNUSED(o_m3_a); LZO_UNUSED(o_m3_b);
        *out_len = pd(op, out);
        return (ip == ip_end ? LZO_E_OK :
-                  (ip < ip_end  ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+               (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
 }
 
 /**********************************************************************/
@@ -390,15 +458,15 @@ typedef struct header_t {
 } header_t;
 
 struct globals {
-       const uint32_t *lzo_crc32_table;
+       /*const uint32_t *lzo_crc32_table;*/
        chksum_t chksum_in;
        chksum_t chksum_out;
-};
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+} FIX_ALIASING;
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
 //#define G (*ptr_to_globals)
 //#define INIT_G() do {
-//        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G)));
+//     SET_PTR_TO_GLOBALS(xzalloc(sizeof(G)));
 //} while (0)
 
 
@@ -407,25 +475,33 @@ struct globals {
 //#define LZOP_VERSION_STRING     "1.01"
 //#define LZOP_VERSION_DATE       "Apr 27th 2003"
 
-#define OPTION_STRING "cfvdt123456789CF"
+// lzop wants to be weird:
+// unlike all other compressosrs, its -k "keep" option is the default,
+// and -U is used to delete the source. We will invert the bit after getopt().
+#define OPTION_STRING "cfUvqdt123456789CFk"
 
+/* Note: must be kept in sync with archival/bbunzip.c */
 enum {
        OPT_STDOUT      = (1 << 0),
        OPT_FORCE       = (1 << 1),
-       OPT_VERBOSE     = (1 << 2),
-       OPT_DECOMPRESS  = (1 << 3),
-       OPT_TEST        = (1 << 4),
-       OPT_1           = (1 << 5),
-       OPT_2           = (1 << 6),
-       OPT_3           = (1 << 7),
-       OPT_4           = (1 << 8),
-       OPT_5           = (1 << 9),
-       OPT_6           = (1 << 10),
-       OPT_789         = (7 << 11),
-       OPT_7           = (1 << 11),
-       OPT_8           = (1 << 12),
-       OPT_C           = (1 << 14),
-       OPT_F           = (1 << 15),
+       OPT_KEEP        = (1 << 2),
+       OPT_VERBOSE     = (1 << 3),
+       OPT_QUIET       = (1 << 4),
+       OPT_DECOMPRESS  = (1 << 5),
+       OPT_TEST        = (1 << 6),
+       OPT_1           = (1 << 7),
+       OPT_2           = (1 << 8),
+       OPT_3           = (1 << 9),
+       OPT_4           = (1 << 10),
+       OPT_5           = (1 << 11),
+       OPT_6           = (1 << 12),
+       OPT_7           = (1 << 13),
+       OPT_8           = (1 << 14),
+       OPT_9           = (1 << 15),
+       OPT_C           = (1 << 16),
+       OPT_F           = (1 << 17),
+       OPT_k           = (1 << 18),
+       OPT_789         = OPT_7 | OPT_8 | OPT_9
 };
 
 /**********************************************************************/
@@ -465,19 +541,10 @@ lzo_adler32(uint32_t adler, const uint8_t* buf, unsigned len)
 static FAST_FUNC uint32_t
 lzo_crc32(uint32_t c, const uint8_t* buf, unsigned len)
 {
-       uint32_t crc;
-
-       if (buf == NULL)
-               return 0;
-
-       crc = ~c;
-       if (len != 0) do {
-       crc = G.lzo_crc32_table[((int)crc ^ *buf) & 0xff] ^ (crc >> 8);
-               buf += 1;
-               len -= 1;
-       } while (len > 0);
+       //if (buf == NULL) - impossible
+       //      return 0;
 
-       return ~crc;
+       return ~crc32_block_endian0(~c, buf, len, global_crc32_table);
 }
 
 /**********************************************************************/
@@ -598,7 +665,7 @@ static int lzo_get_method(header_t *h)
 /**********************************************************************/
 // compress a file
 /**********************************************************************/
-static smallint lzo_compress(const header_t *h)
+static NOINLINE int lzo_compress(const header_t *h)
 {
        unsigned block_size = LZO_BLOCK_SIZE;
        int r = 0; /* LZO_E_OK */
@@ -608,7 +675,6 @@ static smallint lzo_compress(const header_t *h)
        uint32_t d_adler32 = ADLER32_INIT_VALUE;
        uint32_t d_crc32 = CRC32_INIT_VALUE;
        int l;
-       smallint ok = 1;
        uint8_t *wrk_mem = NULL;
 
        if (h->method == M_LZO1X_1)
@@ -657,7 +723,7 @@ static smallint lzo_compress(const header_t *h)
                        /* optimize */
                        if (h->method == M_LZO1X_999) {
                                unsigned new_len = src_len;
-                               r = lzo1x_optimize(b2, dst_len, b1, &new_len, NULL);
+                               r = lzo1x_optimize(b2, dst_len, b1, &new_len /*, NULL*/);
                                if (r != 0 /*LZO_E_OK*/ || new_len != src_len)
                                        bb_error_msg_and_die("internal error - optimization failed");
                        }
@@ -676,8 +742,7 @@ static smallint lzo_compress(const header_t *h)
                if (dst_len < src_len) {
                        /* write checksum of compressed block */
                        if (h->flags & F_ADLER32_C)
-                               write32(lzo_adler32(ADLER32_INIT_VALUE, b2,
-                                                       dst_len));
+                               write32(lzo_adler32(ADLER32_INIT_VALUE, b2, dst_len));
                        if (h->flags & F_CRC32_C)
                                write32(lzo_crc32(CRC32_INIT_VALUE, b2, dst_len));
                        /* write compressed block data */
@@ -691,13 +756,19 @@ static smallint lzo_compress(const header_t *h)
        free(wrk_mem);
        free(b1);
        free(b2);
-       return ok;
+       return 1;
 }
 
-static void lzo_check(uint32_t FAST_FUNC (*fn)(uint32_t, const uint8_t*, unsigned),
-               uint32_t ref, uint32_t init,
-               uint8_t* buf, unsigned len)
+static FAST_FUNC void lzo_check(
+               uint32_t init,
+               uint8_t* buf, unsigned len,
+               uint32_t FAST_FUNC (*fn)(uint32_t, const uint8_t*, unsigned),
+               uint32_t ref)
 {
+       /* This function, by having the same order of parameters
+        * as fn, and by being marked FAST_FUNC (same as fn),
+        * saves a dozen bytes of code.
+        */
        uint32_t c = fn(init, buf, len);
        if (c != ref)
                bb_error_msg_and_die("checksum error");
@@ -706,7 +777,7 @@ static void lzo_check(uint32_t FAST_FUNC (*fn)(uint32_t, const uint8_t*, unsigne
 /**********************************************************************/
 // decompress a file
 /**********************************************************************/
-static smallint lzo_decompress(const header_t *h)
+static NOINLINE int lzo_decompress(const header_t *h)
 {
        unsigned block_size = LZO_BLOCK_SIZE;
        int r;
@@ -714,7 +785,6 @@ static smallint lzo_decompress(const header_t *h)
        uint32_t c_adler32 = ADLER32_INIT_VALUE;
        uint32_t d_adler32 = ADLER32_INIT_VALUE;
        uint32_t c_crc32 = CRC32_INIT_VALUE, d_crc32 = CRC32_INIT_VALUE;
-       smallint ok = 1;
        uint8_t *b1;
        uint32_t mcs_block_size = MAX_COMPRESSED_SIZE(block_size);
        uint8_t *b2 = NULL;
@@ -735,18 +805,17 @@ static smallint lzo_decompress(const header_t *h)
                        bb_error_msg_and_die("this file is a split lzop file");
 
                if (dst_len > MAX_BLOCK_SIZE)
-                       bb_error_msg_and_die("lzop file corrupted");
+                       bb_error_msg_and_die("corrupted data");
 
                /* read compressed block size */
                src_len = read32();
                if (src_len <= 0 || src_len > dst_len)
-                       bb_error_msg_and_die("lzop file corrupted");
+                       bb_error_msg_and_die("corrupted data");
 
                if (dst_len > block_size) {
                        if (b2) {
-//FIXME!
-                               b2 = NULL;
                                free(b2);
+                               b2 = NULL;
                        }
                        block_size = dst_len;
                        mcs_block_size = MAX_COMPRESSED_SIZE(block_size);
@@ -778,23 +847,23 @@ static smallint lzo_decompress(const header_t *h)
                        if (!(option_mask32 & OPT_F)) {
                                /* verify checksum of compressed block */
                                if (h->flags & F_ADLER32_C)
-                                       lzo_check(lzo_adler32, c_adler32,
-                                                       ADLER32_INIT_VALUE,
-                                                       b1, src_len);
+                                       lzo_check(ADLER32_INIT_VALUE,
+                                                       b1, src_len,
+                                                       lzo_adler32, c_adler32);
                                if (h->flags & F_CRC32_C)
-                                       lzo_check(lzo_crc32, c_crc32,
-                                                       CRC32_INIT_VALUE,
-                                                       b1, src_len);
+                                       lzo_check(CRC32_INIT_VALUE,
+                                                       b1, src_len,
+                                                       lzo_crc32, c_crc32);
                        }
 
                        /* decompress */
 //                     if (option_mask32 & OPT_F)
-//                             r = lzo1x_decompress(b1, src_len, b2, &d, NULL);
+//                             r = lzo1x_decompress(b1, src_len, b2, &d /*, NULL*/);
 //                     else
-                               r = lzo1x_decompress_safe(b1, src_len, b2, &d, NULL);
+                               r = lzo1x_decompress_safe(b1, src_len, b2, &d /*, NULL*/);
 
                        if (r != 0 /*LZO_E_OK*/ || dst_len != d) {
-                               bb_error_msg_and_die("corrupted compressed data");
+                               bb_error_msg_and_die("corrupted data");
                        }
                        dst = b2;
                } else {
@@ -805,11 +874,13 @@ static smallint lzo_decompress(const header_t *h)
                if (!(option_mask32 & OPT_F)) {
                        /* verify checksum of uncompressed block */
                        if (h->flags & F_ADLER32_D)
-                               lzo_check(lzo_adler32, d_adler32, ADLER32_INIT_VALUE,
-                                         dst, dst_len);
+                               lzo_check(ADLER32_INIT_VALUE,
+                                       dst, dst_len,
+                                       lzo_adler32, d_adler32);
                        if (h->flags & F_CRC32_D)
-                               lzo_check(lzo_crc32, d_crc32, CRC32_INIT_VALUE,
-                                         dst, dst_len);
+                               lzo_check(CRC32_INIT_VALUE,
+                                       dst, dst_len,
+                                       lzo_crc32, d_crc32);
                }
 
                /* write uncompressed block data */
@@ -817,7 +888,7 @@ static smallint lzo_decompress(const header_t *h)
        }
 
        free(b2);
-       return ok;
+       return 1;
 }
 
 /**********************************************************************/
@@ -849,7 +920,7 @@ static smallint lzo_decompress(const header_t *h)
  *                  chksum_out
  * The rest is identical.
 */
-static const unsigned char lzop_magic[9] = {
+static const unsigned char lzop_magic[9] ALIGN1 = {
        0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a
 };
 
@@ -1002,7 +1073,7 @@ static void lzo_set_method(header_t *h)
        h->level = level;
 }
 
-static smallint do_lzo_compress(void)
+static int do_lzo_compress(void)
 {
        header_t header;
 
@@ -1030,7 +1101,7 @@ static smallint do_lzo_compress(void)
 /**********************************************************************/
 // decompress
 /**********************************************************************/
-static smallint do_lzo_decompress(void)
+static int do_lzo_decompress(void)
 {
        header_t header;
 
@@ -1039,7 +1110,7 @@ static smallint do_lzo_decompress(void)
        return lzo_decompress(&header);
 }
 
-static char* make_new_name_lzop(char *filename)
+static char* FAST_FUNC make_new_name_lzop(char *filename, const char *expected_ext UNUSED_PARAM)
 {
        if (option_mask32 & OPT_DECOMPRESS) {
                char *extension = strrchr(filename, '.');
@@ -1051,7 +1122,7 @@ static char* make_new_name_lzop(char *filename)
        return xasprintf("%s.lzo", filename);
 }
 
-static IF_DESKTOP(long long) int pack_lzop(unpack_info_t *info UNUSED_PARAM)
+static IF_DESKTOP(long long) int FAST_FUNC pack_lzop(transformer_state_t *xstate UNUSED_PARAM)
 {
        if (option_mask32 & OPT_DECOMPRESS)
                return do_lzo_decompress();
@@ -1063,13 +1134,20 @@ int lzop_main(int argc UNUSED_PARAM, char **argv)
 {
        getopt32(argv, OPTION_STRING);
        argv += optind;
+       /* -U is "anti -k", invert bit for bbunpack(): */
+       option_mask32 ^= OPT_KEEP;
+       /* -k disables -U (if any): */
+       /* opt_complementary "k-U"? - nope, only handles -Uk, not -kU */
+       if (option_mask32 & OPT_k)
+               option_mask32 |= OPT_KEEP;
+
        /* lzopcat? */
-       if (applet_name[4] == 'c')
+       if (ENABLE_LZOPCAT && applet_name[4] == 'c')
                option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS);
        /* unlzop? */
-       if (applet_name[0] == 'u')
+       if (ENABLE_UNLZOP && applet_name[4] == 'o')
                option_mask32 |= OPT_DECOMPRESS;
 
-       G.lzo_crc32_table = crc32_filltable(NULL, 0);
-       return bbunpack(argv, make_new_name_lzop, pack_lzop);
+       global_crc32_new_table_le();
+       return bbunpack(argv, pack_lzop, make_new_name_lzop, /*unused:*/ NULL);
 }