/* * Copyright (C) 2023 Patrick McDermott * * This file is part of opkg-opk. * * opkg-opk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * opkg-opk is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with opkg-opk. If not, see . */ #include #include #include #include "defs.h" #include "gzip.h" #include "ustar.h" #define OPKG_OPK_GZIP_WINDOW_BITS_ (15 + 16) enum _opkg_opk_gzip_dir { _OPKG_OPK_GZIP_DIR_READ, _OPKG_OPK_GZIP_DIR_WRITE, }; struct opkg_opk_gzip { enum _opkg_opk_gzip_dir dir; opkg_opk_gzip_read_func *read_func; void *user_data; FILE *write_fp; char write_buffer[512]; size_t written; z_stream stream; gz_header gz_header; }; struct opkg_opk_gzip * opkg_opk_gzip_init_read(opkg_opk_gzip_read_func *read_func, void *user_data) { struct opkg_opk_gzip *gzip; gzip = malloc(sizeof(*gzip)); if (gzip == NULL) { return NULL; } gzip->dir = _OPKG_OPK_GZIP_DIR_READ; gzip->read_func = read_func; gzip->user_data = user_data; gzip->stream.zalloc = Z_NULL; gzip->stream.zfree = Z_NULL; gzip->stream.opaque = Z_NULL; gzip->stream.next_in = Z_NULL; gzip->stream.avail_in = 0; if (inflateInit2(&gzip->stream, OPKG_OPK_GZIP_WINDOW_BITS_) != Z_OK) { free(gzip); return NULL; } return gzip; } struct opkg_opk_gzip * opkg_opk_gzip_init_write(FILE *write_fp) { struct opkg_opk_gzip *gzip; gzip = malloc(sizeof(*gzip)); if (gzip == NULL) { return NULL; } gzip->dir = _OPKG_OPK_GZIP_DIR_WRITE; gzip->write_fp = write_fp; gzip->written = 0; gzip->stream.zalloc = Z_NULL; gzip->stream.zfree = Z_NULL; gzip->stream.opaque = Z_NULL; gzip->stream.next_out = Z_NULL; gzip->stream.avail_out = 0; if (deflateInit2(&gzip->stream, 9, Z_DEFLATED, OPKG_OPK_GZIP_WINDOW_BITS_, 9, Z_DEFAULT_STRATEGY) != Z_OK) { free(gzip); return NULL; } gzip->gz_header.text = 0; gzip->gz_header.time = 0; /* Stored as 32-bit uint */ gzip->gz_header.os = 3; /* Unix, per RFC 1952 */ gzip->gz_header.extra = Z_NULL; gzip->gz_header.name = Z_NULL; gzip->gz_header.comment = Z_NULL; gzip->gz_header.hcrc = 0; if (deflateSetHeader(&gzip->stream, &gzip->gz_header) != Z_OK) { deflateEnd(&gzip->stream); free(gzip); return NULL; } return gzip; } int opkg_opk_gzip_read(struct opkg_opk_gzip *gzip, void *record) { int end; /* Sanity check */ if (gzip->dir != _OPKG_OPK_GZIP_DIR_READ) { return OPKG_OPK_ERROR; } gzip->stream.next_out = record; gzip->stream.avail_out = OPKG_OPK_USTAR_RECORD_SIZE; for (;;) { end = 0; if (gzip->stream.avail_in == 0) { /* Input buffer is empty and needs refilled. */ switch (gzip->read_func(gzip->user_data, (char **) &gzip->stream.next_in, (size_t *) &gzip->stream. avail_in)) { case OPKG_OPK_OK: break; case OPKG_OPK_END: end = 1; break; case OPKG_OPK_ERROR: default: return OPKG_OPK_ERROR; } } switch (inflate(&gzip->stream, Z_SYNC_FLUSH)) { case Z_OK: break; case Z_BUF_ERROR: if (end == 1) { return OPKG_OPK_ERROR; } break; case Z_STREAM_END: if (gzip->stream.avail_out != 0) { /* Premature end */ return OPKG_OPK_ERROR; } return OPKG_OPK_END; default: return OPKG_OPK_ERROR; } if (gzip->stream.avail_out == 0) { /* Output buffer is filled and ready for use. */ return OPKG_OPK_OK; } } } static int _opkg_opk_gzip_write(struct opkg_opk_gzip *gzip, void *record, size_t size, int last) { size_t len; /* Sanity check */ if (gzip->dir != _OPKG_OPK_GZIP_DIR_WRITE) { return OPKG_OPK_ERROR; } gzip->stream.next_in = record; gzip->stream.avail_in = size; do { gzip->stream.next_out = gzip->write_buffer; gzip->stream.avail_out = sizeof(gzip->write_buffer); switch (deflate(&gzip->stream, (last > 0 ? Z_FINISH : Z_NO_FLUSH))) { case Z_OK: case Z_BUF_ERROR: case Z_STREAM_END: break; default: return OPKG_OPK_ERROR; } /* Process output buffer. */ len = sizeof(gzip->write_buffer) - gzip->stream.avail_out; if (fwrite(gzip->write_buffer, 1, len, gzip->write_fp) != len) { return OPKG_OPK_ERROR; } gzip->written += len; } while (gzip->stream.avail_out == 0); if (gzip->stream.avail_in != 0) { return OPKG_OPK_ERROR; } if (last == 0) { /* Input buffer is empty and needs refilled. */ return OPKG_OPK_OK; } else { return OPKG_OPK_END; } } int opkg_opk_gzip_write(struct opkg_opk_gzip *gzip, void *record, size_t size) { if (_opkg_opk_gzip_write(gzip, record, size, 0) == OPKG_OPK_OK) { return OPKG_OPK_OK; } else { return OPKG_OPK_ERROR; } } size_t opkg_opk_gzip_written(struct opkg_opk_gzip *gzip) { return gzip->written; } int opkg_opk_gzip_finish_write(struct opkg_opk_gzip *gzip) { /* Sanity check */ if (gzip->dir != _OPKG_OPK_GZIP_DIR_WRITE) { return OPKG_OPK_ERROR; } if (gzip->written > 0 && _opkg_opk_gzip_write(gzip, NULL, 0, 1) != OPKG_OPK_END) { return OPKG_OPK_ERROR; } return OPKG_OPK_OK; } int opkg_opk_gzip_free(struct opkg_opk_gzip *gzip) { int ret; ret = OPKG_OPK_OK; if (gzip->dir == _OPKG_OPK_GZIP_DIR_READ) { if (inflateEnd(&gzip->stream) != Z_OK) { ret = OPKG_OPK_ERROR; } } else { if (deflateEnd(&gzip->stream) != Z_OK) { ret = OPKG_OPK_ERROR; } } free(gzip); return ret; }