diff options
author | Marc-André Lureau <marcandre.lureau@gmail.com> | 2022-08-02 06:39:52 +0000 |
---|---|---|
committer | Marc-André Lureau <marcandre.lureau@gmail.com> | 2022-08-02 06:39:52 +0000 |
commit | 77e99e92e21376e4f6e7e41ad33462ba65281d0d (patch) | |
tree | 3f9f4bf12251e73269ba03286ebce40a15f515a5 | |
parent | 81ff4cc532046b9bd4277ab9452634a9638c31f3 (diff) | |
parent | 6b90d6204bd7a0d213833dc38eb4c5bcdb945466 (diff) | |
download | gcab-77e99e92e21376e4f6e7e41ad33462ba65281d0d.tar.gz |
Merge branch 'gcap-fix' into 'master'
Fix for zip compression errors
See merge request GNOME/gcab!12
-rw-r--r-- | libgcab/cabinet.c | 111 | ||||
-rw-r--r-- | libgcab/cabinet.h | 9 | ||||
-rw-r--r-- | libgcab/decomp.h | 8 | ||||
-rw-r--r-- | libgcab/gcab-cabinet.c | 14 | ||||
-rw-r--r-- | libgcab/gcab-folder.c | 15 | ||||
-rw-r--r-- | libgcab/gcab-priv.h | 1 |
6 files changed, 76 insertions, 82 deletions
diff --git a/libgcab/cabinet.c b/libgcab/cabinet.c index 6a7ce99..6241904 100644 --- a/libgcab/cabinet.c +++ b/libgcab/cabinet.c @@ -34,53 +34,6 @@ zfree (voidpf opaque, voidpf address) g_free (address); } -static gboolean -cdata_set (cdata_t *cd, int type, guint8 *data, size_t size, GError **error) -{ - if (type > GCAB_COMPRESSION_MSZIP) { - g_critical ("unsupported compression method %d", type); - return FALSE; - } - - cd->nubytes = size; - - if (type == 0) { - memcpy (cd->in, data, size); - cd->ncbytes = size; - } - - if (type == GCAB_COMPRESSION_MSZIP) { - z_stream stream = { 0, }; - int zret; - - stream.zalloc = zalloc; - stream.zfree = zfree; - zret = deflateInit2 (&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); - if (zret != Z_OK) { - g_set_error (error, GCAB_ERROR, GCAB_ERROR_FAILED, - "zlib deflateInit2 failed: %s", zError (zret)); - return FALSE; - } - stream.next_in = data; - stream.avail_in = size; - stream.next_out = cd->in + 2; - stream.avail_out = sizeof (cd->in) - 2; - /* insert the signature */ - cd->in[0] = 'C'; - cd->in[1] = 'K'; - zret = deflate (&stream, Z_FINISH); - if (zret != Z_OK && zret != Z_STREAM_END) { - g_set_error (error, GCAB_ERROR, GCAB_ERROR_FAILED, - "zlib deflate failed: %s", zError (zret)); - return FALSE; - } - deflateEnd (&stream); - cd->ncbytes = stream.total_out + 2; - } - - return TRUE; -} - static char * _data_input_stream_read_until (GDataInputStream *stream, const gchar *stop_chars, @@ -473,13 +426,71 @@ compute_checksum (guint8 *in, guint16 ncbytes, guint32 seed) return csum; } +static gboolean +cdata_deflate_block (cdata_t *cd, guint8 *data, size_t size, int level, + GError **error) +{ + z_stream deflate_stream = { 0, }; + int zret; + + deflate_stream.zalloc = zalloc; + deflate_stream.zfree = zfree; + zret = deflateInit2 (&deflate_stream, level, Z_DEFLATED, -15, 8, + Z_DEFAULT_STRATEGY); + if (zret != Z_OK) { + g_set_error (error, GCAB_ERROR, GCAB_ERROR_FAILED, + "zlib deflateInit2 failed: %s", zError (zret)); + return FALSE; + } + deflate_stream.next_in = data; + deflate_stream.avail_in = size; + deflate_stream.next_out = cd->in + 2; + deflate_stream.avail_out = sizeof (cd->in) - 2; + /* insert the signature */ + cd->in[0] = 'C'; + cd->in[1] = 'K'; + zret = deflate (&deflate_stream, Z_FINISH); + if (zret != Z_OK && zret != Z_STREAM_END) { + g_set_error (error, GCAB_ERROR, GCAB_ERROR_FAILED, + "zlib deflate failed: %s", zError (zret)); + return FALSE; + } + deflateEnd (&deflate_stream); + + cd->ncbytes = deflate_stream.total_out + 2; + cd->nubytes = size; + + return TRUE; +} + G_GNUC_INTERNAL gboolean cdata_write (cdata_t *cd, GDataOutputStream *out, int type, guint8 *data, size_t size, gsize *bytes_written, GCancellable *cancellable, GError **error) { - if (!cdata_set(cd, type, data, size, error)) + switch (type) { + case GCAB_COMPRESSION_NONE: + memcpy (cd->in, data, size); + cd->ncbytes = size; + cd->nubytes = size; + break; + + case GCAB_COMPRESSION_MSZIP: + if (!cdata_deflate_block (cd, data, size, Z_DEFAULT_COMPRESSION, error)) + return FALSE; + + if (cd->ncbytes >= CAB_MAX_MSZIP_BLOCK_SIZE) { + /* if the compressor inflated the data, store it uncompressed */ + if (!cdata_deflate_block (cd, data, size, Z_NO_COMPRESSION, error)) + return FALSE; + } + + break; + + default: + g_critical ("unsupported compression method %d", type); return FALSE; + } guint32 datacsum = compute_checksum(cd->in, cd->ncbytes, 0); guint8 sizecsum[4]; @@ -577,11 +588,11 @@ cdata_read (cdata_t *cd, guint8 res_data, gint comptype, return FALSE; } R2 (cd->nubytes); - if (cd->nubytes > CAB_BLOCKMAX) { + if (cd->nubytes > CAB_MAX_BLOCK_SIZE) { g_set_error (error, GCAB_ERROR, GCAB_ERROR_INVALID_DATA, "CDATA block of %" G_GUINT16_FORMAT " bytes " "was bigger than maximum size %i", - cd->nubytes, CAB_BLOCKMAX); + cd->nubytes, CAB_MAX_BLOCK_SIZE); return FALSE; } RN (cd->reserved, res_data); diff --git a/libgcab/cabinet.h b/libgcab/cabinet.h index 850ac65..5470b22 100644 --- a/libgcab/cabinet.h +++ b/libgcab/cabinet.h @@ -40,7 +40,10 @@ /* based on the spec http://msdn.microsoft.com/en-us/library/bb417343.aspx */ -#define DATABLOCKSIZE 32768 +#define CAB_MAX_BLOCK_SIZE (32768) +#define CAB_MAX_MSZIP_BLOCK_SIZE (32768 + 12) +#define CAB_MAX_LZX_BLOCK_SIZE (32768 + 6144) +#define CAB_MAX_COMPRESSED_BLOCK_SIZE (CAB_MAX_LZX_BLOCK_SIZE) #define CFO_START 0x24 /* folder offset */ #define CFI_START 0x2C /* file offset */ @@ -100,8 +103,8 @@ typedef struct guint16 ncbytes; guint16 nubytes; guint8 *reserved; - guint8 in[CAB_INPUTMAX+2]; - guint8 out[CAB_BLOCKMAX]; + guint8 in[CAB_MAX_COMPRESSED_BLOCK_SIZE]; + guint8 out[CAB_MAX_BLOCK_SIZE]; /* using zlib */ z_stream z; /* using wine decomp.h */ diff --git a/libgcab/decomp.h b/libgcab/decomp.h index 041d60e..36f28d9 100644 --- a/libgcab/decomp.h +++ b/libgcab/decomp.h @@ -117,14 +117,6 @@ bitbuf = lb.bb; bitsleft = lb.bl; inpos = lb.ip; \ } while (0) -/* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed - * blocks have zero growth. MSZIP guarantees that it won't grow above - * uncompressed size by more than 12 bytes. LZX guarantees it won't grow - * more than 6144 bytes. - */ -#define CAB_BLOCKMAX (32768) -#define CAB_INPUTMAX (CAB_BLOCKMAX+6144) - typedef guint8 cab_UBYTE; /* 8 bits */ typedef guint16 cab_UWORD; /* 16 bits */ typedef guint32 cab_ULONG; /* 32 bits */ diff --git a/libgcab/gcab-cabinet.c b/libgcab/gcab-cabinet.c index edf5a98..10087e9 100644 --- a/libgcab/gcab-cabinet.c +++ b/libgcab/gcab-cabinet.c @@ -274,7 +274,7 @@ gcab_cabinet_write (GCabCabinet *self, g_autoptr(GDataOutputStream) dstream = NULL; gssize len, offset = 0; cdata_t block = { 0, }; - guint8 data[DATABLOCKSIZE]; + guint8 data[CAB_MAX_BLOCK_SIZE]; gsize written; size_t sumstr = 0; g_autoptr(GSList) files = NULL; @@ -300,7 +300,7 @@ gcab_cabinet_write (GCabCabinet *self, folder.typecomp = gcab_folder_get_comptype (cabfolder); folder.offsetdata = cheader->offsetfiles + nfiles * 16 + sumstr; - folder.ndatab = gcab_folder_get_ndatablocks (cabfolder); + folder.ndatab = 0; /* avoid seeking to allow growing output streams */ for (guint i = 0; i < folder.offsetdata; i++) @@ -319,10 +319,13 @@ gcab_cabinet_write (GCabCabinet *self, return FALSE; while ((len = g_input_stream_read (in, - &data[offset], DATABLOCKSIZE - offset, - cancellable, error)) == (DATABLOCKSIZE - offset)) { - if (!cdata_write (&block, dstream, folder.typecomp, data, DATABLOCKSIZE, &written, cancellable, error)) + &data[offset], CAB_MAX_BLOCK_SIZE - offset, + cancellable, error)) == + (CAB_MAX_BLOCK_SIZE - offset)) { + if (!cdata_write (&block, dstream, folder.typecomp, data, + CAB_MAX_BLOCK_SIZE, &written, cancellable, error)) return FALSE; + folder.ndatab++; cheader->size += written; offset = 0; } @@ -335,6 +338,7 @@ gcab_cabinet_write (GCabCabinet *self, if (offset != 0) { if (!cdata_write (&block, dstream, folder.typecomp, data, offset, &written, cancellable, error)) return FALSE; + folder.ndatab++; cheader->size += written; } diff --git a/libgcab/gcab-folder.c b/libgcab/gcab-folder.c index 67f2ca9..831c68c 100644 --- a/libgcab/gcab-folder.c +++ b/libgcab/gcab-folder.c @@ -158,21 +158,6 @@ gcab_folder_class_init (GCabFolderClass *klass) } -/* calculate the number of datablocks we will need: - cabinet files are written in blocks of 32768 bytes */ -G_GNUC_INTERNAL gsize -gcab_folder_get_ndatablocks (GCabFolder *self) -{ - gsize total_size = 0; - - for (GSList *l = self->files; l != NULL; l = l->next) { - GCabFile *file = GCAB_FILE (l->data); - total_size += gcab_file_get_usize (file); - } - - return total_size / DATABLOCKSIZE + 1 ; -} - /** * gcab_folder_get_comptype: * @cabfolder: a #GCabFolder diff --git a/libgcab/gcab-priv.h b/libgcab/gcab-priv.h index 6d200e5..c951214 100644 --- a/libgcab/gcab-priv.h +++ b/libgcab/gcab-priv.h @@ -51,7 +51,6 @@ GFile *gcab_file_get_gfile (GCabFile *file); cfile_t *gcab_file_get_cfile (GCabFile *file); void gcab_file_add_attribute (GCabFile *file, guint32 attribute); -gsize gcab_folder_get_ndatablocks (GCabFolder *folder); gboolean gcab_folder_extract (GCabFolder *self, GDataInputStream *data, GFile *path, |