From: Michael Schroeder Date: Wed, 15 Jan 2025 10:46:24 +0000 (+0100) Subject: Implement apkv3 support X-Git-Tag: 0.7.32~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1ebff917045cfa67544a9d2e492e064011780f62;p=thirdparty%2Flibsolv.git Implement apkv3 support --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 30125cc9..e344222c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,10 @@ IF (ENABLE_ZCHUNK_COMPRESSION) SET (ENABLE_ZSTD_COMPRESSION ON) ENDIF (ENABLE_ZCHUNK_COMPRESSION) +IF (ENABLE_APK) +SET (ENABLE_ZSTD_COMPRESSION ON) +ENDIF (ENABLE_APK) + IF (ENABLE_RPMMD OR ENABLE_SUSEREPO OR ENABLE_APPDATA OR ENABLE_COMPS OR ENABLE_HELIXREPO OR ENABLE_MDKREPO) IF (WITH_LIBXML2 ) FIND_PACKAGE (LibXml2 REQUIRED) diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index 4b36934a..637d67fe 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -76,7 +76,7 @@ ENDIF (ENABLE_DEBIAN) IF (ENABLE_APK) SET (libsolvext_SRCS ${libsolvext_SRCS} - repo_apk.c) + repo_apk.c repo_apkv3.c) SET (libsolvext_HEADERS ${libsolvext_HEADERS} repo_apk.h) ENDIF (ENABLE_APK) diff --git a/ext/repo_apk.c b/ext/repo_apk.c index f1743686..223490a2 100644 --- a/ext/repo_apk.c +++ b/ext/repo_apk.c @@ -15,7 +15,7 @@ #include #include - +#include #include "pool.h" #include "repo.h" @@ -24,7 +24,9 @@ #include "solv_xfopen.h" #include "tarhead.h" #include "repo_apk.h" +#include "repo_apkv3.h" +/* zlib decompression */ struct zstream { int fd; @@ -38,13 +40,13 @@ struct zstream { }; static struct zstream * -apkz_open(int fd) +apkz_open(int fd, int raw) { struct zstream *zstream = solv_calloc(1, sizeof(*zstream)); zstream->fd = fd; - if (inflateInit2(&zstream->zs, 15 + 32) != Z_OK) /* 32: enable gzip */ + if (inflateInit2(&zstream->zs, raw ? -15 : 15 + 32) != Z_OK) /* 32: enable gzip */ { - solv_free(&zstream->zs); + solv_free(zstream); return 0; } return zstream; @@ -144,6 +146,195 @@ apkz_read(void *cookie, char *buf, size_t len) } } +/* zstd decompression */ + +struct zstdstream { + int fd; + FILE *fp; + int eof; + ZSTD_DCtx *ctx; + ZSTD_inBuffer in; + unsigned char buf[65536]; +}; + +static struct zstdstream * +apkzstd_open(int fd) +{ + struct zstdstream *zstdstream = solv_calloc(1, sizeof(*zstdstream)); + zstdstream->fd = fd; + zstdstream->in.src = zstdstream->buf; + zstdstream->in.size = zstdstream->in.pos = 0; + if (!(zstdstream->ctx = ZSTD_createDCtx())) + { + solv_free(zstdstream); + return 0; + } + return zstdstream; +} + +static int +apkzstd_close(void *cookie) +{ + struct zstdstream *zstdstream = cookie; + ZSTD_freeDCtx(zstdstream->ctx); + if (zstdstream->fd != -1) + close(zstdstream->fd); + solv_free(zstdstream); + return 0; +} + +static inline ssize_t +apkzstd_fillbuf(struct zstdstream *zstdstream) +{ + ssize_t rr; + if (zstdstream->fp) + { + rr = fread(zstdstream->buf, 1, sizeof(zstdstream->buf), zstdstream->fp); + if (rr <= 0 && ferror(zstdstream->fp)) + rr = -1; + } + else + rr = read(zstdstream->fd, zstdstream->buf, sizeof(zstdstream->buf)); + if (rr >= 0) + { + zstdstream->in.pos = 0; + zstdstream->in.size = rr; + } + return rr; +} + +static ssize_t +apkzstd_read(void *cookie, char *buf, size_t len) +{ + struct zstdstream *zstdstream = cookie; + ZSTD_outBuffer out; + int eof = 0; + size_t r; + + if (!zstdstream) + return -1; + if (zstdstream->eof) + return 0; + out.dst = buf; + out.pos = 0; + out.size = len; + for (;;) + { + if (zstdstream->in.pos >= zstdstream->in.size) + { + ssize_t rr = apkzstd_fillbuf(zstdstream); + if (rr < 0) + return rr; + if (rr == 0) + eof = 1; + } + r = ZSTD_decompressStream(zstdstream->ctx, &out, &zstdstream->in); + if (ZSTD_isError(r)) + return -1; + if (out.pos == out.size || eof) + return out.pos; + } +} + + +/* apkv3 handling */ + +static FILE * +open_apkv3_error(Pool *pool, int fd, const char *fn, const char *msg) +{ + pool_error(pool, -1, "%s: %s", fn, msg); + if (fd != -1) + close(fd); + return 0; +} + +static FILE * +open_apkv3(Pool *pool, int fd, FILE *fp, const char *fn, int adbchar) +{ + unsigned char comp[2]; + char buf[4]; + FILE *cfp; + + comp[0] = comp[1] = 0; + if (adbchar == 'c') + { + ssize_t r; + if (fp) + r = fread(comp, 2, 1, fp) == 1 ? 2 : feof(fp) ? 0 : -1; + else + r = read(fd, comp, 2); + if (r != 2) + return open_apkv3_error(pool, fd, fn, "compression header read error"); + } + else if (adbchar == 'd') + comp[0] = 1; + else if (adbchar != '.') + return open_apkv3_error(pool, fd, fn, "not an apkv3 file"); + if (comp[0] == 0) + cfp = fp ? fp : fdopen(fd, "r"); + else if (comp[0] == 1) + { + struct zstream *zstream = apkz_open(fd, 1); + if (!zstream) + return open_apkv3_error(pool, fd, fn, "zstream setup error"); + if (fp) + zstream->fp = fp; + if ((cfp = solv_cookieopen(zstream, "r", apkz_read, 0, apkz_close)) == 0) + return open_apkv3_error(pool, fd, fn, "zstream cookie setup error"); + } + else if (comp[0] == 2) + { + struct zstdstream *zstdstream = apkzstd_open(fd); + if (!zstdstream) + return open_apkv3_error(pool, fd, fn, "zstdstream setup error"); + if (fp) + zstdstream->fp = fp; + if ((cfp = solv_cookieopen(zstdstream, "r", apkzstd_read, 0, apkzstd_close)) == 0) + return open_apkv3_error(pool, fd, fn, "zstdstream cookie setup error"); + } + else + return open_apkv3_error(pool, fd, fn, "unsupported apkv3 compression"); + + if (adbchar != '.') + { + if (fread(buf, 4, 1, cfp) != 1 || buf[0] != 'A' || buf[1] != 'D' || buf[2] != 'B' || buf[3] != '.') + { + pool_error(pool, -1, "%s: not an apkv3 file", fn); + if (cfp != fp) + fclose(cfp); + return 0; + } + } + return cfp; +} + +static Id +add_apkv3_pkg(Repo *repo, Repodata *data, const char *fn, int flags, int fd, int adbchar) +{ + FILE *fp; + Id p; + if (!(fp = open_apkv3(repo->pool, fd, 0, fn, adbchar))) + return 0; + p = apkv3_add_pkg(repo, data, fn, fp, flags); + fclose(fp); + return p; +} + +static int +add_apkv3_idx(Repo *repo, Repodata *data, FILE *fp, int flags, int adbchar) +{ + FILE *cfp; + int r; + if (!(cfp = open_apkv3(repo->pool, -1, fp, (flags & APK_ADD_INDEX ? "installed db" : "package index"), adbchar))) + return -1; + r = apkv3_add_idx(repo, data, cfp, flags); + fclose(cfp); + return r; +} + + +/* apkv2 handling */ + static void add_deps(Repo *repo, Solvable *s, Id what, char *p) { @@ -215,6 +406,7 @@ repo_add_apk_pkg(Repo *repo, const char *fn, int flags) char *line = 0; size_t l, line_alloc = 0; int haveorigin = 0; + char first[4]; data = repo_add_repodata(repo, flags); if ((fd = open(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, fn) : fn, O_RDONLY)) == -1) @@ -222,7 +414,15 @@ repo_add_apk_pkg(Repo *repo, const char *fn, int flags) pool_error(pool, -1, "%s: %s", fn, strerror(errno)); return 0; } - zstream = apkz_open(fd); + if (read(fd, first, 4) == 4 && first[0] == 'A' && first[1] == 'D' && first[2] == 'B') + return add_apkv3_pkg(repo, data, fn, flags, fd, first[3]); + if (lseek(fd, 0, SEEK_SET)) + { + pool_error(pool, -1, "%s: lseek: %s", fn, strerror(errno)); + close(fd); + return 0; + } + zstream = apkz_open(fd, 0); if (!zstream) { pool_error(pool, -1, "%s: %s", fn, strerror(errno)); @@ -245,7 +445,7 @@ repo_add_apk_pkg(Repo *repo, const char *fn, int flags) fclose(fp); return 0; } - if (flags & APK_ADD_WITH_HDRID) + if ((flags & APK_ADD_WITH_HDRID) != 0) { q1chk = solv_chksum_create(REPOKEY_TYPE_SHA1); zstream->readcb_data = q1chk; @@ -297,7 +497,7 @@ repo_add_apk_pkg(Repo *repo, const char *fn, int flags) else if (!strncmp(line, "arch = ", 7)) s->arch = pool_str2id(pool, line + 7, 1); else if (!strncmp(line, "license = ", 10)) - repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_LICENSE, line + 10); + repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_LICENSE, line + 10); else if (!strncmp(line, "origin = ", 9)) { if (s->name && !strcmp(line + 9, pool_id2str(pool, s->name))) @@ -403,7 +603,7 @@ apk_add_hdrid(Repodata *data, Id p, char *idstr) } static void -apk_process_index(Repo *repo, Repodata *data, struct tarhead *th) +apk_process_index(Repo *repo, Repodata *data, struct tarhead *th, int flags) { Pool *pool = repo->pool; Solvable *s = 0; @@ -464,7 +664,7 @@ apk_process_index(Repo *repo, Repodata *data, struct tarhead *th) else if (line[0] == 'A') s->arch = pool_str2id(pool, line + 2, 1); else if (line[0] == 'L') - repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_LICENSE, line + 2); + repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_LICENSE, line + 2); else if (line[0] == 'o') { if (s->name && !strcmp(line + 2, pool_id2str(pool, s->name))) @@ -479,7 +679,7 @@ apk_process_index(Repo *repo, Repodata *data, struct tarhead *th) add_deps(repo, s, SOLVABLE_PROVIDES, line + 2); else if (line[0] == 'i') add_deps(repo, s, SOLVABLE_SUPPLEMENTS, line + 2); - else if (line[0] == 'C') + else if (line[0] == 'C' && (flags & APK_ADD_WITH_HDRID) != 0) apk_add_hdrid(data, s - pool->solvables, line + 2); } solv_free(line); @@ -488,6 +688,7 @@ apk_process_index(Repo *repo, Repodata *data, struct tarhead *th) int repo_add_apk_repo(Repo *repo, FILE *fp, int flags) { + char first[4]; struct tarhead th; Repodata *data; int c; @@ -501,11 +702,21 @@ repo_add_apk_repo(Repo *repo, FILE *fp, int flags) return (flags & APK_ADD_INDEX) != 0 ? 0 : -1; /* an empty file is allowed for the v2 index */ ungetc(c, fp); + if (c == 'A') + { + if (fread(first, 4, 1, fp) != 1) + return -1; + if (first[0] == 'A' && first[1] == 'D' && first[2] == 'B') + return add_apkv3_idx(repo, data, fp, flags, first[3]); + if ((flags & APK_ADD_INDEX) == 0) + return -1; /* not a tar file */ + } + if (c == 0x1f) { struct zstream *zstream; /* gzip compressed, setup decompression */ - zstream = apkz_open(-1); + zstream = apkz_open(-1, 0); if (!zstream) return -1; zstream->fp = fp; @@ -519,9 +730,15 @@ repo_add_apk_repo(Repo *repo, FILE *fp, int flags) } tarhead_init(&th, fp); + if (c == 'A') + { + /* initialize input buffer with 4 bytes we already read */ + memcpy(th.blk, first, 4); + th.end = 4; + } if ((flags & APK_ADD_INDEX) != 0) - apk_process_index(repo, data, &th); + apk_process_index(repo, data, &th, flags); else { while (tarhead_next(&th) > 0) @@ -529,7 +746,7 @@ repo_add_apk_repo(Repo *repo, FILE *fp, int flags) if (th.type != 1 || strcmp(th.path, "APKINDEX") != 0) tarhead_skip(&th); else - apk_process_index(repo, data, &th); + apk_process_index(repo, data, &th, flags); } } tarhead_free(&th); diff --git a/ext/repo_apkv3.c b/ext/repo_apkv3.c new file mode 100644 index 00000000..678b9130 --- /dev/null +++ b/ext/repo_apkv3.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2024, SUSE LLC + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +/* + * implement processing of uncompressed apkv3 data (without the leading "ADB.") + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pool.h" +#include "repo.h" +#include "util.h" +#include "chksum.h" + +#include "repo_apk.h" +#include "repo_apkv3.h" + + +#define ADB_MAX_SIZE 0x10000000 + +static inline unsigned int +adb_u32(const unsigned char *p) +{ + return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; +} + +static int +adb_read_blk_header(FILE *fp, unsigned long long *sizep) +{ + unsigned char buf[12]; + unsigned int size; + unsigned long long lsize; + if (fread(buf, 4, 1, fp) != 1) + return -1; + size = buf[0] | buf[1] << 8 | buf[2] << 16 | (buf[3] & 0x3f) << 24; + if ((buf[3] & 0xc0) != 0xc0) + { + if (size < 4) + return -1; + *sizep = size - 4; + return (buf[3] & 0xc0) >> 3; + } + if (fread(buf, 12, 1, fp) != 1) + return -1; + lsize = adb_u32(buf + 4); + lsize |= (unsigned long long)adb_u32(buf + 8) << 32; + if (lsize < 16) + return -1; + *sizep = lsize - 16; + return size; +} + +/* low level */ + +static const unsigned char * +adb_blob(const unsigned char *adb, size_t adblen, unsigned int v, size_t *bloblp) +{ + size_t blobl; + int type = (v >> 28) & 15; + v &= 0xfffffff; + if (type != 8 && type != 9 && type != 10) + return 0; + if (v + (type == 8 ? 1 : type == 9 ? 2 : 4) > adblen) + return 0; + blobl = adb[v++]; + if (type > 8) + blobl |= adb[v++] << 8; + if (type > 9) + { + blobl |= adb[v++] << 16; + blobl |= adb[v++] << 24; + } + if (v + blobl > adblen) + return 0; + *bloblp = blobl; + return adb + v; +} + +static int +adb_num(const unsigned char *adb, size_t adblen, unsigned int v, unsigned long long *nump) +{ + unsigned long long num; + int type = (v >> 28) & 15; + v &= 0xfffffff; + if (type != 1 && type != 2 && type != 3) + return 0; + if (type == 1) + { + *nump = v; + return 1; + } + if (v + (type == 2 ? 4 : 8) > adblen) + return 0; + num = adb_u32(adb + v); + if (type == 3) + num |= (unsigned long long)adb_u32(adb + v + 4) << 32; + *nump = num; + return 1; +} + +static unsigned int +adb_arr(const unsigned char *adb, size_t adblen, unsigned int v) +{ + unsigned int cnt; + int type = (v >> 28) & 15; + v &= 0xfffffff; + if (type != 13 && type != 14) + return 0; + if (v + 4 > adblen) + return 0; + cnt = adb_u32(adb + v); + if (cnt == 0 || cnt >= 0x1000000 || v + 4 * cnt > adblen) + return 0; + return cnt; +} + +static inline unsigned int +adb_idx(const unsigned char *adb, unsigned int v, unsigned int cnt, unsigned int idx) +{ + if (idx >= cnt) + return 0; + v = (v & 0xfffffff) + idx * 4; + return adb_u32(adb + v); +} + +/* high level */ + +static Id +adb_poolid(const unsigned char *adb, size_t adblen, unsigned int v, Pool *pool) +{ + size_t blobl; + const unsigned char *blob = adb_blob(adb, adblen, v, &blobl); + return blob && blobl < 0x1000000 ? pool_strn2id(pool, (const char *)blob, (unsigned int)blobl, 1) : 0; +} + +static void +adb_setstr(const unsigned char *adb, size_t adblen, unsigned int v, Repodata *data, Id p, Id key) +{ + size_t blobl; + const unsigned char *blob = adb_blob(adb, adblen, v, &blobl); + if (blob && blobl < 0x1000000) + { + char *space = pool_alloctmpspace(data->repo->pool, blobl + 1); + memcpy(space, blob, blobl); + space[blobl] = 0; + repodata_set_str(data, p, key, space); + pool_freetmpspace(data->repo->pool, space); + } +} + +static void +adb_setnum(const unsigned char *adb, size_t adblen, unsigned int v, Repodata *data, Id p, Id key) +{ + unsigned long long x; + if (adb_num(adb, adblen, v, &x)) + repodata_set_num(data, p, key, x); +} + +static void +adb_setchksum(const unsigned char *adb, size_t adblen, unsigned int v, Repodata *data, Id p, Id key) +{ + size_t blobl; + const unsigned char *blob = adb_blob(adb, adblen, v, &blobl); + if (blob && blobl == 20) + repodata_set_bin_checksum(data, p, key, REPOKEY_TYPE_SHA1, blob); + else if (blob && blobl == 32) + repodata_set_bin_checksum(data, p, key, REPOKEY_TYPE_SHA256, blob); + else if (blob && blobl == 64) + repodata_set_bin_checksum(data, p, key, REPOKEY_TYPE_SHA512, blob); +} + + +static Id +adb_get_dep(const unsigned char *adb, size_t adblen, unsigned int v, Pool *pool, Id *whatp) +{ + unsigned int cnt; + Id id, evr; + unsigned long long match;; + if (!(cnt = adb_arr(adb, adblen, v))) + return 0; + id = adb_poolid(adb, adblen, adb_idx(adb, v, cnt, 1), pool); + if (!id) + return 0; + evr = cnt > 2 ? adb_poolid(adb, adblen, adb_idx(adb, v, cnt, 2), pool) : 0; + match = cnt > 3 ? adb_num(adb, adblen, adb_idx(adb, v, cnt, 3), &match) : 1; + if (match & 16) + { + if (!whatp || *whatp != SOLVABLE_REQUIRES) + return 0; + *whatp = SOLVABLE_CONFLICTS; + } + if (evr) + { + int flags = 0; + if (match & 1) + flags |= REL_EQ; + if (match & 2) + flags |= REL_LT; + if (match & 4) + flags |= REL_GT; + if (match & 8) + { + /* fuzzy match, prepend ~ to evr */ + char *space = pool_alloctmpspace(pool, strlen(pool_id2str(pool, evr)) + 2); + space[0] = '~'; + strcpy(space, pool_id2str(pool, evr)); + evr = pool_str2id(pool, space, 1); + pool_freetmpspace(pool, space); + } + id = pool_rel2id(pool, id, evr, flags, 1); + } + return id; +} + +static void +adb_add_deps(const unsigned char *adb, size_t adblen, unsigned int v, Repodata *data, Id p, Id what) +{ + Id supplements = 0; + Id id, oldwhat = what; + Repo *repo = data->repo; + Pool *pool = repo->pool; + Solvable *s = pool->solvables + p; + unsigned int cnt, idx; + if (!(cnt = adb_arr(adb, adblen, v))) + return; + for (idx = 1; idx < cnt; idx++) + { + unsigned int vv = adb_idx(adb, v, cnt, idx); + if (!vv) + continue; + what = oldwhat; + id = adb_get_dep(adb, adblen, vv, pool, &what); + if (!id) + continue; + if (what == SOLVABLE_PROVIDES) + s->provides = repo_addid_dep(repo, s->provides, id, 0); + else if (what == SOLVABLE_REQUIRES) + s->requires = repo_addid_dep(repo, s->requires, id, 0); + else if (what == SOLVABLE_CONFLICTS) + s->conflicts = repo_addid_dep(repo, s->conflicts, id, 0); + else if (what == SOLVABLE_RECOMMENDS) + s->recommends = repo_addid_dep(repo, s->recommends, id, 0); + else if (what == SOLVABLE_SUPPLEMENTS) + supplements = supplements ? pool_rel2id(pool, id, supplements, REL_AND, 1) : id; + } + if (supplements) + s->supplements = repo_addid_dep(repo, s->supplements, supplements, 0); +} + +static Id +adb_add_pkg_info(Pool *pool, Repo *repo, Repodata *data, const unsigned char *adb, size_t adblen, unsigned int v, int flags) +{ + Solvable *s; + unsigned int cnt; + Id name, origin, license; + + if (!(cnt = adb_arr(adb, adblen, v))) + return 0; + name = adb_poolid(adb, adblen, adb_idx(adb, v, cnt, 1), pool); + if (!name) + return 0; + s = pool_id2solvable(pool, repo_add_solvable(repo)); + s->name = name; + s->evr = adb_poolid(adb, adblen, adb_idx(adb, v, cnt, 2), pool); + adb_setstr(adb, adblen, adb_idx(adb, v, cnt, 4), data, s - pool->solvables, SOLVABLE_DESCRIPTION); + s->arch = adb_poolid(adb, adblen, adb_idx(adb, v, cnt, 5), pool); + license = adb_poolid(adb, adblen, adb_idx(adb, v, cnt, 6), pool); + if (license) + repodata_set_id(data, s - pool->solvables, SOLVABLE_LICENSE, license); + origin = adb_poolid(adb, adblen, adb_idx(adb, v, cnt, 7), pool); + if (origin && origin != s->name) + repodata_set_id(data, s - pool->solvables, SOLVABLE_SOURCENAME, origin); + else + repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME); + adb_setstr(adb, adblen, adb_idx(adb, v, cnt, 9), data, s - pool->solvables, SOLVABLE_URL); + adb_setnum(adb, adblen, adb_idx(adb, v, cnt, 11), data, s - pool->solvables, SOLVABLE_BUILDTIME); + adb_setnum(adb, adblen, adb_idx(adb, v, cnt, 12), data, s - pool->solvables, SOLVABLE_INSTALLSIZE); + adb_setnum(adb, adblen, adb_idx(adb, v, cnt, 13), data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE); + if ((flags & APK_ADD_WITH_HDRID) != 0) + adb_setchksum(adb, adblen, adb_idx(adb, v, cnt, 3), data, s - pool->solvables, SOLVABLE_HDRID); + adb_add_deps(adb, adblen, adb_idx(adb, v, cnt, 15), data, s - pool->solvables, SOLVABLE_REQUIRES); + adb_add_deps(adb, adblen, adb_idx(adb, v, cnt, 16), data, s - pool->solvables, SOLVABLE_PROVIDES); + adb_add_deps(adb, adblen, adb_idx(adb, v, cnt, 18), data, s - pool->solvables, SOLVABLE_SUPPLEMENTS); + adb_add_deps(adb, adblen, adb_idx(adb, v, cnt, 19), data, s - pool->solvables, SOLVABLE_RECOMMENDS); + if (!s->arch) + s->arch = ARCH_NOARCH; + if (!s->evr) + s->evr = ID_EMPTY; + s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0); + return s - pool->solvables; +} + +static Id +add_add_idb_pkg(Pool *pool, Repo *repo, Repodata *data, const unsigned char *adb, size_t adblen, unsigned int v, int flags) +{ + unsigned int cnt; + size_t blobl; + const unsigned char *blob = adb_blob(adb, adblen, v, &blobl); + if (blobl < 4 + 2 + 4 || blobl >= ADB_MAX_SIZE || (blob[3] & 0xf0) != 0) + return 0; + adb = (unsigned char *)blob + 4; + adblen = blobl - 4; + v = adb_u32(adb + 4); + if (!(cnt = adb_arr(adb, adblen, v))) + return 0; + return adb_add_pkg_info(pool, repo, data, adb, adblen, adb_idx(adb, v, cnt, 1), flags); +} + +static const unsigned char * +adb_read_adb_blk(Pool *pool, FILE *fp, const char *fn, size_t *adblenp) +{ + unsigned char *adb; + unsigned long long size; + if (adb_read_blk_header(fp, &size) != 0) + { + pool_error(pool, -1, "%s: missing adb block", fn); + return 0; + } + if (size > ADB_MAX_SIZE) + { + pool_error(pool, -1, "%s: oversized adb block", fn); + return 0; + } + adb = solv_malloc((size_t)size); + if (fread(adb, (size_t)size, 1, fp) != 1) + { + pool_error(pool, -1, "%s: adb block read error", fn); + return 0; + } + *adblenp = (size_t)size; + return adb; +} + +Id +apkv3_add_pkg(Repo *repo, Repodata *data, const char *fn, FILE *fp, int flags) +{ + Pool *pool = repo->pool; + char buf[4]; + const unsigned char *adb; + size_t adblen; + unsigned int v, cnt; + Id p; + + if (fread(buf, 4, 1, fp) != 1 || buf[0] != 'p' || buf[1] != 'c' || buf[2] != 'k' || buf[3] != 'g') + { + pool_error(pool, -1, "%s: not an apkv3 package", fn); + return 0; + } + + adb = adb_read_adb_blk(pool, fp, fn, &adblen); + if (!adb) + return 0; + + v = adb_u32(adb + 4); + if (!(cnt = adb_arr(adb, adblen, v))) + { + solv_free((void *)adb); + return 0; + } + p = adb_add_pkg_info(pool, repo, data, adb, adblen, adb_idx(adb, v, cnt, 1), flags); + if (p && (flags & APK_ADD_WITH_PKGID) != 0) + { + unsigned char pkgid[16]; + Chksum *pkgidchk = solv_chksum_create(REPOKEY_TYPE_MD5); + solv_chksum_add(pkgidchk, adb, adblen); + solv_chksum_free(pkgidchk, pkgid); + repodata_set_bin_checksum(data, p, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid); + } + solv_free((void *)adb); + return p; +} + +int +apkv3_add_idx(Repo *repo, Repodata *data, FILE *fp, int flags) +{ + Pool *pool = repo->pool; + char buf[4]; + const unsigned char *adb; + size_t adblen; + unsigned int v, cnt, idx; + int idb = flags & APK_ADD_INDEX ? 1 : 0; + + if (fread(buf, 4, 1, fp) != 1 || memcmp(buf, (idb ? "idb" : "indx") , 4)) + { + pool_error(pool, -1, (idb ? "not an apkv3 installed database" : "not an apkv3 index")); + return -1; + } + + adb = adb_read_adb_blk(pool, fp, idb ? "installed database" : "index", &adblen); + if (!adb) + return -1; + + v = adb_u32(adb + 4); + if (!(cnt = adb_arr(adb, adblen, v))) + { + solv_free((void *)adb); + return -1; + } + v = adb_idx(adb, v, cnt, idb ? 1 : 2); + if ((cnt = adb_arr(adb, adblen, v)) != 0) + { + for (idx = 1; idx < cnt; idx++) + { + if (idb) + add_add_idb_pkg(pool, repo, data, adb, adblen, adb_idx(adb, v, cnt, idx), flags); + else + adb_add_pkg_info(pool, repo, data, adb, adblen, adb_idx(adb, v, cnt, idx), flags); + } + } + solv_free((void *)adb); + return 0; +} + diff --git a/ext/repo_apkv3.h b/ext/repo_apkv3.h new file mode 100644 index 00000000..1ec34c03 --- /dev/null +++ b/ext/repo_apkv3.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2025, SUSE LLC + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +extern Id apkv3_add_pkg(Repo *repo, Repodata *data, const char *fn, FILE *fp, int flags); +extern int apkv3_add_idx(Repo *repo, Repodata *data, FILE *fp, int flags); +