gunzip.c 4.82 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;
25
26
27
28
29
30

	EVP_MD_CTX mdctx;
	void *mdblock;

	apk_multipart_cb cb;
	void *cbctx;
31
32
};

33
static size_t gzi_read(void *stream, void *ptr, size_t size)
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
	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) {
51
52
			apk_blob_t blob;

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

		gis->z_err = inflate(&gis->zs, Z_NO_FLUSH);
75
76
77
78
79
80
		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;
81
82
83
84
85
				if (gis->cb(gis->cbctx, &gis->mdctx,
					    APK_MPART_BOUNDARY)) {
					gis->z_err = Z_STREAM_END;
					break;
				}
86
			}
87
88
89
90
91
			inflateEnd(&gis->zs);
			if (inflateInit2(&gis->zs, 15+32) != Z_OK)
				return -1;
			gis->z_err = Z_OK;
		}
92
93
94
95
96
97
98
99
	}

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

	return size - gis->zs.avail_out;
}

100
static void gzi_close(void *stream)
101
102
103
104
{
	struct apk_gzip_istream *gis =
		container_of(stream, struct apk_gzip_istream, is);

105
106
	if (gis->cb != NULL)
		EVP_MD_CTX_cleanup(&gis->mdctx);
107
	inflateEnd(&gis->zs);
108
	gis->bs->close(gis->bs, NULL);
109
110
111
	free(gis);
}

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

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

120
121
	gis = malloc(sizeof(struct apk_gzip_istream));
	if (gis == NULL)
122
		goto err;
123
124

	*gis = (struct apk_gzip_istream) {
125
126
		.is.read = gzi_read,
		.is.close = gzi_close,
127
128
		.bs = bs,
		.z_err = 0,
129
130
		.cb = cb,
		.cbctx = ctx,
131
132
133
134
	};

	if (inflateInit2(&gis->zs, 15+32) != Z_OK) {
		free(gis);
135
		goto err;
136
137
	}

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

143
	return &gis->is;
144
145
146
147
148
149
150
151
152
153
154
155
156
157
err:
	bs->close(bs, NULL);
	return NULL;
}

struct apk_gzip_ostream {
	struct apk_ostream os;
	struct apk_ostream *output;
	z_stream zs;
};

static size_t gzo_write(void *stream, const void *ptr, size_t size)
{
	struct apk_gzip_ostream *gos = (struct apk_gzip_ostream *) stream;
Timo Teräs's avatar
Timo Teräs committed
158
	unsigned char buffer[1024];
159
160
161
162
163
164
	size_t have;
	int r;

	gos->zs.avail_in = size;
	gos->zs.next_in = (void *) ptr;
	while (gos->zs.avail_in) {
Timo Teräs's avatar
Timo Teräs committed
165
166
		gos->zs.avail_out = sizeof(buffer);
		gos->zs.next_out = buffer;
167
168
169
		r = deflate(&gos->zs, Z_NO_FLUSH);
		if (r == Z_STREAM_ERROR)
			return -1;
Timo Teräs's avatar
Timo Teräs committed
170
		have = sizeof(buffer) - gos->zs.avail_out;
171
		if (have != 0) {
Timo Teräs's avatar
Timo Teräs committed
172
			r = gos->output->write(gos->output, buffer, have);
173
174
175
176
177
178
179
180
181
182
183
			if (r != have)
				return -1;
		}
	}

	return size;
}

static void gzo_close(void *stream)
{
	struct apk_gzip_ostream *gos = (struct apk_gzip_ostream *) stream;
Timo Teräs's avatar
Timo Teräs committed
184
	unsigned char buffer[1024];
185
186
	size_t have;

Timo Teräs's avatar
Timo Teräs committed
187
188
	gos->zs.avail_out = sizeof(buffer);
	gos->zs.next_out = buffer;
189
	deflate(&gos->zs, Z_FINISH);
Timo Teräs's avatar
Timo Teräs committed
190
191
	have = sizeof(buffer) - gos->zs.avail_out;
	gos->output->write(gos->output, buffer, have);
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
	gos->output->close(gos->output);

	deflateEnd(&gos->zs);
	free(stream);
}

struct apk_ostream *apk_ostream_gzip(struct apk_ostream *output)
{
	struct apk_gzip_ostream *gos;

	if (output == NULL)
		return NULL;

	gos = malloc(sizeof(struct apk_gzip_ostream));
	if (gos == NULL)
		goto err;

	*gos = (struct apk_gzip_ostream) {
		.os.write = gzo_write,
		.os.close = gzo_close,
		.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:
	output->close(output);
	return NULL;
225
226
}