extract: use extraction api, and implement it for v3 packages

The extract applet now works with both v2 and v3 packages.
cute-signatures
Timo Teräs 2021-07-30 16:38:53 +03:00
parent 5843daf7a1
commit f61eba0627
6 changed files with 298 additions and 209 deletions

View File

@ -198,12 +198,13 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
r = -APKE_ADB_BLOCK;
break;
}
sz = adb_block_size(&blk) - sizeof blk;
sz = adb_block_length(&blk);
switch (type) {
case ADB_BLOCK_ADB:
allowed = BIT(ADB_BLOCK_SIG) | BIT(ADB_BLOCK_DATA) | BIT(ADB_BLOCK_DATAX);
db->adb.ptr = malloc(sz);
db->adb.len = adb_block_length(&blk);
db->adb.len = sz;
if (db->adb.len < 16) {
r = -APKE_ADB_BLOCK;
goto err;
@ -215,7 +216,7 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
}
r = cb(db, &blk, apk_istream_from_blob(&seg.is, db->adb));
if (r < 0) goto err;
continue;
goto skip_padding;
case ADB_BLOCK_SIG:
sig = apk_istream_peek(is, sz);
if (IS_ERR(sig)) {
@ -223,7 +224,7 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
goto err;
}
if (!trusted &&
adb_trust_verify_signature(t, db, &vfy, APK_BLOB_PTR_LEN(sig, adb_block_length(&blk))) == 0)
adb_trust_verify_signature(t, db, &vfy, APK_BLOB_PTR_LEN(sig, sz)) == 0)
trusted = 1;
break;
case ADB_BLOCK_DATA:
@ -237,8 +238,11 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
apk_istream_segment(&seg, is, sz, 0);
r = cb(db, &blk, &seg.is);
r = apk_istream_close_error(&seg.is, r);
if (r < 0) break;
r = apk_istream_close(&seg.is);
skip_padding:
r = apk_istream_read(is, 0, adb_block_padding(&blk));
if (r < 0) break;
} while (1);
err:

View File

@ -82,9 +82,9 @@ static inline int apk_digest_calc(struct apk_digest *d, uint8_t alg, const void
}
static inline int apk_digest_ctx_init(struct apk_digest_ctx *dctx, uint8_t alg) {
dctx->alg = alg;
dctx->mdctx = EVP_MD_CTX_new();
if (!dctx->mdctx) return -ENOMEM;
dctx->alg = alg;
#ifdef EVP_MD_CTX_FLAG_FINALISE
EVP_MD_CTX_set_flags(dctx->mdctx, EVP_MD_CTX_FLAG_FINALISE);
#endif

View File

@ -135,6 +135,14 @@ struct apk_segment_istream {
};
struct apk_istream *apk_istream_segment(struct apk_segment_istream *sis, struct apk_istream *is, size_t len, time_t mtime);
struct apk_digest_istream {
struct apk_istream is;
struct apk_istream *pis;
struct apk_digest *digest;
struct apk_digest_ctx dctx;
};
struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct apk_istream *is, struct apk_digest *d);
#define APK_ISTREAM_TEE_COPY_META 1
#define APK_ISTREAM_TEE_OPTIONAL 2

View File

@ -16,22 +16,15 @@
#include "apk_applet.h"
#include "apk_print.h"
#include "apk_adb.h"
#include "apk_pathbuilder.h"
#include "apk_extract.h"
struct extract_ctx {
const char *destination;
unsigned int extract_flags;
struct apk_extract_ctx ectx;
struct apk_ctx *ac;
struct adb db;
int root_fd;
struct adb_obj pkg, paths, path, files, file;
unsigned int cur_path, cur_file;
struct apk_pathbuilder pb;
};
@ -63,22 +56,6 @@ static const struct apk_option_group optgroup_applet = {
.parse = option_parse_applet,
};
static void apk_extract_acl(struct apk_file_info *fi, struct adb_obj *o, struct apk_id_cache *idc)
{
fi->mode = adb_ro_int(o, ADBI_ACL_MODE);
fi->uid = apk_id_cache_resolve_uid(idc, adb_ro_blob(o, ADBI_ACL_USER), 65534);
fi->gid = apk_id_cache_resolve_gid(idc, adb_ro_blob(o, ADBI_ACL_GROUP), 65534);
}
static const char *uvol_detect(struct apk_ctx *ac, const char *path)
{
if (!apk_ctx_get_uvol(ac)) return 0;
if (strncmp(path, "uvol", 4) != 0) return 0;
if (path[4] == 0) return path;
if (path[4] == '/') return &path[5];
return 0;
}
static int uvol_run(struct apk_ctx *ac, char *action, const char *volname, char *arg1, char *arg2)
{
struct apk_out *out = &ac->out;
@ -103,7 +80,7 @@ static int uvol_run(struct apk_ctx *ac, char *action, const char *volname, char
return 0;
}
static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off_t sz, struct apk_istream *is, struct apk_digest_ctx *dctx)
static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off_t sz, struct apk_istream *is)
{
struct apk_out *out = &ac->out;
struct apk_ostream *os;
@ -124,7 +101,7 @@ static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off
}
close(pipefds[0]);
os = apk_ostream_to_fd(pipefds[1]);
apk_stream_copy(is, os, sz, 0, 0, dctx);
apk_stream_copy(is, os, sz, 0, 0, 0);
r = apk_ostream_close(os);
if (r != 0) {
if (r >= 0) r = -APKE_UVOL;
@ -141,7 +118,7 @@ static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off
return 0;
}
static int apk_extract_volume(struct apk_ctx *ac, struct apk_file_info *fi, struct apk_istream *is, struct apk_digest_ctx *dctx)
static int apk_extract_volume(struct apk_ctx *ac, const struct apk_file_info *fi, struct apk_istream *is)
{
char size[64];
int r;
@ -149,195 +126,39 @@ static int apk_extract_volume(struct apk_ctx *ac, struct apk_file_info *fi, stru
snprintf(size, sizeof size, "%ju", fi->size);
r = uvol_run(ac, "create", fi->uvol_name, size, "ro");
if (r != 0) return r;
return uvol_extract(ac, fi->uvol_name, size, fi->size, is, dctx);
}
static int apk_extract_adb_file(struct extract_ctx *ctx, off_t sz, struct apk_istream *is)
{
struct apk_ctx *ac = ctx->ac;
struct apk_out *out = &ac->out;
const char *path_name = apk_pathbuilder_cstr(&ctx->pb);
struct apk_file_info fi = {
.name = path_name,
.uvol_name = uvol_detect(ac, path_name),
.size = adb_ro_int(&ctx->file, ADBI_FI_SIZE),
.mtime = adb_ro_int(&ctx->file, ADBI_FI_MTIME),
};
struct adb_obj acl;
struct apk_digest_ctx dctx;
struct apk_digest d;
apk_blob_t target;
int r;
apk_extract_acl(&fi, adb_ro_obj(&ctx->file, ADBI_FI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac));
target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET);
if (!APK_BLOB_IS_NULL(target)) {
char *target_path;
uint16_t mode;
if (target.len < 2) return -APKE_ADB_SCHEMA;
mode = *(uint16_t*)target.ptr;
target.ptr += 2;
target.len -= 2;
switch (mode) {
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
if (target.len != sizeof(uint64_t)) return -APKE_ADB_SCHEMA;
struct unaligned64 {
uint64_t value;
} __attribute__((packed));
fi.device = ((struct unaligned64 *)target.ptr)->value;
break;
case S_IFLNK:
target_path = alloca(target.len + 1);
memcpy(target_path, target.ptr, target.len);
target_path[target.len] = 0;
fi.link_target = target_path;
break;
default:
return -APKE_ADB_SCHEMA;
}
fi.mode |= mode;
return apk_extract_file(
ctx->root_fd, &fi, 0, 0, is, 0, 0, 0,
ctx->extract_flags, out);
}
apk_digest_from_blob(&fi.digest, adb_ro_blob(&ctx->file, ADBI_FI_HASHES));
if (fi.digest.alg == APK_DIGEST_NONE) return -APKE_ADB_SCHEMA;
fi.mode |= S_IFREG;
apk_digest_ctx_init(&dctx, fi.digest.alg);
if (fi.uvol_name) {
r = apk_extract_volume(ac, &fi, is, &dctx);
} else {
r = apk_extract_file(
ctx->root_fd, &fi, 0, 0, is, 0, 0, &dctx,
ctx->extract_flags, out);
if (r < 0) return r;
}
apk_digest_ctx_final(&dctx, &d);
apk_digest_ctx_free(&dctx);
if (r == 0 && apk_digest_cmp(&fi.digest, &d) != 0)
r = -APKE_FILE_INTEGRITY;
if (fi.uvol_name) {
if (r == 0)
r = uvol_run(ac, "up", fi.uvol_name, 0, 0);
else
uvol_run(ac, "remove", fi.uvol_name, 0, 0);
} else if (r != 0)
unlinkat(ctx->root_fd, fi.name, 0);
r = uvol_extract(ac, fi->uvol_name, size, fi->size, is);
if (r == 0) r = uvol_run(ac, "up", fi->uvol_name, 0, 0);
if (r != 0) uvol_run(ac, "remove", fi->uvol_name, 0, 0);
return r;
}
static int apk_extract_directory(struct extract_ctx *ctx)
static int extract_v3_meta(struct apk_extract_ctx *ectx, struct adb *db)
{
struct apk_ctx *ac = ctx->ac;
struct apk_out *out = &ac->out;
struct apk_file_info fi = {
.name = apk_pathbuilder_cstr(&ctx->pb),
};
struct adb_obj acl;
if (uvol_detect(ac, fi.name)) return 0;
apk_extract_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac));
fi.mode |= S_IFDIR;
return apk_extract_file(
ctx->root_fd, &fi, 0, 0, 0, 0, 0, 0,
ctx->extract_flags, out);
return 0;
}
static int apk_extract_next_file(struct extract_ctx *ctx)
static int extract_file(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, struct apk_istream *is)
{
apk_blob_t target;
struct extract_ctx *ctx = container_of(ectx, struct extract_ctx, ectx);
int r;
if (!ctx->cur_path) {
// one time init
ctx->cur_path = ADBI_FIRST;
ctx->cur_file = 0;
adb_r_rootobj(&ctx->db, &ctx->pkg, &schema_package);
adb_ro_obj(&ctx->pkg, ADBI_PKG_PATHS, &ctx->paths);
adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path);
adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files);
}
if (fi->uvol_name) return apk_extract_volume(ectx->ac, fi, is);
do {
ctx->cur_file++;
while (ctx->cur_file > adb_ra_num(&ctx->files)) {
ctx->cur_path++;
ctx->cur_file = ADBI_FIRST;
if (ctx->cur_path > adb_ra_num(&ctx->paths)) return 1;
adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path);
apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME));
adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files);
r = apk_extract_directory(ctx);
if (r != 0) return r;
}
adb_ro_obj(&ctx->files, ctx->cur_file, &ctx->file);
apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME));
apk_pathbuilder_pushb(&ctx->pb, adb_ro_blob(&ctx->file, ADBI_FI_NAME));
target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET);
if (adb_ro_int(&ctx->file, ADBI_FI_SIZE) != 0 &&
APK_BLOB_IS_NULL(target)) {
return 0;
}
r = apk_extract_adb_file(ctx, 0, 0);
if (r != 0) return r;
} while (1);
}
r = apk_extract_file(ctx->root_fd, fi, 0, 0, is, 0, 0, 0,
ctx->extract_flags, &ectx->ac->out);
r = apk_istream_close_error(is, r);
static int apk_extract_data_block(struct adb *db, struct adb_block *b, struct apk_istream *is)
{
struct extract_ctx *ctx = container_of(db, struct extract_ctx, db);
struct adb_data_package *hdr;
size_t sz = adb_block_length(b);
int r;
if (adb_block_type(b) != ADB_BLOCK_DATA) return 0;
r = apk_extract_next_file(ctx);
if (r != 0) {
if (r > 0) r = -APKE_ADB_BLOCK;
return r;
}
hdr = apk_istream_get(is, sizeof *hdr);
sz -= sizeof *hdr;
if (IS_ERR(hdr)) return PTR_ERR(hdr);
if (hdr->path_idx != ctx->cur_path ||
hdr->file_idx != ctx->cur_file ||
sz != adb_ro_int(&ctx->file, ADBI_FI_SIZE)) {
// got data for some unexpected file
return -APKE_ADB_BLOCK;
}
return apk_extract_adb_file(ctx, sz, is);
}
static int apk_extract_pkg(struct extract_ctx *ctx, const char *fn)
{
struct apk_ctx *ac = ctx->ac;
struct apk_trust *trust = apk_ctx_get_trust(ac);
int r;
r = adb_m_process(&ctx->db,
adb_decompress(apk_istream_from_fd_url(AT_FDCWD, fn, apk_ctx_since(ac, 0)), 0),
ADB_SCHEMA_PACKAGE, trust, apk_extract_data_block);
if (r == 0) {
r = apk_extract_next_file(ctx);
if (r == 0) r = -APKE_ADB_BLOCK;
if (r == 1) r = 0;
}
adb_free(&ctx->db);
if (r != 0) unlinkat(ctx->root_fd, fi->name, 0);
return r;
}
static const struct apk_extract_ops extract_ops = {
.v2meta = apk_extract_v2_meta,
.v3meta = extract_v3_meta,
.file = extract_file,
};
static int extract_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
{
struct extract_ctx *ctx = pctx;
@ -356,9 +177,10 @@ static int extract_main(void *pctx, struct apk_ctx *ac, struct apk_string_array
return r;
}
apk_extract_init(&ctx->ectx, ac, &extract_ops);
foreach_array_item(parg, args) {
apk_out(out, "Extracting %s...", *parg);
r = apk_extract_pkg(ctx, *parg);
r = apk_extract(&ctx->ectx, apk_istream_from_fd_url(AT_FDCWD, *parg, apk_ctx_since(ac, 0)));
if (r != 0) {
apk_err(out, "%s: %s", *parg, apk_error_str(r));
break;

View File

@ -7,10 +7,210 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
#include <sys/stat.h>
#include "apk_context.h"
#include "apk_extract.h"
#include "apk_adb.h"
#include "apk_pathbuilder.h"
struct apk_extract_v3_ctx {
struct apk_extract_ctx *ectx;
struct adb db;
struct adb_obj pkg, paths, path, files, file;
unsigned int cur_path, cur_file;
struct apk_pathbuilder pb;
};
static const char *uvol_detect(struct apk_ctx *ac, const char *path)
{
if (!apk_ctx_get_uvol(ac)) return 0;
if (strncmp(path, "uvol", 4) != 0) return 0;
if (path[4] == 0) return path;
if (path[4] == '/') return &path[5];
return 0;
}
static void apk_extract_v3_acl(struct apk_file_info *fi, struct adb_obj *o, struct apk_id_cache *idc)
{
fi->mode = adb_ro_int(o, ADBI_ACL_MODE);
fi->uid = apk_id_cache_resolve_uid(idc, adb_ro_blob(o, ADBI_ACL_USER), 65534);
fi->gid = apk_id_cache_resolve_gid(idc, adb_ro_blob(o, ADBI_ACL_GROUP), 65534);
}
static int apk_extract_v3_file(struct apk_extract_ctx *ectx, off_t sz, struct apk_istream *is)
{
struct apk_extract_v3_ctx *ctx = ectx->pctx;
struct apk_ctx *ac = ectx->ac;
const char *path_name = apk_pathbuilder_cstr(&ctx->pb);
struct apk_file_info fi = {
.name = path_name,
.uvol_name = uvol_detect(ac, path_name),
.size = adb_ro_int(&ctx->file, ADBI_FI_SIZE),
.mtime = adb_ro_int(&ctx->file, ADBI_FI_MTIME),
};
struct adb_obj acl;
struct apk_digest_istream dis;
apk_blob_t target;
int r;
apk_extract_v3_acl(&fi, adb_ro_obj(&ctx->file, ADBI_FI_ACL, &acl), apk_ctx_get_id_cache(ectx->ac));
target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET);
if (!APK_BLOB_IS_NULL(target)) {
char *target_path;
uint16_t mode;
if (target.len < 2) return -APKE_ADB_SCHEMA;
mode = *(uint16_t*)target.ptr;
target.ptr += 2;
target.len -= 2;
switch (mode) {
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
if (target.len != sizeof(uint64_t)) return -APKE_ADB_SCHEMA;
struct unaligned64 {
uint64_t value;
} __attribute__((packed));
fi.device = ((struct unaligned64 *)target.ptr)->value;
break;
case S_IFLNK:
target_path = alloca(target.len + 1);
memcpy(target_path, target.ptr, target.len);
target_path[target.len] = 0;
fi.link_target = target_path;
break;
default:
return -APKE_ADB_SCHEMA;
}
fi.mode |= mode;
return ectx->ops->file(ectx, &fi, is);
}
apk_digest_from_blob(&fi.digest, adb_ro_blob(&ctx->file, ADBI_FI_HASHES));
if (fi.digest.alg == APK_DIGEST_NONE) return -APKE_ADB_SCHEMA;
fi.mode |= S_IFREG;
r = ectx->ops->file(ectx, &fi, apk_istream_verify(&dis, is, &fi.digest));
return apk_istream_close_error(&dis.is, r);
}
static int apk_extract_v3_directory(struct apk_extract_ctx *ectx)
{
struct apk_extract_v3_ctx *ctx = ectx->pctx;
struct apk_ctx *ac = ectx->ac;
struct apk_file_info fi = {
.name = apk_pathbuilder_cstr(&ctx->pb),
};
struct adb_obj acl;
if (uvol_detect(ac, fi.name)) return 0;
apk_extract_v3_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ectx->ac));
fi.mode |= S_IFDIR;
return ectx->ops->file(ectx, &fi, 0);
}
static int apk_extract_v3_next_file(struct apk_extract_ctx *ectx)
{
struct apk_extract_v3_ctx *ctx = ectx->pctx;
apk_blob_t target;
int r;
if (!ctx->cur_path) {
r = ectx->ops->v3meta(ectx, &ctx->db);
if (r < 0) return r;
// one time init
ctx->cur_path = ADBI_FIRST;
ctx->cur_file = 0;
adb_r_rootobj(&ctx->db, &ctx->pkg, &schema_package);
adb_ro_obj(&ctx->pkg, ADBI_PKG_PATHS, &ctx->paths);
adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path);
adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files);
if (!ectx->ops->file) return -ECANCELED;
}
do {
ctx->cur_file++;
while (ctx->cur_file > adb_ra_num(&ctx->files)) {
ctx->cur_path++;
ctx->cur_file = ADBI_FIRST;
if (ctx->cur_path > adb_ra_num(&ctx->paths)) return 1;
adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path);
apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME));
adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files);
r = apk_extract_v3_directory(ectx);
if (r != 0) return r;
}
adb_ro_obj(&ctx->files, ctx->cur_file, &ctx->file);
apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME));
apk_pathbuilder_pushb(&ctx->pb, adb_ro_blob(&ctx->file, ADBI_FI_NAME));
target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET);
if (adb_ro_int(&ctx->file, ADBI_FI_SIZE) != 0 &&
APK_BLOB_IS_NULL(target)) {
return 0;
}
r = apk_extract_v3_file(ectx, 0, 0);
if (r != 0) return r;
} while (1);
}
static int apk_extract_v3_data_block(struct adb *db, struct adb_block *b, struct apk_istream *is)
{
struct apk_extract_v3_ctx *ctx = container_of(db, struct apk_extract_v3_ctx, db);
struct apk_extract_ctx *ectx = ctx->ectx;
struct adb_data_package *hdr;
size_t sz = adb_block_length(b);
int r;
if (adb_block_type(b) != ADB_BLOCK_DATA) return 0;
r = apk_extract_v3_next_file(ectx);
if (r != 0) {
if (r > 0) r = -APKE_ADB_BLOCK;
return r;
}
hdr = apk_istream_get(is, sizeof *hdr);
sz -= sizeof *hdr;
if (IS_ERR(hdr)) return PTR_ERR(hdr);
if (hdr->path_idx != ctx->cur_path ||
hdr->file_idx != ctx->cur_file ||
sz != adb_ro_int(&ctx->file, ADBI_FI_SIZE)) {
// got data for some unexpected file
return -APKE_ADB_BLOCK;
}
return apk_extract_v3_file(ectx, sz, is);
}
int apk_extract_v3(struct apk_extract_ctx *ectx, struct apk_istream *is)
{
return apk_istream_close_error(is, -APKE_FORMAT_NOT_SUPPORTED);
struct apk_ctx *ac = ectx->ac;
struct apk_trust *trust = apk_ctx_get_trust(ac);
struct apk_extract_v3_ctx ctx = {
.ectx = ectx,
};
int r;
if (IS_ERR(is)) return PTR_ERR(is);
if (!ectx->ops || !ectx->ops->v3meta)
return apk_istream_close_error(is, -APKE_FORMAT_NOT_SUPPORTED);
ectx->pctx = &ctx;
r = adb_m_process(&ctx.db, adb_decompress(is, 0),
ADB_SCHEMA_PACKAGE, trust, apk_extract_v3_data_block);
if (r == 0) {
r = apk_extract_v3_next_file(ectx);
if (r == 0) r = -APKE_ADB_BLOCK;
if (r == 1) r = 0;
}
if (r == -ECANCELED) r = 0;
adb_free(&ctx.db);
ectx->pctx = 0;
return r;
}

View File

@ -302,6 +302,61 @@ struct apk_istream *apk_istream_segment(struct apk_segment_istream *sis, struct
return &sis->is;
}
static void digest_get_meta(struct apk_istream *is, struct apk_file_meta *meta)
{
struct apk_digest_istream *dis = container_of(is, struct apk_digest_istream, is);
return apk_istream_get_meta(dis->pis, meta);
}
static ssize_t digest_read(struct apk_istream *is, void *ptr, size_t size)
{
struct apk_digest_istream *dis = container_of(is, struct apk_digest_istream, is);
ssize_t r;
r = dis->pis->ops->read(dis->pis, ptr, size);
if (r > 0) apk_digest_ctx_update(&dis->dctx, ptr, r);
return r;
}
static int digest_close(struct apk_istream *is)
{
struct apk_digest_istream *dis = container_of(is, struct apk_digest_istream, is);
if (dis->digest) {
struct apk_digest res;
apk_digest_ctx_final(&dis->dctx, &res);
if (apk_digest_cmp(&res, dis->digest) != 0)
apk_istream_error(is, -APKE_FILE_INTEGRITY);
dis->digest = 0;
}
apk_digest_ctx_free(&dis->dctx);
return is->err < 0 ? is->err : 0;
}
static const struct apk_istream_ops digest_istream_ops = {
.get_meta = digest_get_meta,
.read = digest_read,
.close = digest_close,
};
struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct apk_istream *is, struct apk_digest *d)
{
*dis = (struct apk_digest_istream) {
.is.ops = &digest_istream_ops,
.is.buf = is->buf,
.is.buf_size = is->buf_size,
.is.ptr = is->ptr,
.is.end = is->end,
.pis = is,
.digest = d,
};
apk_digest_ctx_init(&dis->dctx, d->alg);
if (dis->is.ptr != dis->is.end)
apk_digest_ctx_update(&dis->dctx, dis->is.ptr, dis->is.end - dis->is.ptr);
return &dis->is;
}
struct apk_tee_istream {
struct apk_istream is;
struct apk_istream *inner_is;