gunzip.c 3.22 KB
Newer Older
1 2 3 4 5
/* gunzip.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
6
 * This program is free software; you can redistribute it and/or modify it
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */

#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;
	struct apk_bstream *bs;
	z_stream zs;
	int z_err;
Timo Teräs's avatar
Timo Teräs committed
25
	int autoclose;
26 27 28 29 30 31

	EVP_MD_CTX mdctx;
	void *mdblock;

	apk_multipart_cb cb;
	void *cbctx;
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
};

static size_t gz_read(void *stream, void *ptr, size_t size)
{
	struct apk_gzip_istream *gis =
		container_of(stream, struct apk_gzip_istream, is);

	if (gis->z_err == Z_DATA_ERROR || gis->z_err == Z_ERRNO)
		return -1;
	if (gis->z_err == Z_STREAM_END)
		return 0;

	if (ptr == NULL)
		return apk_istream_skip(&gis->is, size);

	gis->zs.avail_out = size;
	gis->zs.next_out  = ptr;

	while (gis->zs.avail_out != 0 && gis->z_err == Z_OK) {
		if (gis->zs.avail_in == 0) {
52 53
			apk_blob_t blob;

54 55 56 57 58
			if (gis->cb != NULL && gis->mdblock != NULL) {
				/* Digest the inflated bytes */
				EVP_DigestUpdate(&gis->mdctx, gis->mdblock,
						 (void *)gis->zs.next_in - gis->mdblock);
			}
59 60 61
			blob = gis->bs->read(gis->bs, APK_BLOB_NULL);
			gis->mdblock = blob.ptr;
			gis->zs.avail_in = blob.len;
62
			gis->zs.next_in = (void *) gis->mdblock;
63 64 65
			if (gis->zs.avail_in < 0) {
				gis->z_err = Z_DATA_ERROR;
				return size - gis->zs.avail_out;
66 67 68 69 70 71
			} else if (gis->zs.avail_in == 0) {
				if (gis->cb != NULL)
					gis->cb(gis->cbctx, &gis->mdctx,
						APK_MPART_END);
				gis->z_err = Z_STREAM_END;
				return size - gis->zs.avail_out;
72 73 74 75
			}
		}

		gis->z_err = inflate(&gis->zs, Z_NO_FLUSH);
76 77 78 79 80 81 82 83 84
		if (gis->z_err == Z_STREAM_END) {
			/* Digest the inflated bytes */
			if (gis->cb != NULL) {
				EVP_DigestUpdate(&gis->mdctx, gis->mdblock,
						 (void *)gis->zs.next_in - gis->mdblock);
				gis->mdblock = gis->zs.next_in;
				gis->cb(gis->cbctx, &gis->mdctx,
					APK_MPART_BOUNDARY);
			}
85
			inflateEnd(&gis->zs);
86

87 88 89 90
			if (inflateInit2(&gis->zs, 15+32) != Z_OK)
				return -1;
			gis->z_err = Z_OK;
		}
91 92 93 94 95 96 97 98 99 100 101 102 103
	}

	if (gis->z_err != Z_OK && gis->z_err != Z_STREAM_END)
		return -1;

	return size - gis->zs.avail_out;
}

static void gz_close(void *stream)
{
	struct apk_gzip_istream *gis =
		container_of(stream, struct apk_gzip_istream, is);

104 105
	if (gis->cb != NULL)
		EVP_MD_CTX_cleanup(&gis->mdctx);
106
	inflateEnd(&gis->zs);
Timo Teräs's avatar
Timo Teräs committed
107
	if (gis->autoclose)
108
		gis->bs->close(gis->bs, NULL);
109 110 111
	free(gis);
}

112 113
struct apk_istream *apk_bstream_gunzip_mpart(struct apk_bstream *bs, int autoclose,
					     apk_multipart_cb cb, void *ctx)
114 115 116
{
	struct apk_gzip_istream *gis;

117 118 119
	if (bs == NULL)
		return NULL;

120 121 122 123 124 125 126 127 128
	gis = malloc(sizeof(struct apk_gzip_istream));
	if (gis == NULL)
		return NULL;

	*gis = (struct apk_gzip_istream) {
		.is.read = gz_read,
		.is.close = gz_close,
		.bs = bs,
		.z_err = 0,
Timo Teräs's avatar
Timo Teräs committed
129
		.autoclose = autoclose,
130 131
		.cb = cb,
		.cbctx = ctx,
132 133 134 135 136 137 138
	};

	if (inflateInit2(&gis->zs, 15+32) != Z_OK) {
		free(gis);
		return NULL;
	}

139 140 141 142 143
	if (gis->cb != NULL) {
		EVP_MD_CTX_init(&gis->mdctx);
		cb(ctx, &gis->mdctx, APK_MPART_BEGIN);
	}

144 145 146
	return &gis->is;
}