diff --git a/src/Makefile b/src/Makefile index ea1d0c8..79ff5d6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -19,9 +19,9 @@ ZLIB_LIBS := $(shell $(PKG_CONFIG) --libs zlib) libapk_soname := 2.99.0 libapk_so := $(obj)/libapk.so.$(libapk_soname) libapk.so.$(libapk_soname)-objs := \ - adb.o adb_walk_adb.o adb_walk_genadb.o adb_walk_gentext.o adb_walk_istream.o apk_adb.o \ + adb.o adb_comp.o adb_walk_adb.o adb_walk_genadb.o adb_walk_gentext.o adb_walk_istream.o apk_adb.o \ atom.o blob.o commit.o common.o context.o crypto_openssl.o database.o hash.o \ - io.o io_url.o io_gunzip.o io_archive.o \ + io.o io_archive.o io_gunzip.o io_url.o \ package.o pathbuilder.o print.o solver.o trust.o version.o libapk.so.$(libapk_soname)-libs := libfetch/libfetch.a diff --git a/src/adb.h b/src/adb.h index 2319f92..9cb73b4 100644 --- a/src/adb.h +++ b/src/adb.h @@ -300,4 +300,14 @@ struct adb_walk_genadb { int adb_walk_adb(struct adb_walk *d, struct adb *db, struct apk_trust *trust); int adb_walk_istream(struct adb_walk *d, struct apk_istream *is); +// Seamless compression support + +typedef unsigned int adb_comp_t; + +#define ADB_COMP_NONE 0 +#define ADB_COMP_DEFLATE 1 + +struct apk_istream *adb_decompress(struct apk_istream *is, adb_comp_t *compression); +struct apk_ostream *adb_compress(struct apk_ostream *os, adb_comp_t compression); + #endif diff --git a/src/adb_comp.c b/src/adb_comp.c new file mode 100644 index 0000000..8aed95d --- /dev/null +++ b/src/adb_comp.c @@ -0,0 +1,48 @@ +/* adb_comp.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2021 Timo Teräs + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "apk_defines.h" +#include "adb.h" + +struct apk_istream *adb_decompress(struct apk_istream *is, adb_comp_t *compression) +{ + adb_comp_t c = ADB_COMP_NONE; + + if (IS_ERR_OR_NULL(is)) return is; + + uint8_t *buf = apk_istream_peek(is, 4); + if (memcmp(buf, "ADB", 3) == 0) switch (buf[3]) { + case '.': + c = ADB_COMP_NONE; + break; + case 'd': + c = ADB_COMP_DEFLATE; + is = apk_istream_deflate(is); + break; + default: + apk_istream_close(is); + return ERR_PTR(-APKE_ADB_COMPRESSION); + } + if (compression) *compression = c; + return is; +} + +struct apk_ostream *adb_compress(struct apk_ostream *os, adb_comp_t compression) +{ + if (IS_ERR_OR_NULL(os)) return os; + switch (compression) { + case ADB_COMP_NONE: + return os; + case ADB_COMP_DEFLATE: + if (apk_ostream_write(os, "ADBd", 4) < 0) goto err; + return apk_ostream_deflate(os); + } +err: + apk_ostream_cancel(os, -APKE_ADB_COMPRESSION); + return ERR_PTR(apk_ostream_close(os)); +} diff --git a/src/apk_defines.h b/src/apk_defines.h index bc2863b..fbf66b9 100644 --- a/src/apk_defines.h +++ b/src/apk_defines.h @@ -43,6 +43,7 @@ enum { APKE_SIGNATURE_FAIL, APKE_SIGNATURE_UNTRUSTED, APKE_SIGNATURE_INVALID, + APKE_ADB_COMPRESSION, APKE_ADB_HEADER, APKE_ADB_SCHEMA, APKE_ADB_BLOCK, diff --git a/src/apk_io.h b/src/apk_io.h index 40a0d8c..61aee4f 100644 --- a/src/apk_io.h +++ b/src/apk_io.h @@ -93,6 +93,7 @@ struct apk_istream *apk_istream_from_fd(int fd); struct apk_istream *apk_istream_from_fd_url_if_modified(int atfd, const char *url, time_t since); static inline int apk_istream_error(struct apk_istream *is, int err) { if (!is->err) is->err = err; return err; } ssize_t apk_istream_read(struct apk_istream *is, void *ptr, size_t size); +void *apk_istream_peek(struct apk_istream *is, size_t len); void *apk_istream_get(struct apk_istream *is, size_t len); apk_blob_t apk_istream_get_max(struct apk_istream *is, size_t size); apk_blob_t apk_istream_get_delim(struct apk_istream *is, apk_blob_t token); @@ -119,19 +120,6 @@ static inline int apk_istream_close(struct apk_istream *is) return is->ops->close(is); } -#define APK_MPART_DATA 1 /* data processed so far */ -#define APK_MPART_BOUNDARY 2 /* final part of data, before boundary */ -#define APK_MPART_END 3 /* signals end of stream */ - -typedef int (*apk_multipart_cb)(void *ctx, int part, apk_blob_t data); - -struct apk_istream *apk_istream_gunzip_mpart(struct apk_istream *, - apk_multipart_cb cb, void *ctx); -static inline struct apk_istream *apk_istream_gunzip(struct apk_istream *is) -{ - return apk_istream_gunzip_mpart(is, NULL, NULL); -} - struct apk_segment_istream { struct apk_istream is; struct apk_istream *pis; @@ -152,7 +140,6 @@ struct apk_ostream { int rc; }; -struct apk_ostream *apk_ostream_gzip(struct apk_ostream *); struct apk_ostream *apk_ostream_counter(off_t *); struct apk_ostream *apk_ostream_to_fd(int fd); struct apk_ostream *apk_ostream_to_file(int atfd, const char *file, mode_t mode); @@ -199,4 +186,33 @@ gid_t apk_id_cache_resolve_gid(struct apk_id_cache *idc, apk_blob_t groupname, g apk_blob_t apk_id_cache_resolve_user(struct apk_id_cache *idc, uid_t uid); apk_blob_t apk_id_cache_resolve_group(struct apk_id_cache *idc, gid_t gid); +// Gzip support + +#define APK_MPART_DATA 1 /* data processed so far */ +#define APK_MPART_BOUNDARY 2 /* final part of data, before boundary */ +#define APK_MPART_END 3 /* signals end of stream */ + +typedef int (*apk_multipart_cb)(void *ctx, int part, apk_blob_t data); + +struct apk_istream *apk_istream_zlib(struct apk_istream *, int, + apk_multipart_cb cb, void *ctx); +static inline struct apk_istream *apk_istream_gunzip_mpart(struct apk_istream *is, + apk_multipart_cb cb, void *ctx) { + return apk_istream_zlib(is, 0, cb, ctx); +} +static inline struct apk_istream *apk_istream_gunzip(struct apk_istream *is) { + return apk_istream_zlib(is, 0, NULL, NULL); +} +static inline struct apk_istream *apk_istream_deflate(struct apk_istream *is) { + return apk_istream_zlib(is, 1, NULL, NULL); +} + +struct apk_ostream *apk_ostream_zlib(struct apk_ostream *, int); +static inline struct apk_ostream *apk_ostream_gzip(struct apk_ostream *os) { + return apk_ostream_zlib(os, 0); +} +static inline struct apk_ostream *apk_ostream_deflate(struct apk_ostream *os) { + return apk_ostream_zlib(os, 1); +} + #endif diff --git a/src/io.c b/src/io.c index 5a426cf..0ec278b 100644 --- a/src/io.c +++ b/src/io.c @@ -130,12 +130,11 @@ static int __apk_istream_fill(struct apk_istream *is) return 0; } -void *apk_istream_get(struct apk_istream *is, size_t len) +void *apk_istream_peek(struct apk_istream *is, size_t len) { do { if (is->end - is->ptr >= len) { void *ptr = is->ptr; - is->ptr += len; return ptr; } } while (!__apk_istream_fill(is)); @@ -147,6 +146,13 @@ void *apk_istream_get(struct apk_istream *is, size_t len) return ERR_PTR(-EIO); } +void *apk_istream_get(struct apk_istream *is, size_t len) +{ + void *p = apk_istream_peek(is, len); + if (!IS_ERR_OR_NULL(p)) is->ptr += len; + return p; +} + apk_blob_t apk_istream_get_max(struct apk_istream *is, size_t max) { if (is->ptr == is->end) diff --git a/src/io_gunzip.c b/src/io_gunzip.c index 554a95a..e1a23d6 100644 --- a/src/io_gunzip.c +++ b/src/io_gunzip.c @@ -135,7 +135,13 @@ static const struct apk_istream_ops gunzip_istream_ops = { .close = gzi_close, }; -struct apk_istream *apk_istream_gunzip_mpart(struct apk_istream *is, apk_multipart_cb cb, void *ctx) +static int window_bits(int window_bits, int raw) +{ + if (raw) return -window_bits; // raw mode + return window_bits | 16; // gzip mode +} + +struct apk_istream *apk_istream_zlib(struct apk_istream *is, int raw, apk_multipart_cb cb, void *ctx) { struct apk_gzip_istream *gis; @@ -153,7 +159,7 @@ struct apk_istream *apk_istream_gunzip_mpart(struct apk_istream *is, apk_multipa .cbctx = ctx, }; - if (inflateInit2(&gis->zs, 15+32) != Z_OK) { + if (inflateInit2(&gis->zs, window_bits(15, raw)) != Z_OK) { free(gis); goto err; } @@ -225,7 +231,7 @@ static const struct apk_ostream_ops gzip_ostream_ops = { .close = gzo_close, }; -struct apk_ostream *apk_ostream_gzip(struct apk_ostream *output) +struct apk_ostream *apk_ostream_zlib(struct apk_ostream *output, int raw) { struct apk_gzip_ostream *gos; @@ -239,7 +245,7 @@ struct apk_ostream *apk_ostream_gzip(struct apk_ostream *output) .output = output, }; - if (deflateInit2(&gos->zs, 9, Z_DEFLATED, 15 | 16, 8, + if (deflateInit2(&gos->zs, 9, Z_DEFLATED, window_bits(15, raw), 8, Z_DEFAULT_STRATEGY) != Z_OK) { free(gos); goto err; diff --git a/src/meson.build b/src/meson.build index d7c64f4..ff0b3cc 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,6 +1,7 @@ libapk_so_version = '2.99.0' libapk_src = [ 'adb.c', + 'adb_comp.c', 'adb_walk_adb.c', 'adb_walk_genadb.c', 'adb_walk_gentext.c', diff --git a/src/print.c b/src/print.c index 3cab694..1662e3d 100644 --- a/src/print.c +++ b/src/print.c @@ -39,6 +39,7 @@ const char *apk_error_str(int error) case APKE_SIGNATURE_FAIL: return "signing failure"; case APKE_SIGNATURE_UNTRUSTED: return "UNTRUSTED signature"; case APKE_SIGNATURE_INVALID: return "BAD signature"; + case APKE_ADB_COMPRESSION: return "ADB compression not supported"; case APKE_ADB_HEADER: return "ADB header error"; case APKE_ADB_SCHEMA: return "ADB schema error"; case APKE_ADB_BLOCK: return "ADB block error";