summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@gmail.com>2022-08-02 06:39:52 +0000
committerMarc-André Lureau <marcandre.lureau@gmail.com>2022-08-02 06:39:52 +0000
commit77e99e92e21376e4f6e7e41ad33462ba65281d0d (patch)
tree3f9f4bf12251e73269ba03286ebce40a15f515a5
parent81ff4cc532046b9bd4277ab9452634a9638c31f3 (diff)
parent6b90d6204bd7a0d213833dc38eb4c5bcdb945466 (diff)
downloadgcab-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.c111
-rw-r--r--libgcab/cabinet.h9
-rw-r--r--libgcab/decomp.h8
-rw-r--r--libgcab/gcab-cabinet.c14
-rw-r--r--libgcab/gcab-folder.c15
-rw-r--r--libgcab/gcab-priv.h1
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,