summaryrefslogtreecommitdiff
path: root/src/extract_v3.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/extract_v3.c')
-rw-r--r--src/extract_v3.c202
1 files changed, 201 insertions, 1 deletions
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;
}