2020-02-14 11:53:29 +00:00
|
|
|
/* io_gunzip.c - Alpine Package Keeper (APK)
|
2008-11-07 15:11:08 +00:00
|
|
|
*
|
2011-09-13 08:53:01 +00:00
|
|
|
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
|
2008-11-07 15:11:08 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
2020-04-22 13:33:41 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
2008-11-07 15:11:08 +00:00
|
|
|
*/
|
|
|
|
|
2009-07-22 11:56:27 +00:00
|
|
|
#include <errno.h>
|
2008-11-07 15:11:08 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
#include <zlib.h>
|
|
|
|
|
|
|
|
#include "apk_defines.h"
|
|
|
|
#include "apk_io.h"
|
|
|
|
|
|
|
|
struct apk_gzip_istream {
|
|
|
|
struct apk_istream is;
|
2020-01-11 07:16:38 +00:00
|
|
|
struct apk_istream *zis;
|
2008-11-07 15:11:08 +00:00
|
|
|
z_stream zs;
|
2009-07-13 17:37:03 +00:00
|
|
|
|
|
|
|
apk_multipart_cb cb;
|
|
|
|
void *cbctx;
|
2009-07-17 12:56:09 +00:00
|
|
|
void *cbprev;
|
2010-12-17 07:36:19 +00:00
|
|
|
apk_blob_t cbarg;
|
2008-11-07 15:11:08 +00:00
|
|
|
};
|
|
|
|
|
2019-12-18 08:00:29 +00:00
|
|
|
static void gzi_get_meta(struct apk_istream *is, struct apk_file_meta *meta)
|
2015-11-09 10:47:23 +00:00
|
|
|
{
|
2019-12-18 08:00:29 +00:00
|
|
|
struct apk_gzip_istream *gis = container_of(is, struct apk_gzip_istream, is);
|
2020-01-11 07:16:38 +00:00
|
|
|
apk_istream_get_meta(gis->zis, meta);
|
2015-11-09 10:47:23 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 16:49:22 +00:00
|
|
|
static int gzi_boundary_change(struct apk_gzip_istream *gis)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
2020-01-10 09:02:48 +00:00
|
|
|
r = gis->cb(gis->cbctx, gis->is.err ? APK_MPART_END : APK_MPART_BOUNDARY, gis->cbarg);
|
2018-09-05 16:49:22 +00:00
|
|
|
if (r > 0) r = -ECANCELED;
|
2020-01-10 09:02:48 +00:00
|
|
|
if (r != 0) gis->is.err = r;
|
2018-09-05 16:49:22 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2019-12-18 08:00:29 +00:00
|
|
|
static ssize_t gzi_read(struct apk_istream *is, void *ptr, size_t size)
|
2008-11-07 15:11:08 +00:00
|
|
|
{
|
2019-12-18 08:00:29 +00:00
|
|
|
struct apk_gzip_istream *gis = container_of(is, struct apk_gzip_istream, is);
|
2009-07-17 12:56:09 +00:00
|
|
|
int r;
|
2008-11-07 15:11:08 +00:00
|
|
|
|
|
|
|
gis->zs.avail_out = size;
|
|
|
|
gis->zs.next_out = ptr;
|
|
|
|
|
2020-01-10 09:02:48 +00:00
|
|
|
while (gis->zs.avail_out != 0 && gis->is.err == 0) {
|
2010-12-17 07:36:19 +00:00
|
|
|
if (!APK_BLOB_IS_NULL(gis->cbarg)) {
|
2018-09-05 16:49:22 +00:00
|
|
|
if (gzi_boundary_change(gis))
|
2010-12-17 07:36:19 +00:00
|
|
|
goto ret;
|
|
|
|
gis->cbarg = APK_BLOB_NULL;
|
|
|
|
}
|
2008-11-07 15:11:08 +00:00
|
|
|
if (gis->zs.avail_in == 0) {
|
2009-07-14 06:33:32 +00:00
|
|
|
apk_blob_t blob;
|
|
|
|
|
2009-07-22 08:36:55 +00:00
|
|
|
if (gis->cb != NULL && gis->cbprev != NULL &&
|
|
|
|
gis->cbprev != gis->zs.next_in) {
|
2009-07-17 12:56:09 +00:00
|
|
|
gis->cb(gis->cbctx, APK_MPART_DATA,
|
|
|
|
APK_BLOB_PTR_LEN(gis->cbprev,
|
|
|
|
(void *)gis->zs.next_in - gis->cbprev));
|
2009-07-13 17:37:03 +00:00
|
|
|
}
|
2020-01-11 07:16:38 +00:00
|
|
|
blob = apk_istream_get_all(gis->zis);
|
2009-07-17 12:56:09 +00:00
|
|
|
gis->cbprev = blob.ptr;
|
2009-07-14 06:33:32 +00:00
|
|
|
gis->zs.avail_in = blob.len;
|
2009-07-17 12:56:09 +00:00
|
|
|
gis->zs.next_in = (void *) gis->cbprev;
|
2010-12-09 08:47:09 +00:00
|
|
|
if (blob.len < 0) {
|
2020-01-10 09:02:48 +00:00
|
|
|
gis->is.err = blob.len;
|
2009-07-21 14:37:44 +00:00
|
|
|
goto ret;
|
2009-07-13 17:37:03 +00:00
|
|
|
} else if (gis->zs.avail_in == 0) {
|
2020-01-10 09:02:48 +00:00
|
|
|
gis->is.err = 1;
|
2018-09-05 16:49:22 +00:00
|
|
|
gis->cbarg = APK_BLOB_NULL;
|
|
|
|
gzi_boundary_change(gis);
|
2009-07-21 14:37:44 +00:00
|
|
|
goto ret;
|
2008-11-07 15:11:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-21 14:37:44 +00:00
|
|
|
r = inflate(&gis->zs, Z_NO_FLUSH);
|
|
|
|
switch (r) {
|
|
|
|
case Z_STREAM_END:
|
2009-07-13 17:37:03 +00:00
|
|
|
/* Digest the inflated bytes */
|
2020-01-11 07:16:38 +00:00
|
|
|
if (gis->zis->err && gis->zs.avail_in == 0)
|
|
|
|
gis->is.err = gis->zis->err;
|
2009-07-13 17:37:03 +00:00
|
|
|
if (gis->cb != NULL) {
|
2010-12-17 07:36:19 +00:00
|
|
|
gis->cbarg = APK_BLOB_PTR_LEN(gis->cbprev, (void *) gis->zs.next_in - gis->cbprev);
|
|
|
|
gis->cbprev = gis->zs.next_in;
|
|
|
|
}
|
|
|
|
/* If we hit end of the bitstream (not end
|
|
|
|
* of just this gzip), we need to do the
|
|
|
|
* callback here, as we won't be called again.
|
|
|
|
* For boundaries it should be postponed to not
|
|
|
|
* be called until next gzip read is started. */
|
2020-01-10 09:02:48 +00:00
|
|
|
if (gis->is.err) {
|
2018-09-05 16:49:22 +00:00
|
|
|
gzi_boundary_change(gis);
|
2009-07-22 16:56:13 +00:00
|
|
|
goto ret;
|
2010-12-17 07:36:19 +00:00
|
|
|
}
|
2009-07-10 10:52:44 +00:00
|
|
|
inflateEnd(&gis->zs);
|
|
|
|
if (inflateInit2(&gis->zs, 15+32) != Z_OK)
|
2009-07-22 11:56:27 +00:00
|
|
|
return -ENOMEM;
|
2020-01-10 09:02:48 +00:00
|
|
|
if (gis->cb) goto ret;
|
2009-07-21 14:37:44 +00:00
|
|
|
break;
|
|
|
|
case Z_OK:
|
|
|
|
break;
|
|
|
|
default:
|
2020-01-10 09:02:48 +00:00
|
|
|
gis->is.err = -EIO;
|
2009-07-21 14:37:44 +00:00
|
|
|
break;
|
2009-07-10 10:52:44 +00:00
|
|
|
}
|
2008-11-07 15:11:08 +00:00
|
|
|
}
|
|
|
|
|
2009-07-21 14:37:44 +00:00
|
|
|
ret:
|
2008-11-07 15:11:08 +00:00
|
|
|
return size - gis->zs.avail_out;
|
|
|
|
}
|
|
|
|
|
2019-12-18 08:00:29 +00:00
|
|
|
static void gzi_close(struct apk_istream *is)
|
2008-11-07 15:11:08 +00:00
|
|
|
{
|
2019-12-18 08:00:29 +00:00
|
|
|
struct apk_gzip_istream *gis = container_of(is, struct apk_gzip_istream, is);
|
2008-11-07 15:11:08 +00:00
|
|
|
|
|
|
|
inflateEnd(&gis->zs);
|
2020-01-11 07:16:38 +00:00
|
|
|
apk_istream_close(gis->zis);
|
2008-11-07 15:11:08 +00:00
|
|
|
free(gis);
|
|
|
|
}
|
|
|
|
|
2017-06-21 13:07:58 +00:00
|
|
|
static const struct apk_istream_ops gunzip_istream_ops = {
|
|
|
|
.get_meta = gzi_get_meta,
|
|
|
|
.read = gzi_read,
|
|
|
|
.close = gzi_close,
|
|
|
|
};
|
|
|
|
|
2020-01-11 07:16:38 +00:00
|
|
|
struct apk_istream *apk_istream_gunzip_mpart(struct apk_istream *is, apk_multipart_cb cb, void *ctx)
|
2008-11-07 15:11:08 +00:00
|
|
|
{
|
|
|
|
struct apk_gzip_istream *gis;
|
|
|
|
|
2020-01-11 07:16:38 +00:00
|
|
|
if (IS_ERR_OR_NULL(is)) return ERR_CAST(is);
|
2009-01-06 17:20:22 +00:00
|
|
|
|
2020-01-10 09:02:48 +00:00
|
|
|
gis = malloc(sizeof(*gis) + apk_io_bufsize);
|
2014-10-07 14:03:51 +00:00
|
|
|
if (!gis) goto err;
|
2008-11-07 15:11:08 +00:00
|
|
|
|
|
|
|
*gis = (struct apk_gzip_istream) {
|
2017-06-21 13:07:58 +00:00
|
|
|
.is.ops = &gunzip_istream_ops,
|
2020-01-10 09:02:48 +00:00
|
|
|
.is.buf = (uint8_t*)(gis + 1),
|
|
|
|
.is.buf_size = apk_io_bufsize,
|
2020-01-11 07:16:38 +00:00
|
|
|
.zis = is,
|
2009-07-13 17:37:03 +00:00
|
|
|
.cb = cb,
|
|
|
|
.cbctx = ctx,
|
2008-11-07 15:11:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if (inflateInit2(&gis->zs, 15+32) != Z_OK) {
|
|
|
|
free(gis);
|
2009-07-16 12:16:05 +00:00
|
|
|
goto err;
|
2008-11-07 15:11:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &gis->is;
|
2009-07-16 12:16:05 +00:00
|
|
|
err:
|
2020-01-11 07:16:38 +00:00
|
|
|
apk_istream_close(is);
|
2015-03-10 11:04:14 +00:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2009-07-16 12:16:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct apk_gzip_ostream {
|
|
|
|
struct apk_ostream os;
|
|
|
|
struct apk_ostream *output;
|
|
|
|
z_stream zs;
|
|
|
|
};
|
|
|
|
|
2019-12-18 08:00:29 +00:00
|
|
|
static ssize_t gzo_write(struct apk_ostream *os, const void *ptr, size_t size)
|
2009-07-16 12:16:05 +00:00
|
|
|
{
|
2019-12-18 08:00:29 +00:00
|
|
|
struct apk_gzip_ostream *gos = container_of(os, struct apk_gzip_ostream, os);
|
2009-07-17 11:06:43 +00:00
|
|
|
unsigned char buffer[1024];
|
2009-07-22 11:56:27 +00:00
|
|
|
ssize_t have, r;
|
2009-07-16 12:16:05 +00:00
|
|
|
|
|
|
|
gos->zs.avail_in = size;
|
|
|
|
gos->zs.next_in = (void *) ptr;
|
|
|
|
while (gos->zs.avail_in) {
|
2009-07-17 11:06:43 +00:00
|
|
|
gos->zs.avail_out = sizeof(buffer);
|
|
|
|
gos->zs.next_out = buffer;
|
2009-07-16 12:16:05 +00:00
|
|
|
r = deflate(&gos->zs, Z_NO_FLUSH);
|
|
|
|
if (r == Z_STREAM_ERROR)
|
2009-07-22 11:56:27 +00:00
|
|
|
return -EIO;
|
2009-07-17 11:06:43 +00:00
|
|
|
have = sizeof(buffer) - gos->zs.avail_out;
|
2009-07-16 12:16:05 +00:00
|
|
|
if (have != 0) {
|
2017-06-21 13:07:58 +00:00
|
|
|
r = apk_ostream_write(gos->output, buffer, have);
|
2009-07-16 12:16:05 +00:00
|
|
|
if (r != have)
|
2009-07-22 11:56:27 +00:00
|
|
|
return -EIO;
|
2009-07-16 12:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2019-12-18 08:00:29 +00:00
|
|
|
static int gzo_close(struct apk_ostream *os)
|
2009-07-16 12:16:05 +00:00
|
|
|
{
|
2019-12-18 08:00:29 +00:00
|
|
|
struct apk_gzip_ostream *gos = container_of(os, struct apk_gzip_ostream, os);
|
2009-07-17 11:06:43 +00:00
|
|
|
unsigned char buffer[1024];
|
2009-07-16 12:16:05 +00:00
|
|
|
size_t have;
|
2009-08-12 08:05:09 +00:00
|
|
|
int r, rc = 0;
|
2009-07-16 12:16:05 +00:00
|
|
|
|
2009-07-20 12:20:37 +00:00
|
|
|
do {
|
|
|
|
gos->zs.avail_out = sizeof(buffer);
|
|
|
|
gos->zs.next_out = buffer;
|
|
|
|
r = deflate(&gos->zs, Z_FINISH);
|
|
|
|
have = sizeof(buffer) - gos->zs.avail_out;
|
2017-06-21 13:07:58 +00:00
|
|
|
if (apk_ostream_write(gos->output, buffer, have) != have)
|
2009-08-12 08:05:09 +00:00
|
|
|
rc = -EIO;
|
2009-07-20 12:20:37 +00:00
|
|
|
} while (r == Z_OK);
|
2017-06-21 13:07:58 +00:00
|
|
|
r = apk_ostream_close(gos->output);
|
2009-08-12 08:05:09 +00:00
|
|
|
if (r != 0)
|
|
|
|
rc = r;
|
2009-07-16 12:16:05 +00:00
|
|
|
|
|
|
|
deflateEnd(&gos->zs);
|
2019-12-18 08:00:29 +00:00
|
|
|
free(gos);
|
2009-08-12 08:05:09 +00:00
|
|
|
|
|
|
|
return rc;
|
2009-07-16 12:16:05 +00:00
|
|
|
}
|
|
|
|
|
2017-06-21 13:07:58 +00:00
|
|
|
static const struct apk_ostream_ops gzip_ostream_ops = {
|
|
|
|
.write = gzo_write,
|
|
|
|
.close = gzo_close,
|
|
|
|
};
|
|
|
|
|
2009-07-16 12:16:05 +00:00
|
|
|
struct apk_ostream *apk_ostream_gzip(struct apk_ostream *output)
|
|
|
|
{
|
|
|
|
struct apk_gzip_ostream *gos;
|
|
|
|
|
2014-10-08 08:13:21 +00:00
|
|
|
if (IS_ERR_OR_NULL(output)) return ERR_CAST(output);
|
2009-07-16 12:16:05 +00:00
|
|
|
|
|
|
|
gos = malloc(sizeof(struct apk_gzip_ostream));
|
2015-03-10 11:04:14 +00:00
|
|
|
if (gos == NULL) goto err;
|
2009-07-16 12:16:05 +00:00
|
|
|
|
|
|
|
*gos = (struct apk_gzip_ostream) {
|
2017-06-21 13:07:58 +00:00
|
|
|
.os.ops = &gzip_ostream_ops,
|
2009-07-16 12:16:05 +00:00
|
|
|
.output = output,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (deflateInit2(&gos->zs, 9, Z_DEFLATED, 15 | 16, 8,
|
|
|
|
Z_DEFAULT_STRATEGY) != Z_OK) {
|
|
|
|
free(gos);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &gos->os;
|
|
|
|
err:
|
2017-06-21 13:07:58 +00:00
|
|
|
apk_ostream_close(output);
|
2015-03-10 11:04:14 +00:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2008-11-07 15:11:08 +00:00
|
|
|
}
|
|
|
|
|