extract: use extraction api, and implement it for v3 packages
The extract applet now works with both v2 and v3 packages.cute-signatures
parent
5843daf7a1
commit
f61eba0627
14
src/adb.c
14
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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
202
src/extract_v3.c
202
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;
|
||||
}
|
||||
|
|
55
src/io.c
55
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;
|
||||
|
|
Loading…
Reference in New Issue