summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/adb.c14
-rw-r--r--src/apk_crypto.h2
-rw-r--r--src/apk_io.h8
-rw-r--r--src/app_extract.c226
-rw-r--r--src/extract_v3.c202
-rw-r--r--src/io.c55
6 files changed, 298 insertions, 209 deletions
diff --git a/src/adb.c b/src/adb.c
index a687aba..277aba6 100644
--- a/src/adb.c
+++ b/src/adb.c
@@ -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:
diff --git a/src/apk_crypto.h b/src/apk_crypto.h
index d3ce24b..4eab130 100644
--- a/src/apk_crypto.h
+++ b/src/apk_crypto.h
@@ -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
diff --git a/src/apk_io.h b/src/apk_io.h
index 3ad0790..762f9e0 100644
--- a/src/apk_io.h
+++ b/src/apk_io.h
@@ -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
diff --git a/src/app_extract.c b/src/app_extract.c
index dd410e5..57d40fe 100644
--- a/src/app_extract.c
+++ b/src/app_extract.c
@@ -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)
-{
- 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);
-}
-
-static int apk_extract_next_file(struct extract_ctx *ctx)
+static int extract_v3_meta(struct apk_extract_ctx *ectx, struct adb *db)
{
- apk_blob_t target;
- 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);
- }
-
- 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);
+ return 0;
}
-static int apk_extract_data_block(struct adb *db, struct adb_block *b, struct apk_istream *is)
+static int extract_file(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, 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);
+ struct extract_ctx *ctx = container_of(ectx, struct extract_ctx, ectx);
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);
-}
+ if (fi->uvol_name) return apk_extract_volume(ectx->ac, fi, 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 = 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);
- 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;
diff --git a/src/extract_v3.c b/src/extract_v3.c
index c6e80df..156bcbc 100644
--- a/src/extract_v3.c
+++ b/src/extract_v3.c
@@ -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;
}
diff --git a/src/io.c b/src/io.c
index d78959b..218e008 100644
--- a/src/io.c
+++ b/src/io.c
@@ -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;