diff --git a/src/adb.c b/src/adb.c index 44b3589..e86e243 100644 --- a/src/adb.c +++ b/src/adb.c @@ -503,10 +503,12 @@ adb_val_t adb_w_blob(struct adb *db, apk_blob_t b) val.u16 = htole16(n); vec[0].iov_len = sizeof val.u16; o = ADB_TYPE_BLOB_16; - } else { + } else if (n > 0) { val.u8 = n; vec[0].iov_len = sizeof val.u8; o = ADB_TYPE_BLOB_8; + } else { + return ADB_VAL_NULL; } return ADB_VAL(o, adb_w_data(db, vec, ARRAY_SIZE(vec), vec[0].iov_len)); @@ -712,7 +714,7 @@ adb_val_t adb_wa_append(struct adb_obj *o, adb_val_t v) assert(o->schema->kind == ADB_KIND_ARRAY); if (o->num >= o->obj[ADBI_NUM_ENTRIES]) return adb_w_error(o->db, E2BIG); if (ADB_IS_ERROR(v)) return adb_w_error(o->db, ADB_VAL_VALUE(v)); - o->obj[o->num++] = v; + if (v != ADB_VAL_NULL) o->obj[o->num++] = v; return v; } diff --git a/src/adb.h b/src/adb.h index 413ef36..cf3da69 100644 --- a/src/adb.h +++ b/src/adb.h @@ -236,4 +236,39 @@ struct adb_xfrm { }; int adb_c_xfrm(struct adb_xfrm *, int (*cb)(struct adb_xfrm *, struct adb_block *, struct apk_istream *)); +/* SAX style event based handling of ADB */ + +struct adb_db_schema { + unsigned long magic; + const struct adb_object_schema *root; +}; + +struct adb_walk; +struct adb_walk_ops { + int (*schema)(struct adb_walk *, uint32_t schema_id); + int (*comment)(struct adb_walk *, apk_blob_t comment); + int (*start_array)(struct adb_walk *, unsigned int num_items); + int (*start_object)(struct adb_walk *); + int (*end)(struct adb_walk *); + int (*key)(struct adb_walk *, apk_blob_t key_name); + int (*scalar)(struct adb_walk *, apk_blob_t scalar, int multiline); +}; + +extern const struct adb_walk_ops adb_walk_gentext_ops; + +struct adb_walk { + const struct adb_walk_ops *ops; + const struct adb_db_schema *schemas; +}; + +struct adb_walk_gentext { + struct adb_walk d; + FILE *out; + int nest; + int line_started : 1; + int key_printed : 1; +}; + +int adb_walk_adb(struct adb_walk *d, struct adb *db, struct apk_trust *trust); + #endif diff --git a/src/adb_walk_adb.c b/src/adb_walk_adb.c new file mode 100644 index 0000000..713d5a7 --- /dev/null +++ b/src/adb_walk_adb.c @@ -0,0 +1,166 @@ +#include "adb.h" + +#include +#include +#include +#include "apk_adb.h" +#include "apk_applet.h" +#include "apk_print.h" + +struct adb_walk_ctx { + struct adb_walk *d; + struct adb *db; + struct apk_trust *trust; +}; + +static int dump_object(struct adb_walk_ctx *ctx, const struct adb_object_schema *schema, adb_val_t v); +static int dump_adb(struct adb_walk_ctx *ctx); + +static int dump_item(struct adb_walk_ctx *ctx, const char *name, const uint8_t *kind, adb_val_t v) +{ + struct adb_walk *d = ctx->d; + struct adb db, *origdb; + struct adb_obj o; + struct adb_object_schema *obj_schema; + char tmp[256]; + apk_blob_t b; + + if (v == ADB_VAL_NULL) return 0; + + d->ops->key(d, name ? APK_BLOB_STR(name) : APK_BLOB_NULL); + + switch (*kind) { + case ADB_KIND_ARRAY: + obj_schema = container_of(kind, struct adb_object_schema, kind); + adb_r_obj(ctx->db, v, &o, obj_schema); + //if (!adb_ra_num(&o)) return 0; + + d->ops->start_array(d, adb_ra_num(&o)); + for (size_t i = ADBI_FIRST; i <= adb_ra_num(&o); i++) { + dump_item(ctx, NULL, obj_schema->fields[0].kind, adb_ro_val(&o, i)); + } + d->ops->end(d); + break; + case ADB_KIND_ADB: + db.hdr.schema = container_of(kind, struct adb_adb_schema, kind)->schema_id; + db.data = adb_r_blob(ctx->db, v); + origdb = ctx->db; + ctx->db = &db; + d->ops->start_object(d); + dump_adb(ctx); + d->ops->end(d); + ctx->db = origdb; + break; + case ADB_KIND_OBJECT: + d->ops->start_object(d); + dump_object(ctx, container_of(kind, struct adb_object_schema, kind), v); + d->ops->end(d); + break; + case ADB_KIND_BLOB: + case ADB_KIND_INT:; + struct adb_scalar_schema *scalar = container_of(kind, struct adb_scalar_schema, kind); + if (scalar->tostring) { + b = scalar->tostring(ctx->db, v, tmp, sizeof tmp); + } else { + b = APK_BLOB_STR("(unknown)"); + } + if (!APK_BLOB_IS_NULL(b)) + d->ops->scalar(d, b, scalar->multiline); + break; + } + return 0; +} + +static int dump_object(struct adb_walk_ctx *ctx, const struct adb_object_schema *schema, adb_val_t v) +{ + size_t schema_len = 0; + struct adb_obj o; + char tmp[256]; + apk_blob_t b; + struct adb_walk *d = ctx->d; + + adb_r_obj(ctx->db, v, &o, schema); + if (schema) { + if (schema->tostring) { + b = schema->tostring(&o, tmp, sizeof tmp); + if (!APK_BLOB_IS_NULL(b)) + d->ops->scalar(d, b, 0); + return 0; + } + schema_len = schema->num_fields; + } + + for (size_t i = ADBI_FIRST; i < adb_ro_num(&o); i++) { + adb_val_t val = adb_ro_val(&o, i); + if (val == ADB_NULL) continue; + if (i < schema_len && schema->fields[i-1].kind != 0) { + dump_item(ctx, schema->fields[i-1].name, schema->fields[i-1].kind, val); + } + } + return 0; +} + +static int dump_adb(struct adb_walk_ctx *ctx) +{ + char tmp[256]; + struct adb_block *blk; + struct adb_sign_hdr *s; + struct adb_verify_ctx vfy = {}; + unsigned char *id; + uint32_t schema_magic = ctx->db->hdr.schema; + const struct adb_db_schema *ds; + struct adb_walk *d = ctx->d; + int r, len; + + for (ds = d->schemas; ds->magic; ds++) + if (ds->magic == schema_magic) break; + + adb_foreach_block(blk, ctx->db->data) { + apk_blob_t b = APK_BLOB_PTR_LEN((char*)(blk+1), ADB_BLOCK_SIZE(blk)); + switch (ADB_BLOCK_TYPE(blk)) { + case ADB_BLOCK_ADB: + len = snprintf(tmp, sizeof tmp, "ADB block, size: %u", ADB_BLOCK_SIZE(blk)); + d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len)); + if (ds->root) { + ctx->db->adb = b; + dump_object(ctx, ds->root, adb_r_root(ctx->db)); + } + break; + case ADB_BLOCK_SIG: + s = (struct adb_sign_hdr*) b.ptr; + r = adb_trust_verify_signature(ctx->trust, ctx->db, &vfy, b); + + len = snprintf(tmp, sizeof tmp, "signature: v%d ", s->sign_ver); + switch (s->sign_ver) { + case 0: + id = (unsigned char*)(s + 1); + for (size_t j = 0; j < 16; j++) + len += snprintf(&tmp[len], sizeof tmp - len, "%02x", id[j]); + break; + default: + break; + } + len += snprintf(&tmp[len], sizeof tmp - len, ": %s", r ? apk_error_str(r) : "OK"); + d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len)); + break; + default: + len = snprintf(tmp, sizeof tmp, "unknown block %d, size: %d", + ADB_BLOCK_TYPE(blk), ADB_BLOCK_SIZE(blk)); + d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len)); + } + } + if (IS_ERR(blk)) { + d->ops->comment(d, APK_BLOB_STRLIT("block enumeration error: corrupt data area")); + } + return 0; +} + +int adb_walk_adb(struct adb_walk *d, struct adb *db, struct apk_trust *trust) +{ + struct adb_walk_ctx ctx = { + .d = d, + .db = db, + .trust = trust, + }; + return dump_adb(&ctx); +} diff --git a/src/adb_walk_gentext.c b/src/adb_walk_gentext.c new file mode 100644 index 0000000..995dda3 --- /dev/null +++ b/src/adb_walk_gentext.c @@ -0,0 +1,141 @@ +#include "adb.h" +#include "apk_print.h" + +static void adb_walk_gentext_indent(struct adb_walk_gentext *dt) +{ + int i; + + if (!dt->line_started) { + for (i = 0; i < dt->nest; i++) { + fprintf(dt->out, " "); + } + } else { + fprintf(dt->out, " "); + } + dt->line_started = 1; +} + +static void adb_walk_gentext_newline(struct adb_walk_gentext *dt) +{ + dt->line_started = 0; + dt->key_printed = 0; +} + +static int adb_walk_gentext_schema(struct adb_walk *d, uint32_t schema_id) +{ + struct adb_walk_gentext *dt = container_of(d, struct adb_walk_gentext, d); + FILE *out = dt->out; + + adb_walk_gentext_indent(dt); + fprintf(out, "#%%SCHEMA: %08X\n", schema_id); + adb_walk_gentext_newline(dt); + return 0; +} + +static int adb_walk_gentext_comment(struct adb_walk *d, apk_blob_t comment) +{ + struct adb_walk_gentext *dt = container_of(d, struct adb_walk_gentext, d); + FILE *out = dt->out; + + adb_walk_gentext_indent(dt); + fprintf(out, "# "BLOB_FMT"\n", BLOB_PRINTF(comment)); + adb_walk_gentext_newline(dt); + return 0; +} + +static int adb_walk_gentext_start_array(struct adb_walk *d, unsigned int num) +{ + struct adb_walk_gentext *dt = container_of(d, struct adb_walk_gentext, d); + FILE *out = dt->out; + + adb_walk_gentext_indent(dt); + fprintf(out, "# %d items\n", num); + adb_walk_gentext_newline(dt); + dt->nest++; + return 0; +} + +static int adb_walk_gentext_start_object(struct adb_walk *d) +{ + struct adb_walk_gentext *dt = container_of(d, struct adb_walk_gentext, d); + + dt->nest++; + return 0; +} + +static int adb_walk_gentext_end(struct adb_walk *d) +{ + struct adb_walk_gentext *dt = container_of(d, struct adb_walk_gentext, d); + FILE *out = dt->out; + + if (dt->line_started) { + adb_walk_gentext_indent(dt); + fprintf(out, "# empty object\n"); + adb_walk_gentext_newline(dt); + } + dt->nest--; + return 0; +} + +static int adb_walk_gentext_key(struct adb_walk *d, apk_blob_t key) +{ + struct adb_walk_gentext *dt = container_of(d, struct adb_walk_gentext, d); + FILE *out = dt->out; + + if (!APK_BLOB_IS_NULL(key)) { + if (dt->key_printed) { + fprintf(out, "\n"); + adb_walk_gentext_newline(dt); + } + adb_walk_gentext_indent(dt); + fprintf(out, BLOB_FMT":", BLOB_PRINTF(key)); + dt->key_printed = 1; + } else { + adb_walk_gentext_indent(dt); + fprintf(out, "-"); + } + return 0; +} + +static int adb_walk_gentext_scalar(struct adb_walk *d, apk_blob_t scalar, int multiline) +{ + struct adb_walk_gentext *dt = container_of(d, struct adb_walk_gentext, d); + FILE *out = dt->out; + apk_blob_t nl = APK_BLOB_STR("\n"); + + adb_walk_gentext_indent(dt); + + if (scalar.len >= 60 || multiline) { + /* long or multiline */ + apk_blob_t l; + + fprintf(out, "|\n"); + adb_walk_gentext_newline(dt); + dt->nest++; + while (apk_blob_split(scalar, nl, &l, &scalar)) { + adb_walk_gentext_indent(dt); + fprintf(out, BLOB_FMT"\n", BLOB_PRINTF(l)); + adb_walk_gentext_newline(dt); + } + if (scalar.len) { + adb_walk_gentext_indent(dt); + fprintf(out, BLOB_FMT"\n", BLOB_PRINTF(scalar)); + adb_walk_gentext_newline(dt); + } + dt->nest--; + } else { + fprintf(out, BLOB_FMT"\n", BLOB_PRINTF(scalar)); + adb_walk_gentext_newline(dt); + } + return 0; +} + +const struct adb_walk_ops adb_walk_gentext_ops = { + .schema = adb_walk_gentext_schema, + .comment = adb_walk_gentext_comment, + .start_array = adb_walk_gentext_start_array, + .start_object = adb_walk_gentext_start_object, + .end = adb_walk_gentext_end, + .key = adb_walk_gentext_key, + .scalar = adb_walk_gentext_scalar, +}; diff --git a/src/apk_adb.c b/src/apk_adb.c index 2827733..0e08848 100644 --- a/src/apk_adb.c +++ b/src/apk_adb.c @@ -160,7 +160,7 @@ static adb_val_t int_fromstring(struct adb *db, apk_blob_t val) { uint32_t n = apk_blob_pull_uint(&val, 10); if (val.len) return ADB_ERROR(EINVAL); - return adb_w_int(db, n); + return adb_w_int(db, n) ?: ADB_VAL_NULL; } static int int_compare(struct adb *db1, adb_val_t v1, struct adb *db2, adb_val_t v2) diff --git a/src/app_adbdump.c b/src/app_adbdump.c index 40da2c5..5020314 100644 --- a/src/app_adbdump.c +++ b/src/app_adbdump.c @@ -5,218 +5,27 @@ #include "apk_applet.h" #include "apk_print.h" -struct adb_dump_ctx { - struct adb *db; - struct apk_trust *trust; - char prefix[128], *pfx; -}; - -struct adb_db_schema { - unsigned long magic; - const struct adb_object_schema *root; -}; - -static void ctx_nest(struct adb_dump_ctx *ctx, unsigned depth) -{ - while (depth--) *ctx->pfx++ = ' '; - assert(ctx->pfx < &ctx->prefix[ARRAY_SIZE(ctx->prefix)]); - *ctx->pfx = 0; -} - -static void ctx_unnest(struct adb_dump_ctx *ctx, unsigned depth) -{ - ctx->pfx -= depth; - assert(ctx->pfx >= ctx->prefix); - *ctx->pfx = 0; -} - -static void ctx_itemstart(struct adb_dump_ctx *ctx) -{ - ctx->pfx[-2] = '-'; -} - -static void ctx_itemdone(struct adb_dump_ctx *ctx) -{ - memset(ctx->prefix, ' ', ctx->pfx - ctx->prefix); -} - -static void dump_object(struct adb_dump_ctx *ctx, const struct adb_object_schema *schema, adb_val_t v); -static void dump_adb(struct adb_dump_ctx *ctx); - -static void dump_item(struct adb_dump_ctx *ctx, const char *name, const uint8_t *kind, adb_val_t v) -{ - struct adb db, *origdb; - struct adb_obj o; - struct adb_object_schema *obj_schema; - char tmp[256]; - apk_blob_t b, nl = APK_BLOB_STR("\n"); - - switch (*kind) { - case ADB_KIND_ARRAY: - obj_schema = container_of(kind, struct adb_object_schema, kind); - adb_r_obj(ctx->db, v, &o, obj_schema); - if (!adb_ra_num(&o)) return; - - fprintf(stdout, "%s%s: # %u items\n", ctx->prefix, name, adb_ra_num(&o)); - ctx_nest(ctx, 4); - for (size_t i = ADBI_FIRST; i <= adb_ra_num(&o); i++) { - ctx_itemstart(ctx); - dump_item(ctx, NULL, obj_schema->fields[0].kind, adb_ro_val(&o, i)); - ctx_itemdone(ctx); - } - ctx_unnest(ctx, 4); - break; - case ADB_KIND_ADB: - db.hdr.schema = container_of(kind, struct adb_adb_schema, kind)->schema_id; - db.data = adb_r_blob(ctx->db, v); - origdb = ctx->db; - ctx->db = &db; - dump_adb(ctx); - ctx->db = origdb; - break; - case ADB_KIND_OBJECT: - if (name) { - fprintf(stdout, "%s%s:\n", ctx->prefix, name); - ctx_nest(ctx, 4); - } - dump_object(ctx, container_of(kind, struct adb_object_schema, kind), v); - if (name) ctx_unnest(ctx, 4); - break; - case ADB_KIND_BLOB: - case ADB_KIND_INT:; - struct adb_scalar_schema *scalar = container_of(kind, struct adb_scalar_schema, kind); - if (scalar->tostring) { - b = scalar->tostring(ctx->db, v, tmp, sizeof tmp); - } else { - b = APK_BLOB_STR("(unknown)"); - } - if (!APK_BLOB_IS_NULL(b)) { - fputs(ctx->prefix, stdout); - if (name) fprintf(stdout, "%s: ", name); - if (b.len >= 60 || scalar->multiline) { - /* long or multiline */ - apk_blob_t l; - fprintf(stdout, "|\n"); - ctx_itemdone(ctx); - ctx_nest(ctx, 4); - while (apk_blob_split(b, nl, &l, &b)) { - fprintf(stdout, "%s"BLOB_FMT"\n", - ctx->prefix, BLOB_PRINTF(l)); - } - if (b.len) { - fprintf(stdout, "%s"BLOB_FMT"\n", - ctx->prefix, BLOB_PRINTF(b)); - } - ctx_unnest(ctx, 4); - } else { - fwrite(b.ptr, 1, b.len, stdout); - fputc('\n', stdout); - } - } - break; - } -} - -static void dump_object(struct adb_dump_ctx *ctx, const struct adb_object_schema *schema, adb_val_t v) -{ - size_t schema_len = 0; - struct adb_obj o; - char tmp[256]; - apk_blob_t b; - - adb_r_obj(ctx->db, v, &o, schema); - if (schema) { - if (schema->tostring) { - b = schema->tostring(&o, tmp, sizeof tmp); - if (!APK_BLOB_IS_NULL(b)) - fprintf(stdout, "%s"BLOB_FMT"\n", ctx->prefix, BLOB_PRINTF(b)); - ctx_itemdone(ctx); - return; - } - schema_len = schema->num_fields; - } - - for (size_t i = ADBI_FIRST; i < adb_ro_num(&o); i++) { - adb_val_t val = adb_ro_val(&o, i); - if (val == ADB_NULL) continue; - if (i < schema_len && schema->fields[i-1].kind != 0) { - dump_item(ctx, schema->fields[i-1].name, schema->fields[i-1].kind, val); - ctx_itemdone(ctx); - } - } -} - static const struct adb_db_schema dbschemas[] = { { .magic = ADB_SCHEMA_INDEX, .root = &schema_index, }, { .magic = ADB_SCHEMA_INSTALLED_DB, .root = &schema_idb, }, { .magic = ADB_SCHEMA_PACKAGE, .root = &schema_package }, + {}, }; -static void dump_adb(struct adb_dump_ctx *ctx) -{ - struct adb_block *blk; - struct adb_sign_hdr *s; - struct adb_verify_ctx vfy = {}; - const struct adb_db_schema *ds; - unsigned char *id; - uint32_t schema_magic = ctx->db->hdr.schema; - int r; - - for (ds = dbschemas; ds < &dbschemas[ARRAY_SIZE(dbschemas)]; ds++) - if (ds->magic == schema_magic) break; - if (ds >= &dbschemas[ARRAY_SIZE(dbschemas)]) ds = NULL; - - adb_foreach_block(blk, ctx->db->data) { - apk_blob_t b = APK_BLOB_PTR_LEN((char*)(blk+1), ADB_BLOCK_SIZE(blk)); - switch (ADB_BLOCK_TYPE(blk)) { - case ADB_BLOCK_ADB: - fprintf(stdout, "%s# ADB block, size: %d\n", ctx->prefix, ADB_BLOCK_SIZE(blk)); - ctx_itemdone(ctx); - ctx->db->adb = b; - if (ds) - dump_object(ctx, ds->root, adb_r_root(ctx->db)); - else - fprintf(stdout, "%s# Unrecognized schema: 0x%08x\n", ctx->prefix, schema_magic); - break; - case ADB_BLOCK_SIG: - s = (struct adb_sign_hdr*) b.ptr; - fprintf(stdout, "%s# signature: v%d ", ctx->prefix, s->sign_ver); - ctx_itemdone(ctx); - r = adb_trust_verify_signature(ctx->trust, ctx->db, &vfy, b); - switch (s->sign_ver) { - case 0: - id = (unsigned char*)(s + 1); - for (size_t j = 0; j < 16; j++) - fprintf(stdout, "%02x", id[j]); - break; - default: - break; - } - fprintf(stdout, ": %s\n", r ? apk_error_str(r) : "OK"); - break; - default: - fprintf(stdout, "%s# unknown block %d, size: %d\n", - ctx->prefix, ADB_BLOCK_TYPE(blk), ADB_BLOCK_SIZE(blk)); - ctx_itemdone(ctx); - } - } - if (IS_ERR(blk)) fprintf(stdout, "%s# block enumeration error: corrupt data area\n", ctx->prefix); -} - -static int mmap_and_dump_adb(struct apk_trust *trust, int fd) +static int mmap_and_dump_adb(struct apk_trust *trust, int fd, struct apk_out *out) { struct adb db; - struct adb_dump_ctx ctx = { - .db = &db, - .pfx = ctx.prefix, - .trust = trust, + struct adb_walk_gentext td = { + .d.ops = &adb_walk_gentext_ops, + .d.schemas = dbschemas, + .out = out->out, }; int r; r = adb_m_map(&db, fd, 0, NULL); if (r) return r; - dump_adb(&ctx); + adb_walk_adb(&td.d, &db, trust); adb_free(&db); return 0; } @@ -228,7 +37,7 @@ static int adbdump_main(void *pctx, struct apk_ctx *ac, struct apk_string_array int r; foreach_array_item(arg, args) { - r = mmap_and_dump_adb(apk_ctx_get_trust(ac), open(*arg, O_RDONLY)); + r = mmap_and_dump_adb(apk_ctx_get_trust(ac), open(*arg, O_RDONLY), out); if (r) { apk_err(out, "%s: %s", *arg, apk_error_str(r)); return r; diff --git a/src/meson.build b/src/meson.build index bacad3d..43f6c38 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,6 +1,8 @@ libapk_so_version = '2.99.0' libapk_src = [ 'adb.c', + 'adb_walk_adb.c', + 'adb_walk_gentext.c', 'apk_adb.c', 'atom.c', 'blob.c',