summaryrefslogblamecommitdiff
path: root/src/gunzip.c
blob: 010d45ef3786ab67dfd35eda453a7ed5efeba48a (plain) (tree)
1
2
3
4
5
6




                                                     
                                                                          

















                                                                           
                      





                            



















                                                                  

                                        




                                                                                         


                                                                     
                                                                


                                                                





                                                                        



                                                           








                                                                                         
                                             
 


                                                                  
                 












                                                                  

                                                
                             
                           
                                              


                  

                                                                                   


                                     


                            





                                                      


                                     
                                       

                             






                                                    




                                                      


                        
/* gunzip.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * 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;
	int autoclose;

	EVP_MD_CTX mdctx;
	void *mdblock;

	apk_multipart_cb cb;
	void *cbctx;
};

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

			if (gis->cb != NULL && gis->mdblock != NULL) {
				/* Digest the inflated bytes */
				EVP_DigestUpdate(&gis->mdctx, gis->mdblock,
						 (void *)gis->zs.next_in - gis->mdblock);
			}
			blob = gis->bs->read(gis->bs, APK_BLOB_NULL);
			gis->mdblock = blob.ptr;
			gis->zs.avail_in = blob.len;
			gis->zs.next_in = (void *) gis->mdblock;
			if (gis->zs.avail_in < 0) {
				gis->z_err = Z_DATA_ERROR;
				return size - gis->zs.avail_out;
			} 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;
			}
		}

		gis->z_err = inflate(&gis->zs, Z_NO_FLUSH);
		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);
			}
			inflateEnd(&gis->zs);

			if (inflateInit2(&gis->zs, 15+32) != Z_OK)
				return -1;
			gis->z_err = Z_OK;
		}
	}

	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);

	if (gis->cb != NULL)
		EVP_MD_CTX_cleanup(&gis->mdctx);
	inflateEnd(&gis->zs);
	if (gis->autoclose)
		gis->bs->close(gis->bs, NULL);
	free(gis);
}

struct apk_istream *apk_bstream_gunzip_mpart(struct apk_bstream *bs, int autoclose,
					     apk_multipart_cb cb, void *ctx)
{
	struct apk_gzip_istream *gis;

	if (bs == NULL)
		return NULL;

	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,
		.autoclose = autoclose,
		.cb = cb,
		.cbctx = ctx,
	};

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

	if (gis->cb != NULL) {
		EVP_MD_CTX_init(&gis->mdctx);
		cb(ctx, &gis->mdctx, APK_MPART_BEGIN);
	}

	return &gis->is;
}