Further refactor extract API to have separate ops vtable

This splits the callbacks by type, and further prepares the API
to be usable for v3 files too.
cute-signatures
Timo Teräs 2021-07-27 15:34:04 +03:00
parent 9c843e4ecd
commit 5843daf7a1
7 changed files with 162 additions and 135 deletions

View File

@ -14,6 +14,7 @@
#include "apk_print.h"
#include "apk_io.h"
struct adb;
struct apk_ctx;
struct apk_extract_ctx;
@ -26,26 +27,31 @@ int apk_extract_file(int atfd, const struct apk_file_info *ae,
apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx,
unsigned int extract_flags, struct apk_out *out);
typedef int (*apk_extract_cb)(struct apk_extract_ctx *,
const struct apk_file_info *ae,
struct apk_istream *istream);
struct apk_extract_ops {
int (*v2index)(struct apk_extract_ctx *, apk_blob_t *desc, struct apk_istream *is);
int (*v2meta)(struct apk_extract_ctx *, struct apk_istream *is);
int (*v3index)(struct apk_extract_ctx *, struct adb *);
int (*v3meta)(struct apk_extract_ctx *, struct adb *);
int (*script)(struct apk_extract_ctx *, unsigned int script, size_t size, struct apk_istream *is);
int (*file)(struct apk_extract_ctx *, const struct apk_file_info *fi, struct apk_istream *is);
};
struct apk_extract_ctx {
struct apk_ctx *ac;
apk_extract_cb cb;
const struct apk_extract_ops *ops;
struct apk_checksum *identity;
unsigned generate_identity : 1;
unsigned metadata : 1;
unsigned metadata_verified : 1;
apk_blob_t desc;
void *pctx;
unsigned generate_identity : 1;
unsigned is_package : 1;
unsigned is_index : 1;
};
static inline void apk_extract_init(struct apk_extract_ctx *ectx, struct apk_ctx *ac, apk_extract_cb cb) {
*ectx = (struct apk_extract_ctx){.ac = ac, .cb = cb};
static inline void apk_extract_init(struct apk_extract_ctx *ectx, struct apk_ctx *ac, const struct apk_extract_ops *ops) {
*ectx = (struct apk_extract_ctx){.ac = ac, .ops = ops};
}
static inline void apk_extract_reset(struct apk_extract_ctx *ectx) {
apk_extract_init(ectx, ectx->ac, ectx->cb);
apk_extract_init(ectx, ectx->ac, ectx->ops);
}
static inline void apk_extract_generate_identity(struct apk_extract_ctx *ctx, struct apk_checksum *id) {
ctx->identity = id;
@ -58,6 +64,7 @@ int apk_extract(struct apk_extract_ctx *, struct apk_istream *is);
int apk_extract_v2(struct apk_extract_ctx *, struct apk_istream *is);
void apk_extract_v2_control(struct apk_extract_ctx *, apk_blob_t, apk_blob_t);
int apk_extract_v2_meta(struct apk_extract_ctx *ectx, struct apk_istream *is);
int apk_extract_v3(struct apk_extract_ctx *, struct apk_istream *is);

View File

@ -11,11 +11,11 @@ struct conv_ctx {
struct adb_obj pkgs;
struct adb dbi;
struct apk_extract_ctx ectx;
int found;
};
static void convert_index(struct conv_ctx *ctx, struct apk_istream *is)
static int convert_v2index(struct apk_extract_ctx *ectx, apk_blob_t *desc, struct apk_istream *is)
{
struct conv_ctx *ctx = container_of(ectx, struct conv_ctx, ectx);
struct adb_obj pkginfo;
apk_blob_t token = APK_BLOB_STR("\n"), l;
int i;
@ -30,30 +30,18 @@ static void convert_index(struct conv_ctx *ctx, struct apk_istream *is)
i = adb_pkg_field_index(l.ptr[0]);
if (i > 0) adb_wo_pkginfo(&pkginfo, i, APK_BLOB_PTR_LEN(l.ptr+2, l.len-2));
}
return apk_istream_close(is);
}
static int load_apkindex(struct apk_extract_ctx *ectx, const struct apk_file_info *fi,
struct apk_istream *is)
{
struct conv_ctx *ctx = container_of(ectx, struct conv_ctx, ectx);
if (strcmp(fi->name, "APKINDEX") == 0) {
ctx->found = 1;
convert_index(ctx, is);
return apk_istream_close(is);
}
return 0;
}
static const struct apk_extract_ops extract_convndx = {
.v2index = convert_v2index,
};
static int load_index(struct conv_ctx *ctx, struct apk_istream *is)
{
int r = 0;
if (IS_ERR(is)) return PTR_ERR(is);
ctx->found = 0;
apk_extract_init(&ctx->ectx, ctx->ac, load_apkindex);
r = apk_extract(&ctx->ectx, is);
if (r >= 0 && ctx->found == 0) r = -APKE_V2NDX_FORMAT;
return r;
apk_extract_init(&ctx->ectx, ctx->ac, &extract_convndx);
return apk_extract(&ctx->ectx, is);
}
static int conv_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)

View File

@ -65,14 +65,13 @@ struct manifest_file_ctx {
const char *prefix1, *prefix2;
};
static int read_file_entry(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, struct apk_istream *is)
static int process_pkg_file(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, struct apk_istream *is)
{
struct manifest_file_ctx *mctx = container_of(ectx, struct manifest_file_ctx, ectx);
struct apk_out *out = mctx->out;
char csum_buf[APK_BLOB_CHECKSUM_BUF];
apk_blob_t csum_blob = APK_BLOB_BUF(csum_buf);
if (ectx->metadata) return 0;
if ((fi->mode & S_IFMT) != S_IFREG) return 0;
memset(csum_buf, '\0', sizeof(csum_buf));
@ -86,6 +85,11 @@ static int read_file_entry(struct apk_extract_ctx *ectx, const struct apk_file_i
return 0;
}
static const struct apk_extract_ops extract_manifest_ops = {
.v2meta = apk_extract_v2_meta,
.file = process_pkg_file,
};
static void process_file(struct apk_database *db, const char *match)
{
struct apk_out *out = &db->ctx->out;
@ -96,7 +100,7 @@ static void process_file(struct apk_database *db, const char *match)
};
int r;
apk_extract_init(&ctx.ectx, db->ctx, read_file_entry);
apk_extract_init(&ctx.ectx, db->ctx, &extract_manifest_ops);
if (apk_out_verbosity(out) > 1) {
ctx.prefix1 = match;
ctx.prefix2 = ": ";

View File

@ -164,23 +164,22 @@ static adb_val_t mkndx_read_v2_pkginfo(struct adb *db, struct apk_istream *is, s
return adb_w_obj(&pkginfo);
}
static int mkndx_parse_v2_tar(struct apk_extract_ctx *ectx, const struct apk_file_info *ae, struct apk_istream *is)
static int mkndx_parse_v2meta(struct apk_extract_ctx *ectx, struct apk_istream *is)
{
struct mkndx_ctx *ctx = container_of(ectx, struct mkndx_ctx, ectx);
if (ectx->metadata_verified) return -ECANCELED;
if (ectx->metadata && strcmp(ae->name, ".PKGINFO") == 0) {
adb_val_t o = adb_wa_append(
&ctx->pkgs,
mkndx_read_v2_pkginfo(
&ctx->db, is, ctx->file_size, &ctx->ectx,
ctx->rewrite_arch));
if (ADB_IS_ERROR(o)) return -ADB_VAL_VALUE(o);
}
adb_val_t o = adb_wa_append(
&ctx->pkgs,
mkndx_read_v2_pkginfo(
&ctx->db, is, ctx->file_size, &ctx->ectx,
ctx->rewrite_arch));
if (ADB_IS_ERROR(o)) return -ADB_VAL_VALUE(o);
return 0;
}
static const struct apk_extract_ops extract_ndxinfo_ops = {
.v2meta = mkndx_parse_v2meta,
};
static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
{
struct apk_out *out = &ac->out;
@ -199,7 +198,7 @@ static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
return -1;
}
apk_extract_init(&ctx->ectx, ac, mkndx_parse_v2_tar);
apk_extract_init(&ctx->ectx, ac, &extract_ndxinfo_ops);
adb_init(&odb);
adb_w_init_tmp(&tmpdb, 200);
@ -275,7 +274,7 @@ static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
if (!found) {
do_file:
r = apk_extract(&ctx->ectx, apk_istream_from_file(AT_FDCWD, *parg));
if (r < 0 && r != -ECANCELED) goto err_pkg;
if (r < 0) goto err_pkg;
newpkgs++;
}
}

View File

@ -2163,37 +2163,29 @@ struct apkindex_ctx {
int repo, found;
};
static int load_apkindex(struct apk_extract_ctx *ectx, const struct apk_file_info *fi,
struct apk_istream *is)
static int load_v2index(struct apk_extract_ctx *ectx, apk_blob_t *desc, struct apk_istream *is)
{
struct apkindex_ctx *ctx = container_of(ectx, struct apkindex_ctx, ectx);
struct apk_repository *repo;
struct apk_repository *repo = &ctx->db->repos[ctx->repo];
repo = &ctx->db->repos[ctx->repo];
if (strcmp(fi->name, "DESCRIPTION") == 0) {
repo->description = apk_blob_from_istream(is, fi->size);
} else if (strcmp(fi->name, "APKINDEX") == 0) {
ctx->found = 1;
return apk_db_index_read(ctx->db, is, ctx->repo);
}
return 0;
repo->description = *desc;
*desc = APK_BLOB_NULL;
return apk_db_index_read(ctx->db, is, ctx->repo);
}
static const struct apk_extract_ops extract_index = {
.v2index = load_v2index,
};
static int load_index(struct apk_database *db, struct apk_istream *is, int repo)
{
struct apkindex_ctx ctx;
int r = 0;
struct apkindex_ctx ctx = {
.db = db,
.repo = repo,
};
if (IS_ERR(is)) return PTR_ERR(is);
ctx.db = db;
ctx.repo = repo;
ctx.found = 0;
apk_extract_init(&ctx.ectx, db->ctx, load_apkindex);
r = apk_extract(&ctx.ectx, is);
if (r >= 0 && ctx.found == 0)
r = -APKE_V2NDX_FORMAT;
return r;
apk_extract_init(&ctx.ectx, db->ctx, &extract_index);
return apk_extract(&ctx.ectx, is);
}
int apk_db_index_read_file(struct apk_database *db, const char *file, int repo)
@ -2285,10 +2277,9 @@ static void extract_cb(void *_ctx, size_t bytes_done)
static void apk_db_run_pending_script(struct install_ctx *ctx)
{
if (ctx->script_pending && ctx->ectx.metadata_verified) {
ctx->script_pending = FALSE;
apk_ipkg_run_script(ctx->ipkg, ctx->db, ctx->script, ctx->script_args);
}
if (!ctx->script_pending) return;
ctx->script_pending = FALSE;
apk_ipkg_run_script(ctx->ipkg, ctx->db, ctx->script, ctx->script_args);
}
static int read_info_line(void *_ctx, apk_blob_t line)
@ -2376,9 +2367,25 @@ static int contains_control_character(const char *str)
return 0;
}
static int apk_db_install_archive_entry(struct apk_extract_ctx *ectx,
const struct apk_file_info *ae,
struct apk_istream *is)
static int apk_db_install_v2meta(struct apk_extract_ctx *ectx, struct apk_istream *is)
{
struct install_ctx *ctx = container_of(ectx, struct install_ctx, ectx);
apk_blob_t l, token = APK_BLOB_STR("\n");
while (apk_istream_get_delim(is, token, &l) == 0)
read_info_line(ctx, l);
return 0;
}
static int apk_db_install_script(struct apk_extract_ctx *ectx, unsigned int type, size_t size, struct apk_istream *is)
{
struct install_ctx *ctx = container_of(ectx, struct install_ctx, ectx);
struct apk_package *pkg = ctx->pkg;
apk_ipkg_add_script(pkg->ipkg, is, type, size);
ctx->script_pending |= (type == ctx->script);
return 0;
}
static int apk_db_install_file(struct apk_extract_ctx *ectx, const struct apk_file_info *ae, struct apk_istream *is)
{
struct install_ctx *ctx = container_of(ectx, struct install_ctx, ectx);
static const char dot1[] = "/./", dot2[] = "/../";
@ -2393,28 +2400,7 @@ static int apk_db_install_archive_entry(struct apk_extract_ctx *ectx,
int ret = 0, r;
char tmpname_file[TMPNAME_MAX], tmpname_link_target[TMPNAME_MAX];
/* Package metainfo and script processing */
if (ectx->metadata) {
if (ae->name[0] != '.') return 0;
if (strcmp(ae->name, ".PKGINFO") == 0) {
apk_blob_t l, token = APK_BLOB_STR("\n");
while (apk_istream_get_delim(is, token, &l) == 0)
read_info_line(ctx, l);
return 0;
}
r = apk_script_type(&ae->name[1]);
if (r != APK_SCRIPT_INVALID) {
apk_ipkg_add_script(ipkg, is, r, ae->size);
ctx->script_pending |= (r == ctx->script);
apk_db_run_pending_script(ctx);
}
return 0;
}
/* Handle script */
apk_db_run_pending_script(ctx);
/* Rest of files need to be inside data portion */
if (ae->name[0] == '.') return 0;
/* Sanity check the file name */
@ -2594,6 +2580,12 @@ static int apk_db_install_archive_entry(struct apk_extract_ctx *ectx,
return ret;
}
static const struct apk_extract_ops extract_installer = {
.v2meta = apk_db_install_v2meta,
.script = apk_db_install_script,
.file = apk_db_install_file,
};
static void apk_db_purge_pkg(struct apk_database *db,
struct apk_installed_package *ipkg,
int is_installed)
@ -2805,7 +2797,7 @@ static int apk_db_unpack_pkg(struct apk_database *db,
.cb = cb,
.cb_ctx = cb_ctx,
};
apk_extract_init(&ctx.ectx, db->ctx, apk_db_install_archive_entry);
apk_extract_init(&ctx.ectx, db->ctx, &extract_installer);
apk_extract_verify_identity(&ctx.ectx, &pkg->csum);
r = apk_extract(&ctx.ectx, is);
if (need_copy && r == 0) pkg->repos |= BIT(APK_REPOSITORY_CACHED);

View File

@ -278,31 +278,61 @@ update_digest:
return 0;
}
static int apk_extract_verify_v2index(struct apk_extract_ctx *ectx, apk_blob_t *desc, struct apk_istream *is)
{
return 0;
}
static int apk_extract_verify_v2file(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, struct apk_istream *is)
{
return 0;
}
static const struct apk_extract_ops extract_v2verify_ops = {
.v2index = apk_extract_verify_v2index,
.v2meta = apk_extract_v2_meta,
.file = apk_extract_verify_v2file,
};
static int apk_extract_v2_entry(void *pctx, const struct apk_file_info *fi, struct apk_istream *is)
{
struct apk_extract_ctx *ectx = pctx;
struct apk_sign_ctx *sctx = ectx->pctx;
int r;
int r, type;
r = apk_sign_ctx_process_file(sctx, fi, is);
if (r <= 0) return r;
ectx->metadata = sctx->control_started && !sctx->data_started;
ectx->metadata_verified = sctx->control_verified;
r = ectx->cb ? ectx->cb(ectx, fi, is) : 0;
if (ectx->metadata && strcmp(fi->name, ".PKGINFO") == 0) {
// Parse the .PKGINFO for the data, in case the callback did not do it.
apk_blob_t l, r, token = APK_BLOB_STR("\n");
while (apk_istream_get_delim(is, token, &l) == 0) {
if (l.len < 1 || l.ptr[0] == '#') continue;
if (apk_blob_split(l, APK_BLOB_STR(" = "), &l, &r))
apk_extract_v2_control(ectx, l, r);
if (!sctx->control_started) return 0;
if (!sctx->data_started || !sctx->has_data_checksum) {
if (fi->name[0] == '.') {
ectx->is_package = 1;
if (ectx->is_index) return -APKE_V2NDX_FORMAT;
if (!ectx->ops->v2meta) return -APKE_FORMAT_NOT_SUPPORTED;
if (strcmp(fi->name, ".PKGINFO") == 0) {
return ectx->ops->v2meta(ectx, is);
} else if (strcmp(fi->name, ".INSTALL") == 0) {
return -APKE_V2PKG_FORMAT;
} else if ((type = apk_script_type(&fi->name[1])) != APK_SCRIPT_INVALID) {
if (ectx->ops->script) return ectx->ops->script(ectx, type, fi->size, is);
}
} else {
ectx->is_index = 1;
if (ectx->is_package) return -APKE_V2PKG_FORMAT;
if (!ectx->ops->v2index) return -APKE_FORMAT_NOT_SUPPORTED;
if (strcmp(fi->name, "DESCRIPTION") == 0) {
free(ectx->desc.ptr);
ectx->desc = apk_blob_from_istream(is, fi->size);
} else if (strcmp(fi->name, "APKINDEX") == 0) {
return ectx->ops->v2index(ectx, &ectx->desc, is);
}
}
return 0;
}
return r;
if (!sctx->control_verified) return 0;
if (!ectx->ops->file) return -ECANCELED;
return ectx->ops->file(ectx, fi, is);
}
int apk_extract_v2(struct apk_extract_ctx *ectx, struct apk_istream *is)
@ -319,13 +349,18 @@ int apk_extract_v2(struct apk_extract_ctx *ectx, struct apk_istream *is)
else
action = trust->allow_untrusted ? APK_SIGN_NONE : APK_SIGN_VERIFY;
if (!ectx->ops) ectx->ops = &extract_v2verify_ops;
ectx->pctx = &sctx;
apk_sign_ctx_init(&sctx, action, ectx->identity, trust);
r = apk_tar_parse(
apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &sctx),
apk_extract_v2_entry, ectx, apk_ctx_get_id_cache(ac));
if (r == -ECANCELED) r = 0;
if (r == 0 && !ectx->is_package && !ectx->is_index)
r = ectx->ops->v2index ? -APKE_V2NDX_FORMAT : -APKE_V2PKG_FORMAT;
if (ectx->generate_identity) *ectx->identity = sctx.identity;
apk_sign_ctx_free(&sctx);
free(ectx->desc.ptr);
apk_extract_reset(ectx);
return r;
@ -345,3 +380,16 @@ void apk_extract_v2_control(struct apk_extract_ctx *ectx, apk_blob_t l, apk_blob
EVP_MD_size(sctx->md)));
}
}
int apk_extract_v2_meta(struct apk_extract_ctx *ectx, struct apk_istream *is)
{
apk_blob_t k, v, token = APK_BLOB_STRLIT("\n");
while (apk_istream_get_delim(is, token, &k) == 0) {
if (k.len < 1 || k.ptr[0] == '#') continue;
if (apk_blob_split(k, APK_BLOB_STRLIT(" = "), &k, &v)) {
apk_extract_v2_control(ectx, k, v);
}
}
return 0;
}

View File

@ -592,29 +592,19 @@ static int read_info_line(struct read_info_ctx *ri, apk_blob_t line)
return 0;
}
static int apk_pkg_parse_entry(struct apk_extract_ctx *ectx, const struct apk_file_info *ae,
struct apk_istream *is)
static int apk_pkg_v2meta(struct apk_extract_ctx *ectx, struct apk_istream *is)
{
struct read_info_ctx *ri = container_of(ectx, struct read_info_ctx, ectx);
struct apk_package *pkg = ri->pkg;
if (ectx->metadata_verified) return -ECANCELED;
if (!ectx->metadata) return 0;
if (strcmp(ae->name, ".PKGINFO") == 0) {
/* APK 2.0 format */
apk_blob_t l, token = APK_BLOB_STR("\n");
while (apk_istream_get_delim(is, token, &l) == 0)
read_info_line(ri, l);
} else if (strcmp(ae->name, ".INSTALL") == 0) {
apk_warn(&ri->db->ctx->out,
"Package '%s-" BLOB_FMT "' contains deprecated .INSTALL",
pkg->name->name, BLOB_PRINTF(*pkg->version));
}
apk_blob_t l, token = APK_BLOB_STR("\n");
while (apk_istream_get_delim(is, token, &l) == 0)
read_info_line(ri, l);
return 0;
}
static const struct apk_extract_ops extract_pkgmeta_ops = {
.v2meta = apk_pkg_v2meta,
};
int apk_pkg_read(struct apk_database *db, const char *file, struct apk_package **pkg)
{
struct read_info_ctx ctx;
@ -633,12 +623,11 @@ int apk_pkg_read(struct apk_database *db, const char *file, struct apk_package *
goto err;
ctx.pkg->size = fi.size;
apk_extract_init(&ctx.ectx, db->ctx, apk_pkg_parse_entry);
apk_extract_init(&ctx.ectx, db->ctx, &extract_pkgmeta_ops);
apk_extract_generate_identity(&ctx.ectx, &ctx.pkg->csum);
r = apk_extract(&ctx.ectx, apk_istream_from_file(AT_FDCWD, file));
if (r < 0 && r != -ECANCELED)
goto err;
if (r < 0) goto err;
if (ctx.pkg->name == NULL || ctx.pkg->uninstallable) {
r = -ENOTSUP;
goto err;