Gitlab has successfully been upgraded to 14.0.10 🎉. Enjoy!

gunzip.c 4.66 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

	apk_multipart_cb cb;
	void *cbctx;
28
	void *cbprev;
29 30
};

31
static size_t gzi_read(void *stream, void *ptr, size_t size)
32 33 34
{
	struct apk_gzip_istream *gis =
		container_of(stream, struct apk_gzip_istream, is);
35
	int r;
36 37 38 39 40 41 42 43 44 45 46 47 48 49

	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) {
50 51
			apk_blob_t blob;

52 53 54 55
			if (gis->cb != NULL && gis->cbprev != NULL) {
				gis->cb(gis->cbctx, APK_MPART_DATA,
					APK_BLOB_PTR_LEN(gis->cbprev,
					(void *)gis->zs.next_in - gis->cbprev));
56
			}
57
			blob = gis->bs->read(gis->bs, APK_BLOB_NULL);
58
			gis->cbprev = blob.ptr;
59
			gis->zs.avail_in = blob.len;
60
			gis->zs.next_in = (void *) gis->cbprev;
61 62 63
			if (gis->zs.avail_in < 0) {
				gis->z_err = Z_DATA_ERROR;
				return size - gis->zs.avail_out;
64 65
			} else if (gis->zs.avail_in == 0) {
				if (gis->cb != NULL)
66 67
					gis->cb(gis->cbctx, APK_MPART_END,
						APK_BLOB_NULL);
68 69
				gis->z_err = Z_STREAM_END;
				return size - gis->zs.avail_out;
70 71 72 73
			}
		}

		gis->z_err = inflate(&gis->zs, Z_NO_FLUSH);
74 75 76
		if (gis->z_err == Z_STREAM_END) {
			/* Digest the inflated bytes */
			if (gis->cb != NULL) {
77 78 79 80 81 82 83 84
				r = gis->cb(gis->cbctx, APK_MPART_BOUNDARY,
					    APK_BLOB_PTR_LEN(gis->cbprev,
					    (void *)gis->zs.next_in - gis->cbprev));
				if (r != 0) {
					gis->z_err = Z_DATA_ERROR;
					if (r > 0)
						r = -1;
					return r;
85
				}
86
				gis->cbprev = gis->zs.next_in;
87
			}
88 89 90 91 92
			inflateEnd(&gis->zs);
			if (inflateInit2(&gis->zs, 15+32) != Z_OK)
				return -1;
			gis->z_err = Z_OK;
		}
93 94 95 96 97 98 99 100
	}

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

	return size - gis->zs.avail_out;
}

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

	inflateEnd(&gis->zs);
107
	gis->bs->close(gis->bs, NULL);
108 109 110
	free(gis);
}

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

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

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

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

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

	return &gis->is;
138 139 140 141 142 143 144 145 146 147 148 149 150 151
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
152
	unsigned char buffer[1024];
153 154 155 156 157 158
	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
159 160
		gos->zs.avail_out = sizeof(buffer);
		gos->zs.next_out = buffer;
161 162 163
		r = deflate(&gos->zs, Z_NO_FLUSH);
		if (r == Z_STREAM_ERROR)
			return -1;
Timo Teräs's avatar
Timo Teräs committed
164
		have = sizeof(buffer) - gos->zs.avail_out;
165
		if (have != 0) {
Timo Teräs's avatar
Timo Teräs committed
166
			r = gos->output->write(gos->output, buffer, have);
167 168 169 170 171 172 173 174 175 176 177
			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
178
	unsigned char buffer[1024];
179 180
	size_t have;

Timo Teräs's avatar
Timo Teräs committed
181 182
	gos->zs.avail_out = sizeof(buffer);
	gos->zs.next_out = buffer;
183
	deflate(&gos->zs, Z_FINISH);
Timo Teräs's avatar
Timo Teräs committed
184 185
	have = sizeof(buffer) - gos->zs.avail_out;
	gos->output->write(gos->output, buffer, have);
186 187 188 189 190 191 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
	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;
219 220
}