adb: introduce apk-tools database format, and few applets
This is a flat buffers inspired format that allows fast mmaped access to the data with low overhead, signature support and relatively good forward support.cute-signatures
parent
81782bfc15
commit
efe0c4afec
|
@ -19,6 +19,7 @@ ZLIB_LIBS := $(shell $(PKG_CONFIG) --libs zlib)
|
|||
libapk_soname := 3.12.0
|
||||
libapk_so := $(obj)/libapk.so.$(libapk_soname)
|
||||
libapk.so.$(libapk_soname)-objs := \
|
||||
adb.o adb_trust.o \
|
||||
common.o database.o package.o commit.o solver.o \
|
||||
version.o atom.o blob.o hash.o print.o \
|
||||
io.o io_url.o io_gunzip.o io_archive.o
|
||||
|
@ -67,6 +68,12 @@ apk-objs := apk.o help.o \
|
|||
app_index.o app_fetch.o app_verify.o app_dot.o \
|
||||
app_audit.o
|
||||
|
||||
ifeq ($(ADB),y)
|
||||
libapk.so.$(libapk_soname)-objs += apk_adb.o
|
||||
apk-objs += app_adbdump.o app_adbsign.o app_mkndx.o \
|
||||
app_convdb.o app_convndx.o
|
||||
endif
|
||||
|
||||
LIBS_apk := -lapk
|
||||
LIBS_apk-test := -lapk
|
||||
LIBS_apk.so := -L$(obj) -lapk
|
||||
|
|
|
@ -0,0 +1,802 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "apk_blob.h"
|
||||
|
||||
/* Block enumeration */
|
||||
static inline struct adb_block *adb_block_validate(struct adb_block *blk, apk_blob_t b)
|
||||
{
|
||||
size_t pos = (char *)blk - b.ptr;
|
||||
if (pos == b.len) return NULL;
|
||||
pos += sizeof(struct adb_block);
|
||||
if (pos > b.len || ADB_BLOCK_SIZE(blk) > b.len - pos) return ERR_PTR(-EBADMSG);
|
||||
return blk;
|
||||
}
|
||||
|
||||
struct adb_block *adb_block_first(apk_blob_t b)
|
||||
{
|
||||
return adb_block_validate((struct adb_block*)b.ptr, b);
|
||||
}
|
||||
|
||||
struct adb_block *adb_block_next(struct adb_block *cur, apk_blob_t b)
|
||||
{
|
||||
return adb_block_validate((struct adb_block*)((char*)cur + sizeof(struct adb_block) + ADB_BLOCK_SIZE(cur)), b);
|
||||
}
|
||||
|
||||
/* Init stuff */
|
||||
int adb_free(struct adb *db)
|
||||
{
|
||||
if (db->mmap.ptr) {
|
||||
munmap(db->mmap.ptr, db->mmap.len);
|
||||
} else {
|
||||
struct adb_w_bucket *bucket, *nxt;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < db->num_buckets; i++)
|
||||
list_for_each_entry_safe(bucket, nxt, &db->bucket[i], node)
|
||||
free(bucket);
|
||||
free(db->adb.ptr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adb_reset(struct adb *db)
|
||||
{
|
||||
struct adb_w_bucket *bucket, *nxt;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < db->num_buckets; i++) {
|
||||
list_for_each_entry_safe(bucket, nxt, &db->bucket[i], node)
|
||||
free(bucket);
|
||||
list_init(&db->bucket[i]);
|
||||
}
|
||||
db->adb.len = 0;
|
||||
}
|
||||
|
||||
int adb_m_map(struct adb *db, int fd, uint32_t expected_schema, struct adb_trust *t)
|
||||
{
|
||||
struct stat st;
|
||||
struct adb_header *hdr;
|
||||
struct adb_block *blk;
|
||||
struct adb_verify_ctx vfy = {};
|
||||
int trusted = t ? 0 : 1;
|
||||
|
||||
if (fstat(fd, &st) != 0) return -errno;
|
||||
if (st.st_size < sizeof *hdr) return -EIO;
|
||||
|
||||
memset(db, 0, sizeof *db);
|
||||
db->mmap.ptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
db->mmap.len = st.st_size;
|
||||
if (db->mmap.ptr == MAP_FAILED) return -errno;
|
||||
|
||||
hdr = (struct adb_header *) db->mmap.ptr;
|
||||
if (hdr->magic != htole32(ADB_FORMAT_MAGIC)) goto bad_msg;
|
||||
if (expected_schema && expected_schema != le32toh(hdr->schema)) goto bad_msg;
|
||||
|
||||
db->hdr = *hdr;
|
||||
db->data = APK_BLOB_PTR_LEN(db->mmap.ptr + sizeof *hdr, db->mmap.len - sizeof *hdr);
|
||||
adb_foreach_block(blk, 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:
|
||||
if (!APK_BLOB_IS_NULL(db->adb)) goto bad_msg;
|
||||
db->adb = b;
|
||||
break;
|
||||
case ADB_BLOCK_SIG:
|
||||
if (APK_BLOB_IS_NULL(db->adb)) goto bad_msg;
|
||||
if (!trusted &&
|
||||
adb_trust_verify_signature(t, db, &vfy, b) == 0)
|
||||
trusted = 1;
|
||||
break;
|
||||
default:
|
||||
if (APK_BLOB_IS_NULL(db->adb)) goto bad_msg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!trusted) blk = ERR_PTR(-ENOKEY);
|
||||
if (IS_ERR(blk)) goto err;
|
||||
return 0;
|
||||
|
||||
bad_msg:
|
||||
blk = ERR_PTR(-EBADMSG);
|
||||
err:
|
||||
adb_free(db);
|
||||
return PTR_ERR(blk);
|
||||
}
|
||||
|
||||
int adb_w_init_dynamic(struct adb *db, uint32_t schema, void *buckets, size_t num_buckets)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
*db = (struct adb) {
|
||||
.hdr.magic = htole32(ADB_FORMAT_MAGIC),
|
||||
.hdr.schema = htole32(schema),
|
||||
.num_buckets = num_buckets,
|
||||
.bucket = buckets,
|
||||
};
|
||||
|
||||
if (num_buckets) {
|
||||
for (i = 0; i < db->num_buckets; i++)
|
||||
list_init(&db->bucket[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adb_w_init_static(struct adb *db, void *buf, size_t bufsz)
|
||||
{
|
||||
*db = (struct adb) {
|
||||
.hdr.magic = htole32(ADB_FORMAT_MAGIC),
|
||||
.adb.ptr = buf,
|
||||
.mmap.len = bufsz,
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read interface */
|
||||
static inline void *adb_r_deref(const struct adb *db, adb_val_t v, size_t offs, size_t s)
|
||||
{
|
||||
offs += ADB_VAL_VALUE(v);
|
||||
if (offs + s > db->adb.len) return NULL;
|
||||
return db->adb.ptr + offs;
|
||||
}
|
||||
|
||||
adb_val_t adb_r_root(const struct adb *db)
|
||||
{
|
||||
if (db->adb.len < sizeof(adb_val_t)) return ADB_NULL;
|
||||
return *(adb_val_t *)(db->adb.ptr + db->adb.len - sizeof(adb_val_t));
|
||||
}
|
||||
|
||||
uint32_t adb_r_int(const struct adb *db, adb_val_t v)
|
||||
{
|
||||
uint32_t *int4;
|
||||
|
||||
switch (ADB_VAL_TYPE(v)) {
|
||||
case ADB_TYPE_INT:
|
||||
return ADB_VAL_VALUE(v);
|
||||
case ADB_TYPE_INT_32:
|
||||
int4 = adb_r_deref(db, v, 0, sizeof int4);
|
||||
if (!int4) return 0;
|
||||
return le32toh(*int4);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
apk_blob_t adb_r_blob(const struct adb *db, adb_val_t v)
|
||||
{
|
||||
void *blob;
|
||||
size_t len;
|
||||
|
||||
switch (ADB_VAL_TYPE(v)) {
|
||||
case ADB_TYPE_BLOB_8:
|
||||
blob = adb_r_deref(db, v, 0, 1);
|
||||
len = *(uint8_t*) blob;
|
||||
return APK_BLOB_PTR_LEN(adb_r_deref(db, v, 1, len), len);
|
||||
case ADB_TYPE_BLOB_16:
|
||||
blob = adb_r_deref(db, v, 0, 2);
|
||||
len = le16toh(*(uint16_t*) blob);
|
||||
return APK_BLOB_PTR_LEN(adb_r_deref(db, v, 2, len), len);
|
||||
case ADB_TYPE_BLOB_32:
|
||||
blob = adb_r_deref(db, v, 0, 4);
|
||||
len = le32toh(*(uint32_t*) blob);
|
||||
return APK_BLOB_PTR_LEN(adb_r_deref(db, v, 4, len), len);
|
||||
default:
|
||||
return APK_BLOB_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct adb_obj *adb_r_obj(struct adb *db, adb_val_t v, struct adb_obj *obj, const struct adb_object_schema *schema)
|
||||
{
|
||||
adb_val_t *o;
|
||||
uint32_t num;
|
||||
|
||||
if (ADB_VAL_TYPE(v) != ADB_TYPE_ARRAY &&
|
||||
ADB_VAL_TYPE(v) != ADB_TYPE_OBJECT)
|
||||
goto err;
|
||||
|
||||
o = adb_r_deref(db, v, 0, sizeof(adb_val_t[ADBI_NUM_ENTRIES]));
|
||||
if (!o) goto err;
|
||||
|
||||
num = le32toh(o[ADBI_NUM_ENTRIES]);
|
||||
o = adb_r_deref(db, v, 0, sizeof(adb_val_t[num]));
|
||||
if (!o) goto err;
|
||||
|
||||
*obj = (struct adb_obj) {
|
||||
.schema = schema,
|
||||
.db = db,
|
||||
.num = num,
|
||||
.obj = o,
|
||||
};
|
||||
return obj;
|
||||
err:
|
||||
*obj = (struct adb_obj) {
|
||||
.schema = schema,
|
||||
.db = db,
|
||||
.num = 1,
|
||||
.obj = 0,
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
|
||||
struct adb_obj *adb_r_rootobj(struct adb *db, struct adb_obj *obj, const struct adb_object_schema *schema)
|
||||
{
|
||||
return adb_r_obj(db, adb_r_root(db), obj, schema);
|
||||
}
|
||||
|
||||
adb_val_t adb_ro_val(const struct adb_obj *o, unsigned i)
|
||||
{
|
||||
if (i >= o->num) return ADB_NULL;
|
||||
return o->obj[i];
|
||||
}
|
||||
|
||||
uint32_t adb_ro_int(const struct adb_obj *o, unsigned i)
|
||||
{
|
||||
adb_val_t val = adb_ro_val(o, i);
|
||||
if (val == ADB_NULL && o->schema && o->schema->get_default_int)
|
||||
return o->schema->get_default_int(i);
|
||||
return adb_r_int(o->db, val);
|
||||
}
|
||||
|
||||
apk_blob_t adb_ro_blob(const struct adb_obj *o, unsigned i)
|
||||
{
|
||||
return adb_r_blob(o->db, adb_ro_val(o, i));
|
||||
}
|
||||
|
||||
struct adb_obj *adb_ro_obj(const struct adb_obj *o, unsigned i, struct adb_obj *no)
|
||||
{
|
||||
const struct adb_object_schema *schema = NULL;
|
||||
|
||||
if (o->schema) {
|
||||
if (o->schema->kind == ADB_KIND_ARRAY)
|
||||
schema = container_of(o->schema->fields[0].kind, struct adb_object_schema, kind);
|
||||
else if (i > 0 && i < o->schema->num_fields)
|
||||
schema = container_of(o->schema->fields[i-1].kind, struct adb_object_schema, kind);
|
||||
assert(schema->kind == ADB_KIND_OBJECT || schema->kind == ADB_KIND_ARRAY);
|
||||
}
|
||||
|
||||
return adb_r_obj(o->db, adb_ro_val(o, i), no, schema);
|
||||
}
|
||||
|
||||
static struct adb *__db1, *__db2;
|
||||
static const struct adb_object_schema *__schema;
|
||||
|
||||
static int wacmp(const void *p1, const void *p2)
|
||||
{
|
||||
struct adb_obj o1, o2;
|
||||
adb_r_obj(__db1, *(adb_val_t *)p1, &o1, __schema);
|
||||
adb_r_obj(__db2, *(adb_val_t *)p2, &o2, __schema);
|
||||
return o1.schema->compare(&o1, &o2);
|
||||
}
|
||||
|
||||
int adb_ra_find(struct adb_obj *arr, int cur, struct adb *db, adb_val_t val)
|
||||
{
|
||||
adb_val_t *ndx;
|
||||
|
||||
__db1 = db;
|
||||
__db2 = arr->db;
|
||||
__schema = arr->schema;
|
||||
assert(__schema->kind == ADB_KIND_ARRAY);
|
||||
__schema = container_of(__schema->fields[0].kind, struct adb_object_schema, kind);
|
||||
|
||||
if (cur == 0) {
|
||||
ndx = bsearch(&val, &arr->obj[ADBI_FIRST], adb_ra_num(arr), sizeof(arr->obj[0]), wacmp);
|
||||
if (!ndx) return -1;
|
||||
cur = ndx - arr->obj;
|
||||
while (cur > 1 && wacmp(&val, &arr->obj[cur-1]) == 0) cur--;
|
||||
} else {
|
||||
cur++;
|
||||
if (wacmp(&val, &arr->obj[cur]) != 0)
|
||||
return -1;
|
||||
}
|
||||
return cur;
|
||||
|
||||
}
|
||||
|
||||
/* Write interface */
|
||||
static inline size_t iovec_len(struct iovec *vec, size_t nvec)
|
||||
{
|
||||
size_t i, l = 0;
|
||||
for (i = 0; i < nvec; i++) l += vec[i].iov_len;
|
||||
return l;
|
||||
}
|
||||
|
||||
static unsigned iovec_hash(struct iovec *vec, size_t nvec, size_t *len)
|
||||
{
|
||||
size_t i, l = 0;
|
||||
unsigned hash = 5381;
|
||||
|
||||
for (i = 0; i < nvec; i++) {
|
||||
hash = apk_blob_hash_seed(APK_BLOB_PTR_LEN(vec[i].iov_base, vec[i].iov_len), hash);
|
||||
l += vec[i].iov_len;
|
||||
}
|
||||
*len = l;
|
||||
return hash;
|
||||
}
|
||||
|
||||
static unsigned iovec_memcmp(struct iovec *vec, size_t nvec, void *base)
|
||||
{
|
||||
uint8_t *b = (uint8_t *) base;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nvec; i++) {
|
||||
if (memcmp(b, vec[i].iov_base, vec[i].iov_len) != 0)
|
||||
return 1;
|
||||
b += vec[i].iov_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static adb_val_t adb_w_error(struct adb *db, int rc)
|
||||
{
|
||||
assert(0);
|
||||
db->hdr.magic = 0;
|
||||
return ADB_ERROR(rc);
|
||||
}
|
||||
|
||||
static size_t adb_w_raw(struct adb *db, struct iovec *vec, size_t n, size_t len, size_t alignment)
|
||||
{
|
||||
void *ptr;
|
||||
size_t offs, i;
|
||||
|
||||
if ((i = ROUND_UP(db->adb.len, alignment) - db->adb.len) != 0) {
|
||||
memset(&db->adb.ptr[db->adb.len], 0, i);
|
||||
db->adb.len += i;
|
||||
}
|
||||
|
||||
if (db->adb.len + len > db->mmap.len) {
|
||||
assert(db->num_buckets);
|
||||
if (!db->mmap.len) db->mmap.len = 8192;
|
||||
while (db->adb.len + len > db->mmap.len)
|
||||
db->mmap.len *= 2;
|
||||
ptr = realloc(db->adb.ptr, db->mmap.len);
|
||||
assert(ptr);
|
||||
db->adb.ptr = ptr;
|
||||
}
|
||||
|
||||
offs = db->adb.len;
|
||||
for (i = 0; i < n; i++) {
|
||||
memcpy(&db->adb.ptr[db->adb.len], vec[i].iov_base, vec[i].iov_len);
|
||||
db->adb.len += vec[i].iov_len;
|
||||
}
|
||||
|
||||
return offs;
|
||||
}
|
||||
|
||||
static size_t adb_w_data(struct adb *db, struct iovec *vec, size_t nvec, size_t alignment)
|
||||
{
|
||||
size_t len, i;
|
||||
unsigned hash, bucketno;
|
||||
struct adb_w_bucket *bucket;
|
||||
struct adb_w_bucket_entry *entry = 0;
|
||||
|
||||
if (!db->num_buckets) return adb_w_raw(db, vec, nvec, iovec_len(vec, nvec), alignment);
|
||||
|
||||
hash = iovec_hash(vec, nvec, &len);
|
||||
bucketno = hash % db->num_buckets;
|
||||
list_for_each_entry(bucket, &db->bucket[bucketno], node) {
|
||||
for (i = 0, entry = bucket->entries; i < ARRAY_SIZE(bucket->entries); i++, entry++) {
|
||||
if (entry->len == 0) goto add;
|
||||
if (entry->hash != hash) continue;
|
||||
if (entry->len == len && iovec_memcmp(vec, nvec, &((uint8_t*)db->adb.ptr)[entry->offs]) == 0) {
|
||||
if ((entry->offs & alignment) != 0) goto add;
|
||||
return entry->offs;
|
||||
}
|
||||
}
|
||||
entry = 0;
|
||||
}
|
||||
|
||||
bucket = calloc(1, sizeof *bucket);
|
||||
list_init(&bucket->node);
|
||||
list_add_tail(&bucket->node, &db->bucket[bucketno]);
|
||||
entry = &bucket->entries[0];
|
||||
|
||||
add:
|
||||
entry->hash = hash;
|
||||
entry->len = len;
|
||||
entry->offs = adb_w_raw(db, vec, nvec, len, alignment);
|
||||
return entry->offs;
|
||||
}
|
||||
|
||||
static size_t adb_w_data1(struct adb *db, void *ptr, size_t len, size_t alignment)
|
||||
{
|
||||
struct iovec vec[] = {
|
||||
{ .iov_base = ptr, .iov_len = len },
|
||||
};
|
||||
if (!ptr) return ADB_NULL;
|
||||
return adb_w_data(db, vec, ARRAY_SIZE(vec), alignment);
|
||||
}
|
||||
|
||||
void adb_w_root(struct adb *db, adb_val_t root_val)
|
||||
{
|
||||
struct iovec vec = {
|
||||
.iov_base = &root_val, .iov_len = sizeof(adb_val_t),
|
||||
};
|
||||
adb_w_raw(db, &vec, 1, vec.iov_len, sizeof root_val);
|
||||
}
|
||||
|
||||
void adb_w_rootobj(struct adb_obj *obj)
|
||||
{
|
||||
adb_w_root(obj->db, adb_w_obj(obj));
|
||||
}
|
||||
|
||||
adb_val_t adb_w_blob(struct adb *db, apk_blob_t b)
|
||||
{
|
||||
union {
|
||||
uint32_t u32;
|
||||
uint16_t u16;
|
||||
uint8_t u8;
|
||||
} val;
|
||||
uint32_t n = b.len;
|
||||
struct iovec vec[2] = {
|
||||
{ .iov_base = &val, .iov_len = sizeof val },
|
||||
{ .iov_base = (void *) b.ptr, .iov_len = n },
|
||||
};
|
||||
adb_val_t o;
|
||||
|
||||
if (n > 0xffff) {
|
||||
val.u32 = htole32(n);
|
||||
vec[0].iov_len = sizeof val.u32;
|
||||
o = ADB_TYPE_BLOB_32;
|
||||
} else if (n > 0xff) {
|
||||
val.u16 = htole16(n);
|
||||
vec[0].iov_len = sizeof val.u16;
|
||||
o = ADB_TYPE_BLOB_16;
|
||||
} else {
|
||||
val.u8 = n;
|
||||
vec[0].iov_len = sizeof val.u8;
|
||||
o = ADB_TYPE_BLOB_8;
|
||||
}
|
||||
|
||||
return ADB_VAL(o, adb_w_data(db, vec, ARRAY_SIZE(vec), vec[0].iov_len));
|
||||
}
|
||||
|
||||
adb_val_t adb_w_int(struct adb *db, uint32_t val)
|
||||
{
|
||||
if (val >= 0x10000000)
|
||||
return ADB_VAL(ADB_TYPE_INT_32, adb_w_data1(db, &val, sizeof val, sizeof val));
|
||||
return ADB_VAL(ADB_TYPE_INT, val);
|
||||
}
|
||||
|
||||
adb_val_t adb_w_copy(struct adb *db, struct adb *srcdb, adb_val_t v)
|
||||
{
|
||||
void *ptr;
|
||||
size_t sz, align = 1;
|
||||
|
||||
if (db == srcdb) return v;
|
||||
|
||||
switch (ADB_VAL_TYPE(v)) {
|
||||
case ADB_TYPE_SPECIAL:
|
||||
case ADB_TYPE_INT:
|
||||
return v;
|
||||
case ADB_TYPE_INT_32:
|
||||
sz = align = sizeof(uint32_t);
|
||||
goto copy;
|
||||
case ADB_TYPE_BLOB_8:
|
||||
ptr = adb_r_deref(srcdb, v, 0, 1);
|
||||
sz = 1UL + *(uint8_t*) ptr;
|
||||
goto copy;
|
||||
case ADB_TYPE_BLOB_16:
|
||||
ptr = adb_r_deref(srcdb, v, 0, 2);
|
||||
sz = 1UL + *(uint16_t*) ptr;
|
||||
goto copy;
|
||||
case ADB_TYPE_OBJECT:
|
||||
case ADB_TYPE_ARRAY: {
|
||||
adb_val_t cpy[512];
|
||||
struct adb_obj obj;
|
||||
adb_r_obj(srcdb, v, &obj, NULL);
|
||||
sz = adb_ro_num(&obj);
|
||||
if (sz > ARRAY_SIZE(cpy)) return adb_w_error(db, E2BIG);
|
||||
cpy[ADBI_NUM_ENTRIES] = obj.obj[ADBI_NUM_ENTRIES];
|
||||
for (int i = ADBI_FIRST; i < sz; i++) cpy[i] = adb_w_copy(db, srcdb, adb_ro_val(&obj, i));
|
||||
return ADB_VAL(ADB_VAL_TYPE(v), adb_w_data1(db, cpy, sizeof(adb_val_t[sz]), sizeof(adb_val_t)));
|
||||
}
|
||||
case ADB_TYPE_INT_64:
|
||||
case ADB_TYPE_BLOB_32:
|
||||
default:
|
||||
return adb_w_error(db, ENOSYS);
|
||||
}
|
||||
copy:
|
||||
ptr = adb_r_deref(srcdb, v, 0, sz);
|
||||
return ADB_VAL(ADB_VAL_TYPE(v), adb_w_data1(db, ptr, sz, align));
|
||||
}
|
||||
|
||||
adb_val_t adb_w_adb(struct adb *db, struct adb *valdb)
|
||||
{
|
||||
uint32_t bsz;
|
||||
struct adb_block blk = {
|
||||
.type_size = htole32((ADB_BLOCK_ADB << 30) + valdb->adb.len)
|
||||
};
|
||||
struct iovec vec[] = {
|
||||
{ .iov_base = &bsz, .iov_len = sizeof bsz },
|
||||
{ .iov_base = &blk, .iov_len = sizeof blk },
|
||||
{ .iov_base = valdb->adb.ptr, .iov_len = valdb->adb.len },
|
||||
};
|
||||
if (valdb->adb.len <= 4) return ADB_NULL;
|
||||
bsz = htole32(iovec_len(vec, ARRAY_SIZE(vec)) - sizeof bsz);
|
||||
return ADB_VAL(ADB_TYPE_BLOB_32, adb_w_raw(db, vec, ARRAY_SIZE(vec), iovec_len(vec, ARRAY_SIZE(vec)), sizeof(uint32_t)));
|
||||
}
|
||||
|
||||
adb_val_t adb_w_fromstring(struct adb *db, const uint8_t *kind, apk_blob_t val)
|
||||
{
|
||||
int r;
|
||||
|
||||
switch (*kind) {
|
||||
case ADB_KIND_BLOB:
|
||||
case ADB_KIND_INT:
|
||||
return container_of(kind, struct adb_scalar_schema, kind)->fromstring(db, val);
|
||||
case ADB_KIND_OBJECT:
|
||||
case ADB_KIND_ARRAY:; {
|
||||
struct adb_obj obj;
|
||||
struct adb_object_schema *schema = container_of(kind, struct adb_object_schema, kind);
|
||||
adb_wo_alloca(&obj, schema, db);
|
||||
r = schema->fromstring(&obj, val);
|
||||
if (r) return ADB_ERROR(r);
|
||||
return adb_w_obj(&obj);
|
||||
}
|
||||
default:
|
||||
return ADB_ERROR(ENOSYS);
|
||||
}
|
||||
}
|
||||
|
||||
struct adb_obj *adb_wo_init(struct adb_obj *o, adb_val_t *p, const struct adb_object_schema *schema, struct adb *db)
|
||||
{
|
||||
memset(p, 0, sizeof(adb_val_t[schema->num_fields]));
|
||||
/* Use the backing num entries index as the 'maximum' allocated space
|
||||
* information while building the object/array. */
|
||||
p[ADBI_NUM_ENTRIES] = schema->num_fields;
|
||||
|
||||
*o = (struct adb_obj) {
|
||||
.schema = schema,
|
||||
.db = db,
|
||||
.obj = p,
|
||||
.num = 1,
|
||||
};
|
||||
return o;
|
||||
}
|
||||
|
||||
void adb_wo_reset(struct adb_obj *o)
|
||||
{
|
||||
uint32_t max = o->obj[ADBI_NUM_ENTRIES];
|
||||
memset(o->obj, 0, sizeof(adb_val_t[o->num]));
|
||||
o->obj[ADBI_NUM_ENTRIES] = max;
|
||||
o->num = 1;
|
||||
}
|
||||
|
||||
void adb_wo_resetdb(struct adb_obj *o)
|
||||
{
|
||||
adb_wo_reset(o);
|
||||
adb_reset(o->db);
|
||||
}
|
||||
|
||||
static adb_val_t __adb_w_obj(struct adb_obj *o, uint32_t type)
|
||||
{
|
||||
uint32_t n, max = o->obj[ADBI_NUM_ENTRIES];
|
||||
adb_val_t *obj = o->obj, val = ADB_NULL;
|
||||
|
||||
if (o->schema && o->schema->pre_commit) o->schema->pre_commit(o);
|
||||
|
||||
for (n = o->num; n > 1 && obj[n-1] == ADB_NULL; n--)
|
||||
;
|
||||
if (n > 1) {
|
||||
obj[ADBI_NUM_ENTRIES] = htole32(n);
|
||||
val = ADB_VAL(type, adb_w_data1(o->db, obj, sizeof(adb_val_t[n]), sizeof(adb_val_t)));
|
||||
}
|
||||
adb_wo_reset(o);
|
||||
o->obj[ADBI_NUM_ENTRIES] = max;
|
||||
return val;
|
||||
}
|
||||
|
||||
adb_val_t adb_w_obj(struct adb_obj *o)
|
||||
{
|
||||
return __adb_w_obj(o, ADB_TYPE_OBJECT);
|
||||
}
|
||||
|
||||
adb_val_t adb_w_arr(struct adb_obj *o)
|
||||
{
|
||||
return __adb_w_obj(o, ADB_TYPE_ARRAY);
|
||||
}
|
||||
|
||||
adb_val_t adb_wo_fromstring(struct adb_obj *o, apk_blob_t val)
|
||||
{
|
||||
adb_wo_reset(o);
|
||||
return o->schema->fromstring(o, val);
|
||||
}
|
||||
|
||||
adb_val_t adb_wo_val(struct adb_obj *o, unsigned i, adb_val_t v)
|
||||
{
|
||||
if (i >= 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));
|
||||
if (v != ADB_NULL && i >= o->num) o->num = i + 1;
|
||||
return o->obj[i] = v;
|
||||
}
|
||||
|
||||
adb_val_t adb_wo_val_fromstring(struct adb_obj *o, unsigned i, apk_blob_t val)
|
||||
{
|
||||
if (i >= o->obj[ADBI_NUM_ENTRIES]) return adb_w_error(o->db, E2BIG);
|
||||
if (i >= o->num) o->num = i + 1;
|
||||
return o->obj[i] = adb_w_fromstring(o->db, o->schema->fields[i-1].kind, val);
|
||||
}
|
||||
|
||||
adb_val_t adb_wo_int(struct adb_obj *o, unsigned i, uint32_t v)
|
||||
{
|
||||
if (o->schema && o->schema->get_default_int &&
|
||||
v == o->schema->get_default_int(i))
|
||||
return ADB_NULL;
|
||||
return adb_wo_val(o, i, adb_w_int(o->db, v));
|
||||
}
|
||||
|
||||
adb_val_t adb_wo_blob(struct adb_obj *o, unsigned i, apk_blob_t b)
|
||||
{
|
||||
return adb_wo_val(o, i, adb_w_blob(o->db, b));
|
||||
}
|
||||
|
||||
adb_val_t adb_wo_obj(struct adb_obj *o, unsigned i, struct adb_obj *no)
|
||||
{
|
||||
assert(o->db == no->db);
|
||||
return adb_wo_val(o, i, adb_w_obj(no));
|
||||
}
|
||||
|
||||
adb_val_t adb_wo_arr(struct adb_obj *o, unsigned i, struct adb_obj *no)
|
||||
{
|
||||
assert(o->db == no->db);
|
||||
return adb_wo_val(o, i, adb_w_arr(no));
|
||||
}
|
||||
|
||||
adb_val_t adb_wa_append(struct adb_obj *o, adb_val_t v)
|
||||
{
|
||||
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;
|
||||
return v;
|
||||
}
|
||||
|
||||
adb_val_t adb_wa_append_obj(struct adb_obj *o, struct adb_obj *no)
|
||||
{
|
||||
assert(o->db == no->db);
|
||||
return adb_wa_append(o, adb_w_obj(no));
|
||||
}
|
||||
|
||||
adb_val_t adb_wa_append_fromstring(struct adb_obj *o, apk_blob_t b)
|
||||
{
|
||||
return adb_wa_append(o, adb_w_fromstring(o->db, o->schema->fields[0].kind, b));
|
||||
}
|
||||
|
||||
void adb_wa_sort(struct adb_obj *arr)
|
||||
{
|
||||
__db1 = __db2 = arr->db;
|
||||
__schema = container_of(arr->schema->fields[0].kind, struct adb_object_schema, kind);
|
||||
qsort(&arr->obj[ADBI_FIRST], adb_ra_num(arr), sizeof(arr->obj[0]), wacmp);
|
||||
}
|
||||
|
||||
void adb_wa_sort_unique(struct adb_obj *arr)
|
||||
{
|
||||
int i, j, num;
|
||||
|
||||
adb_wa_sort(arr);
|
||||
num = adb_ra_num(arr);
|
||||
if (num >= 2) {
|
||||
for (i = 2, j = 2; i <= num; i++) {
|
||||
if (arr->obj[i] == arr->obj[i-1]) continue;
|
||||
arr->obj[j++] = arr->obj[i];
|
||||
}
|
||||
arr->num = j;
|
||||
}
|
||||
}
|
||||
|
||||
/* Schema helpers */
|
||||
int adb_s_field_by_name(const struct adb_object_schema *schema, const char *name)
|
||||
{
|
||||
for (int i = 0; i < schema->num_fields; i++)
|
||||
if (strcmp(schema->fields[i].name, name) == 0)
|
||||
return i + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Container creation */
|
||||
int adb_c_header(struct apk_ostream *os, struct adb *db)
|
||||
{
|
||||
return apk_ostream_write(os, &db->hdr, sizeof db->hdr);
|
||||
}
|
||||
|
||||
int adb_c_block(struct apk_ostream *os, uint32_t type, apk_blob_t val)
|
||||
{
|
||||
struct adb_block blk = {
|
||||
.type_size = htole32((type << 30) + val.len)
|
||||
};
|
||||
int r;
|
||||
|
||||
r = apk_ostream_write(os, &blk, sizeof blk);
|
||||
if (r < 0) return r;
|
||||
|
||||
r = apk_ostream_write(os, val.ptr, val.len);
|
||||
if (r < 0) return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adb_c_block_copy(struct apk_ostream *os, struct adb_block *b, struct apk_istream *is, struct adb_verify_ctx *vfy)
|
||||
{
|
||||
EVP_MD_CTX *mdctx = NULL;
|
||||
int r;
|
||||
|
||||
r = apk_ostream_write(os, b, sizeof *b);
|
||||
if (r < 0) return r;
|
||||
|
||||
if (vfy) {
|
||||
mdctx = EVP_MD_CTX_new();
|
||||
EVP_DigestInit_ex(mdctx, EVP_sha512(), 0);
|
||||
}
|
||||
r = apk_stream_copy(is, os, ADB_BLOCK_SIZE(b), 0, 0, mdctx);
|
||||
if (vfy) {
|
||||
EVP_DigestFinal_ex(mdctx, vfy->sha512, 0);
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
vfy->calc |= (1 << ADB_HASH_SHA512);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int adb_c_create(struct apk_ostream *os, struct adb *db, struct adb_trust *t)
|
||||
{
|
||||
if (IS_ERR(os)) return PTR_ERR(os);
|
||||
if (db->hdr.magic != htole32(ADB_FORMAT_MAGIC)) {
|
||||
apk_ostream_cancel(os, -EAPKFORMAT);
|
||||
goto ret;
|
||||
}
|
||||
adb_c_header(os, db);
|
||||
adb_c_block(os, ADB_BLOCK_ADB, db->adb);
|
||||
if (t) adb_trust_write_signatures(t, db, NULL, os);
|
||||
ret:
|
||||
return apk_ostream_close(os);
|
||||
}
|
||||
|
||||
/* Container transformation interface */
|
||||
int adb_c_xfrm(struct adb_xfrm *x, int (*cb)(struct adb_xfrm *, struct adb_block *, struct apk_istream *))
|
||||
{
|
||||
struct adb_block blk;
|
||||
struct apk_segment_istream seg;
|
||||
int r, block_no = 0;
|
||||
size_t sz;
|
||||
|
||||
r = apk_istream_read(x->is, &x->db.hdr, sizeof x->db.hdr);
|
||||
if (r < 0) goto err;
|
||||
|
||||
if (x->db.hdr.magic != htole32(ADB_FORMAT_MAGIC)) goto bad_msg;
|
||||
r = apk_ostream_write(x->os, &x->db.hdr, sizeof x->db.hdr);
|
||||
if (r < 0) goto err;
|
||||
|
||||
do {
|
||||
r = apk_istream_read(x->is, &blk, sizeof blk);
|
||||
if (r <= 0) {
|
||||
if (r == 0) r = cb(x, NULL, NULL);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ((block_no++ == 0) != (ADB_BLOCK_TYPE(&blk) == ADB_BLOCK_ADB)) goto bad_msg;
|
||||
|
||||
sz = ADB_BLOCK_SIZE(&blk);
|
||||
r = cb(x, &blk, apk_istream_segment(&seg, x->is, sz, 0));
|
||||
if (r < 0) goto err;
|
||||
|
||||
if (r == 0 && seg.bytes_left == sz) {
|
||||
r = apk_ostream_write(x->os, &blk, sizeof blk);
|
||||
if (r < 0) goto err;
|
||||
r = apk_stream_copy(x->is, x->os, seg.bytes_left, 0, 0, 0);
|
||||
if (r < 0) goto err;
|
||||
} else if (seg.bytes_left > 0) {
|
||||
r = apk_istream_read(x->is, NULL, sz - seg.bytes_left);
|
||||
if (r < 0) goto err;
|
||||
}
|
||||
} while (1);
|
||||
bad_msg:
|
||||
r = -EBADMSG;
|
||||
err:
|
||||
apk_ostream_cancel(x->os, r);
|
||||
return r;
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
#ifndef ADB_H
|
||||
#define ADB_H
|
||||
|
||||
#include <endian.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include "apk_io.h"
|
||||
|
||||
struct adb;
|
||||
struct adb_obj;
|
||||
struct adb_trust;
|
||||
struct adb_verify_ctx;
|
||||
|
||||
typedef uint32_t adb_val_t;
|
||||
|
||||
#define ADB_TYPE_SPECIAL 0x00000000
|
||||
#define ADB_TYPE_INT 0x10000000
|
||||
#define ADB_TYPE_INT_32 0x20000000
|
||||
#define ADB_TYPE_INT_64 0x30000000
|
||||
#define ADB_TYPE_BLOB_8 0x80000000
|
||||
#define ADB_TYPE_BLOB_16 0x90000000
|
||||
#define ADB_TYPE_BLOB_32 0xa0000000
|
||||
#define ADB_TYPE_ARRAY 0xd0000000
|
||||
#define ADB_TYPE_OBJECT 0xe0000000
|
||||
#define ADB_TYPE_ERROR 0xf0000000
|
||||
#define ADB_TYPE_MASK 0xf0000000
|
||||
#define ADB_VALUE_MASK 0x0fffffff
|
||||
#define ADB_VAL_TYPE(x) ((le32toh(x))&ADB_TYPE_MASK)
|
||||
#define ADB_VAL_VALUE(x) ((le32toh(x))&ADB_VALUE_MASK)
|
||||
#define ADB_IS_ERROR(x) (ADB_VAL_TYPE(x) == ADB_TYPE_ERROR)
|
||||
#define ADB_VAL(type, val) (htole32((type) | (val)))
|
||||
#define ADB_ERROR(val) ADB_VAL(ADB_TYPE_ERROR, val)
|
||||
|
||||
/* ADB_TYPE_SPECIAL */
|
||||
#define ADB_VAL_NULL 0x00000000
|
||||
#define ADB_VAL_TRUE 0x00000001
|
||||
#define ADB_VAL_FALSE 0x00000002
|
||||
|
||||
#define ADB_NULL ADB_VAL(ADB_TYPE_SPECIAL, ADB_VAL_NULL)
|
||||
|
||||
/* Generic */
|
||||
#define ADBI_NUM_ENTRIES 0x00
|
||||
#define ADBI_FIRST 0x01
|
||||
|
||||
/* File Header */
|
||||
#define ADB_FORMAT_MAGIC 0x2e424441 // ADB.
|
||||
|
||||
struct adb_header {
|
||||
uint32_t magic;
|
||||
uint32_t schema;
|
||||
};
|
||||
|
||||
/* Blocks */
|
||||
#define ADB_BLOCK_END -1
|
||||
#define ADB_BLOCK_ADB 0
|
||||
#define ADB_BLOCK_SIG 2
|
||||
|
||||
#define ADB_BLOCK_TYPE(b) (le32toh((b)->type_size) >> 30)
|
||||
#define ADB_BLOCK_SIZE(b) (le32toh((b)->type_size) & 0x3fffffff)
|
||||
|
||||
struct adb_block {
|
||||
uint32_t type_size;
|
||||
};
|
||||
|
||||
struct adb_sign_hdr {
|
||||
uint8_t sign_ver, hash_alg;
|
||||
};
|
||||
|
||||
struct adb_sign_v0 {
|
||||
struct adb_sign_hdr hdr;
|
||||
uint8_t id[16];
|
||||
uint8_t sig[0];
|
||||
};
|
||||
|
||||
/* Hash algorithms */
|
||||
#define ADB_HASH_NONE 0x00
|
||||
#define ADB_HASH_SHA1 0x01
|
||||
#define ADB_HASH_SHA256 0x02
|
||||
#define ADB_HASH_SHA512 0x03
|
||||
|
||||
/* Block enumeration */
|
||||
struct adb_block *adb_block_first(apk_blob_t b);
|
||||
struct adb_block *adb_block_next(struct adb_block *cur, apk_blob_t b);
|
||||
|
||||
#define adb_foreach_block(__blk, __adb) \
|
||||
for (__blk = adb_block_first(__adb); !IS_ERR_OR_NULL(__blk); __blk = adb_block_next(__blk, __adb))
|
||||
|
||||
/* Schema */
|
||||
#define ADB_KIND_ADB 1
|
||||
#define ADB_KIND_OBJECT 2
|
||||
#define ADB_KIND_ARRAY 3
|
||||
#define ADB_KIND_BLOB 4
|
||||
#define ADB_KIND_INT 5
|
||||
|
||||
#define ADB_ARRAY_ITEM(_t) { { .kind = &(_t).kind } }
|
||||
#define ADB_FIELD(_i, _n, _t) [(_i)-1] = { .name = _n, .kind = &(_t).kind }
|
||||
|
||||
struct adb_object_schema {
|
||||
uint8_t kind;
|
||||
uint16_t num_fields;
|
||||
|
||||
apk_blob_t (*tostring)(struct adb_obj *, char *, size_t);
|
||||
int (*fromstring)(struct adb_obj *, apk_blob_t);
|
||||
uint32_t (*get_default_int)(unsigned i);
|
||||
int (*compare)(struct adb_obj *, struct adb_obj *);
|
||||
void (*pre_commit)(struct adb_obj *);
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
const uint8_t *kind;
|
||||
} fields[];
|
||||
};
|
||||
|
||||
struct adb_scalar_schema {
|
||||
uint8_t kind;
|
||||
|
||||
apk_blob_t (*tostring)(struct adb*, adb_val_t, char *, size_t);
|
||||
adb_val_t (*fromstring)(struct adb*, apk_blob_t);
|
||||
int (*compare)(adb_val_t, adb_val_t);
|
||||
};
|
||||
|
||||
struct adb_adb_schema {
|
||||
uint8_t kind;
|
||||
uint32_t schema_id;
|
||||
struct adb_object_schema *obj;
|
||||
};
|
||||
|
||||
/* Database read interface */
|
||||
struct adb_w_bucket {
|
||||
struct list_head node;
|
||||
struct adb_w_bucket_entry {
|
||||
uint32_t hash;
|
||||
uint32_t offs;
|
||||
uint32_t len;
|
||||
} entries[40];
|
||||
};
|
||||
|
||||
struct adb {
|
||||
apk_blob_t mmap, data, adb;
|
||||
struct adb_header hdr;
|
||||
size_t num_buckets;
|
||||
struct list_head *bucket;
|
||||
};
|
||||
|
||||
struct adb_obj {
|
||||
struct adb *db;
|
||||
const struct adb_object_schema *schema;
|
||||
uint32_t num;
|
||||
adb_val_t *obj;
|
||||
};
|
||||
|
||||
/* Container read interface */
|
||||
int adb_free(struct adb *);
|
||||
void adb_reset(struct adb *);
|
||||
|
||||
int adb_m_map(struct adb *, int fd, uint32_t expected_schema, struct adb_trust *);
|
||||
#define adb_w_init_alloca(db, schema, num_buckets) adb_w_init_dynamic(db, schema, alloca(sizeof(struct list_head[num_buckets])), num_buckets)
|
||||
#define adb_w_init_tmp(db, size) adb_w_init_static(db, alloca(size), size)
|
||||
int adb_w_init_dynamic(struct adb *db, uint32_t schema, void *buckets, size_t num_buckets);
|
||||
int adb_w_init_static(struct adb *db, void *buf, size_t bufsz);
|
||||
|
||||
/* Primitive read */
|
||||
adb_val_t adb_r_root(const struct adb *);
|
||||
struct adb_obj *adb_r_rootobj(struct adb *a, struct adb_obj *o, const struct adb_object_schema *);
|
||||
uint32_t adb_r_int(const struct adb *, adb_val_t);
|
||||
apk_blob_t adb_r_blob(const struct adb *, adb_val_t);
|
||||
struct adb_obj *adb_r_obj(struct adb *, adb_val_t, struct adb_obj *o, const struct adb_object_schema *);
|
||||
|
||||
/* Object read */
|
||||
static inline uint32_t adb_ro_num(const struct adb_obj *o) { return o->num; }
|
||||
static inline uint32_t adb_ra_num(const struct adb_obj *o) { return (o->num ?: 1) - 1; }
|
||||
|
||||
adb_val_t adb_ro_val(const struct adb_obj *o, unsigned i);
|
||||
uint32_t adb_ro_int(const struct adb_obj *o, unsigned i);
|
||||
apk_blob_t adb_ro_blob(const struct adb_obj *o, unsigned i);
|
||||
struct adb_obj *adb_ro_obj(const struct adb_obj *o, unsigned i, struct adb_obj *);
|
||||
int adb_ra_find(struct adb_obj *arr, int cur, struct adb *db, adb_val_t val);
|
||||
|
||||
/* Primitive write */
|
||||
void adb_w_root(struct adb *, adb_val_t);
|
||||
void adb_w_rootobj(struct adb_obj *);
|
||||
adb_val_t adb_w_blob(struct adb *, apk_blob_t);
|
||||
adb_val_t adb_w_int(struct adb *, uint32_t);
|
||||
adb_val_t adb_w_copy(struct adb *, struct adb *, adb_val_t);
|
||||
adb_val_t adb_w_adb(struct adb *, struct adb *);
|
||||
adb_val_t adb_w_fromstring(struct adb *, const uint8_t *kind, apk_blob_t);
|
||||
|
||||
/* Object write */
|
||||
#define adb_wo_alloca(o, schema, db) adb_wo_init(o, alloca(sizeof(adb_val_t[(schema)->num_fields])), schema, db)
|
||||
|
||||
struct adb_obj *adb_wo_init(struct adb_obj *, adb_val_t *, const struct adb_object_schema *, struct adb *);
|
||||
void adb_wo_reset(struct adb_obj *);
|
||||
void adb_wo_resetdb(struct adb_obj *);
|
||||
adb_val_t adb_w_obj(struct adb_obj *);
|
||||
adb_val_t adb_w_arr(struct adb_obj *);
|
||||
adb_val_t adb_wo_fromstring(struct adb_obj *o, apk_blob_t);
|
||||
adb_val_t adb_wo_val(struct adb_obj *o, unsigned i, adb_val_t);
|
||||
adb_val_t adb_wo_val_fromstring(struct adb_obj *o, unsigned i, apk_blob_t);
|
||||
adb_val_t adb_wo_int(struct adb_obj *o, unsigned i, uint32_t);
|
||||
adb_val_t adb_wo_blob(struct adb_obj *o, unsigned i, apk_blob_t);
|
||||
adb_val_t adb_wo_obj(struct adb_obj *o, unsigned i, struct adb_obj *);
|
||||
adb_val_t adb_wo_arr(struct adb_obj *o, unsigned i, struct adb_obj *);
|
||||
adb_val_t adb_wa_append(struct adb_obj *o, adb_val_t);
|
||||
adb_val_t adb_wa_append_obj(struct adb_obj *o, struct adb_obj *);
|
||||
adb_val_t adb_wa_append_fromstring(struct adb_obj *o, apk_blob_t);
|
||||
void adb_wa_sort(struct adb_obj *);
|
||||
void adb_wa_sort_unique(struct adb_obj *);
|
||||
|
||||
/* Schema helpers */
|
||||
int adb_s_field_by_name(const struct adb_object_schema *, const char *);
|
||||
|
||||
/* Creation */
|
||||
int adb_c_header(struct apk_ostream *os, struct adb *db);
|
||||
int adb_c_block(struct apk_ostream *os, uint32_t type, apk_blob_t);
|
||||
int adb_c_block_copy(struct apk_ostream *os, struct adb_block *b, struct apk_istream *is, struct adb_verify_ctx *);
|
||||
int adb_c_create(struct apk_ostream *os, struct adb *db, struct adb_trust *t);
|
||||
|
||||
/* Trust */
|
||||
#include <openssl/evp.h>
|
||||
|
||||
struct adb_pkey {
|
||||
uint8_t id[16];
|
||||
EVP_PKEY *key;
|
||||
};
|
||||
|
||||
int adb_pkey_init(struct adb_pkey *pkey, EVP_PKEY *key);
|
||||
void adb_pkey_free(struct adb_pkey *pkey);
|
||||
int adb_pkey_load(struct adb_pkey *pkey, int dirfd, const char *fn);
|
||||
|
||||
struct adb_trust {
|
||||
EVP_MD_CTX *mdctx;
|
||||
struct list_head trusted_key_list;
|
||||
struct list_head private_key_list;
|
||||
};
|
||||
|
||||
struct adb_verify_ctx {
|
||||
uint32_t calc;
|
||||
uint8_t sha512[64];
|
||||
};
|
||||
|
||||
int adb_trust_init(struct adb_trust *trust, int keysfd, struct apk_string_array *);
|
||||
void adb_trust_free(struct adb_trust *trust);
|
||||
int adb_trust_write_signatures(struct adb_trust *trust, struct adb *db, struct adb_verify_ctx *vfy, struct apk_ostream *os);
|
||||
int adb_trust_verify_signature(struct adb_trust *trust, struct adb *db, struct adb_verify_ctx *vfy, apk_blob_t sigb);
|
||||
|
||||
/* Transform existing file */
|
||||
struct adb_xfrm {
|
||||
struct apk_istream *is;
|
||||
struct apk_ostream *os;
|
||||
struct adb db;
|
||||
struct adb_verify_ctx vfy;
|
||||
};
|
||||
int adb_c_xfrm(struct adb_xfrm *, int (*cb)(struct adb_xfrm *, struct adb_block *, struct apk_istream *));
|
||||
|
||||
#endif
|
|
@ -0,0 +1,266 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
#include "apk_defines.h"
|
||||
#include "adb.h"
|
||||
|
||||
struct adb_trust_key {
|
||||
struct list_head key_node;
|
||||
struct adb_pkey key;
|
||||
|
||||
};
|
||||
|
||||
/* Trust */
|
||||
int adb_pkey_init(struct adb_pkey *pkey, EVP_PKEY *key)
|
||||
{
|
||||
unsigned char dig[EVP_MAX_MD_SIZE], *pub = NULL;
|
||||
unsigned int dlen = sizeof dig;
|
||||
int len;
|
||||
|
||||
if ((len = i2d_PublicKey(key, &pub)) < 0) return -EIO;
|
||||
EVP_Digest(pub, len, dig, &dlen, EVP_sha512(), NULL);
|
||||
memcpy(pkey->id, dig, sizeof pkey->id);
|
||||
OPENSSL_free(pub);
|
||||
|
||||
pkey->key = key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adb_pkey_free(struct adb_pkey *pkey)
|
||||
{
|
||||
EVP_PKEY_free(pkey->key);
|
||||
}
|
||||
|
||||
int adb_pkey_load(struct adb_pkey *pkey, int dirfd, const char *fn)
|
||||
{
|
||||
EVP_PKEY *key;
|
||||
BIO *bio;
|
||||
int fd;
|
||||
|
||||
fd = openat(dirfd, fn, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0) return -errno;
|
||||
|
||||
bio = BIO_new_fp(fdopen(fd, "r"), BIO_CLOSE);
|
||||
if (!bio) return -ENOMEM;
|
||||
|
||||
key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
||||
if (!key) {
|
||||
BIO_reset(bio);
|
||||
key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
||||
}
|
||||
ERR_clear_error();
|
||||
|
||||
BIO_free(bio);
|
||||
if (!key) return -EBADMSG;
|
||||
|
||||
adb_pkey_init(pkey, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct adb_trust_key *adb_trust_load_key(int dirfd, const char *filename)
|
||||
{
|
||||
struct adb_trust_key *key;
|
||||
int r;
|
||||
|
||||
key = calloc(1, sizeof *key);
|
||||
if (!key) return ERR_PTR(-ENOMEM);
|
||||
|
||||
r = adb_pkey_load(&key->key, dirfd, filename);
|
||||
if (r) {
|
||||
free(key);
|
||||
return ERR_PTR(-ENOKEY);
|
||||
}
|
||||
|
||||
list_init(&key->key_node);
|
||||
return key;
|
||||
}
|
||||
|
||||
static int __adb_trust_load_pubkey(void *pctx, int dirfd, const char *filename)
|
||||
{
|
||||
struct adb_trust *trust = pctx;
|
||||
struct adb_trust_key *key = adb_trust_load_key(dirfd, filename);
|
||||
|
||||
if (!IS_ERR(key))
|
||||
list_add_tail(&key->key_node, &trust->trusted_key_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adb_trust_init(struct adb_trust *trust, int dirfd, struct apk_string_array *pkey_files)
|
||||
{
|
||||
char **fn;
|
||||
|
||||
*trust = (struct adb_trust){
|
||||
.mdctx = EVP_MD_CTX_new(),
|
||||
};
|
||||
if (!trust->mdctx) return -ENOMEM;
|
||||
EVP_MD_CTX_set_flags(trust->mdctx, EVP_MD_CTX_FLAG_FINALISE);
|
||||
list_init(&trust->trusted_key_list);
|
||||
list_init(&trust->private_key_list);
|
||||
apk_dir_foreach_file(dirfd, __adb_trust_load_pubkey, trust);
|
||||
|
||||
foreach_array_item(fn, pkey_files) {
|
||||
struct adb_trust_key *key = adb_trust_load_key(AT_FDCWD, *fn);
|
||||
if (IS_ERR(key)) return PTR_ERR(key);
|
||||
list_add_tail(&key->key_node, &trust->private_key_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __adb_trust_free_keys(struct list_head *h)
|
||||
{
|
||||
struct adb_trust_key *tkey, *n;
|
||||
|
||||
list_for_each_entry_safe(tkey, n, h, key_node) {
|
||||
list_del(&tkey->key_node);
|
||||
adb_pkey_free(&tkey->key);
|
||||
free(tkey);
|
||||
}
|
||||
}
|
||||
|
||||
void adb_trust_free(struct adb_trust *trust)
|
||||
{
|
||||
if (!trust->mdctx) return;
|
||||
__adb_trust_free_keys(&trust->trusted_key_list);
|
||||
__adb_trust_free_keys(&trust->private_key_list);
|
||||
EVP_MD_CTX_free(trust->mdctx);
|
||||
}
|
||||
|
||||
static int adb_verify_ctx_calc(struct adb_verify_ctx *vfy, unsigned int hash_alg, apk_blob_t data, apk_blob_t *pmd)
|
||||
{
|
||||
const EVP_MD *evp;
|
||||
apk_blob_t md;
|
||||
|
||||
switch (hash_alg) {
|
||||
case ADB_HASH_SHA512:
|
||||
evp = EVP_sha512();
|
||||
*pmd = md = APK_BLOB_BUF(vfy->sha512);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!(vfy->calc & (1 << hash_alg))) {
|
||||
unsigned int sz = md.len;
|
||||
if (APK_BLOB_IS_NULL(data)) return -ENOMSG;
|
||||
if (EVP_Digest(data.ptr, data.len, (unsigned char*) md.ptr, &sz, evp, NULL) != 1 ||
|
||||
sz != md.len)
|
||||
return -EIO;
|
||||
vfy->calc |= (1 << hash_alg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adb_trust_write_signatures(struct adb_trust *trust, struct adb *db, struct adb_verify_ctx *vfy, struct apk_ostream *os)
|
||||
{
|
||||
union {
|
||||
struct adb_sign_hdr hdr;
|
||||
struct adb_sign_v0 v0;
|
||||
unsigned char buf[8192];
|
||||
} sig;
|
||||
struct adb_trust_key *tkey;
|
||||
apk_blob_t md;
|
||||
size_t siglen;
|
||||
int r;
|
||||
|
||||
if (!vfy) {
|
||||
vfy = alloca(sizeof *vfy);
|
||||
memset(vfy, 0, sizeof *vfy);
|
||||
}
|
||||
|
||||
r = adb_verify_ctx_calc(vfy, ADB_HASH_SHA512, db->adb, &md);
|
||||
if (r) return r;
|
||||
|
||||
list_for_each_entry(tkey, &trust->private_key_list, key_node) {
|
||||
sig.v0 = (struct adb_sign_v0) {
|
||||
.hdr.sign_ver = 0,
|
||||
.hdr.hash_alg = ADB_HASH_SHA512,
|
||||
};
|
||||
memcpy(sig.v0.id, tkey->key.id, sizeof(sig.v0.id));
|
||||
|
||||
siglen = sizeof sig.buf - sizeof sig.v0;
|
||||
EVP_MD_CTX_set_pkey_ctx(trust->mdctx, NULL);
|
||||
if (EVP_DigestSignInit(trust->mdctx, NULL, EVP_sha512(), NULL, tkey->key.key) != 1 ||
|
||||
EVP_DigestUpdate(trust->mdctx, &db->hdr, sizeof db->hdr) != 1 ||
|
||||
EVP_DigestUpdate(trust->mdctx, &sig.hdr.sign_ver, sizeof sig.hdr.sign_ver) != 1 ||
|
||||
EVP_DigestUpdate(trust->mdctx, &sig.hdr.hash_alg, sizeof sig.hdr.hash_alg) != 1 ||
|
||||
EVP_DigestUpdate(trust->mdctx, md.ptr, md.len) != 1 ||
|
||||
EVP_DigestSignFinal(trust->mdctx, sig.v0.sig, &siglen) != 1) {
|
||||
ERR_print_errors_fp(stdout);
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
r = adb_c_block(os, ADB_BLOCK_SIG, APK_BLOB_PTR_LEN((char*) &sig, sizeof(sig.v0) + siglen));
|
||||
if (r < 0) goto err;
|
||||
}
|
||||
return 0;
|
||||
err_io:
|
||||
r = -EIO;
|
||||
err:
|
||||
apk_ostream_cancel(os, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
int adb_trust_verify_signature(struct adb_trust *trust, struct adb *db, struct adb_verify_ctx *vfy, apk_blob_t sigb)
|
||||
{
|
||||
struct adb_trust_key *tkey;
|
||||
struct adb_sign_hdr *sig;
|
||||
struct adb_sign_v0 *sig0;
|
||||
apk_blob_t md;
|
||||
|
||||
if (APK_BLOB_IS_NULL(db->adb)) return -ENOMSG;
|
||||
if (sigb.len < sizeof(struct adb_sign_hdr)) return -EBADMSG;
|
||||
|
||||
sig = (struct adb_sign_hdr *) sigb.ptr;
|
||||
sig0 = (struct adb_sign_v0 *) sigb.ptr;
|
||||
if (sig->sign_ver != 0) return -ENOSYS;
|
||||
|
||||
list_for_each_entry(tkey, &trust->trusted_key_list, key_node) {
|
||||
if (memcmp(sig0->id, tkey->key.id, sizeof sig0->id) != 0) continue;
|
||||
if (adb_verify_ctx_calc(vfy, sig->hash_alg, db->adb, &md) != 0) continue;
|
||||
|
||||
EVP_MD_CTX_set_pkey_ctx(trust->mdctx, NULL);
|
||||
if (EVP_DigestVerifyInit(trust->mdctx, NULL, EVP_sha512(), NULL, tkey->key.key) != 1 ||
|
||||
EVP_DigestUpdate(trust->mdctx, &db->hdr, sizeof db->hdr) != 1 ||
|
||||
EVP_DigestUpdate(trust->mdctx, &sig->sign_ver, sizeof sig->sign_ver) != 1 ||
|
||||
EVP_DigestUpdate(trust->mdctx, &sig->hash_alg, sizeof sig->hash_alg) != 1 ||
|
||||
EVP_DigestUpdate(trust->mdctx, md.ptr, md.len) != 1 ||
|
||||
EVP_DigestVerifyFinal(trust->mdctx, sig0->sig, sigb.len - sizeof(*sig0)) != 1) {
|
||||
ERR_clear_error();
|
||||
continue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
|
||||
/* Command group for signing */
|
||||
|
||||
#include "apk_applet.h"
|
||||
|
||||
#define SIGNING_OPTIONS(OPT) \
|
||||
OPT(OPT_SIGN_sign_key, APK_OPT_ARG "sign-key")
|
||||
|
||||
APK_OPT_GROUP(options_signing, "Signing", SIGNING_OPTIONS);
|
||||
|
||||
static int option_parse_signing(void *ctx, struct apk_db_options *dbopts, int optch, const char *optarg)
|
||||
{
|
||||
switch (optch) {
|
||||
case OPT_SIGN_sign_key:
|
||||
*apk_string_array_add(&dbopts->private_keys) = (char*) optarg;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct apk_option_group optgroup_signing = {
|
||||
.desc = options_signing,
|
||||
.parse = option_parse_signing,
|
||||
};
|
|
@ -472,6 +472,7 @@ int main(int argc, char **argv)
|
|||
|
||||
memset(&dbopts, 0, sizeof(dbopts));
|
||||
list_init(&dbopts.repository_list);
|
||||
apk_string_array_init(&dbopts.private_keys);
|
||||
umask(0);
|
||||
setup_terminal();
|
||||
|
||||
|
@ -577,6 +578,7 @@ err:
|
|||
|
||||
fetchConnectionCacheClose();
|
||||
apk_string_array_free(&args);
|
||||
apk_string_array_free(&dbopts.private_keys);
|
||||
free(apk_argv);
|
||||
|
||||
if (r < 0) r = 250;
|
||||
|
|
|
@ -0,0 +1,472 @@
|
|||
#include <errno.h>
|
||||
#include "adb.h"
|
||||
#include "apk_adb.h"
|
||||
#include "apk_print.h"
|
||||
#include "apk_version.h"
|
||||
|
||||
#define APK_VERSION_CONFLICT 16
|
||||
|
||||
/* Few helpers to map old database to new one */
|
||||
|
||||
int apk_dep_split(apk_blob_t *b, apk_blob_t *bdep)
|
||||
{
|
||||
extern const apk_spn_match_def apk_spn_dependency_separator;
|
||||
|
||||
if (APK_BLOB_IS_NULL(*b)) return 0;
|
||||
if (apk_blob_cspn(*b, apk_spn_dependency_separator, bdep, b)) {
|
||||
/* found separator - update b to skip over after all separators */
|
||||
if (!apk_blob_spn(*b, apk_spn_dependency_separator, NULL, b))
|
||||
*b = APK_BLOB_NULL;
|
||||
} else {
|
||||
/* no separator - return this as the last dependency, signal quit */
|
||||
*bdep = *b;
|
||||
*b = APK_BLOB_NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
adb_val_t adb_wo_pkginfo(struct adb_obj *obj, unsigned int f, apk_blob_t val)
|
||||
{
|
||||
struct apk_checksum csum;
|
||||
adb_val_t v = ADB_ERROR(EAPKFORMAT);
|
||||
|
||||
/* FIXME: get rid of this function, and handle the conversion via schema? */
|
||||
switch (f) {
|
||||
case ADBI_PI_UNIQUE_ID:
|
||||
if (!val.ptr || val.len < 4) break;
|
||||
apk_blob_pull_csum(&val, &csum);
|
||||
v = adb_w_int(obj->db, get_unaligned32(csum.data) & ADB_VALUE_MASK);
|
||||
break;
|
||||
case ADBI_PI_REPO_COMMIT:
|
||||
if (val.len < 40) break;
|
||||
csum.type = 20;
|
||||
apk_blob_pull_hexdump(&val, APK_BLOB_CSUM(csum));
|
||||
if (val.ptr) v = adb_w_blob(obj->db, APK_BLOB_CSUM(csum));
|
||||
break;
|
||||
default:
|
||||
return adb_wo_val_fromstring(obj, f, val);
|
||||
}
|
||||
if (v != ADB_NULL && !ADB_IS_ERROR(v))
|
||||
v = adb_wo_val(obj, f, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
unsigned int adb_pkg_field_index(char f)
|
||||
{
|
||||
#define MAP(ch, ndx) [ch - 'A'] = ndx
|
||||
static unsigned char map[] = {
|
||||
MAP('C', ADBI_PI_UNIQUE_ID),
|
||||
MAP('P', ADBI_PI_NAME),
|
||||
MAP('V', ADBI_PI_VERSION),
|
||||
MAP('T', ADBI_PI_DESCRIPTION),
|
||||
MAP('U', ADBI_PI_URL),
|
||||
MAP('I', ADBI_PI_INSTALLED_SIZE),
|
||||
MAP('S', ADBI_PI_FILE_SIZE),
|
||||
MAP('L', ADBI_PI_LICENSE),
|
||||
MAP('A', ADBI_PI_ARCH),
|
||||
MAP('D', ADBI_PI_DEPENDS),
|
||||
MAP('i', ADBI_PI_INSTALL_IF),
|
||||
MAP('p', ADBI_PI_PROVIDES),
|
||||
MAP('o', ADBI_PI_ORIGIN),
|
||||
MAP('m', ADBI_PI_MAINTAINER),
|
||||
MAP('t', ADBI_PI_BUILD_TIME),
|
||||
MAP('c', ADBI_PI_REPO_COMMIT),
|
||||
MAP('r', ADBI_PI_REPLACES),
|
||||
MAP('k', ADBI_PI_PRIORITY),
|
||||
};
|
||||
if (f < 'A' || f-'A' >= ARRAY_SIZE(map)) return 0;
|
||||
return map[(unsigned char)f - 'A'];
|
||||
}
|
||||
|
||||
/* Schema */
|
||||
|
||||
static apk_blob_t string_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
|
||||
{
|
||||
return adb_r_blob(db, val);
|
||||
}
|
||||
|
||||
static adb_val_t string_fromstring(struct adb *db, apk_blob_t val)
|
||||
{
|
||||
return adb_w_blob(db, val);
|
||||
}
|
||||
|
||||
static struct adb_scalar_schema scalar_string = {
|
||||
.kind = ADB_KIND_BLOB,
|
||||
.tostring = string_tostring,
|
||||
.fromstring = string_fromstring,
|
||||
};
|
||||
|
||||
const struct adb_object_schema schema_string_array = {
|
||||
.kind = ADB_KIND_ARRAY,
|
||||
.num_fields = APK_MAX_PKG_TRIGGERS,
|
||||
.fields = ADB_ARRAY_ITEM(scalar_string),
|
||||
};
|
||||
|
||||
static apk_blob_t hexblob_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
|
||||
{
|
||||
apk_blob_t b = adb_r_blob(db, val), to = APK_BLOB_PTR_LEN(buf, bufsz);
|
||||
|
||||
if (APK_BLOB_IS_NULL(b)) return b;
|
||||
|
||||
apk_blob_push_hexdump(&to, b);
|
||||
if (!APK_BLOB_IS_NULL(to))
|
||||
return APK_BLOB_PTR_PTR(buf, to.ptr-1);
|
||||
|
||||
return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "(%ld bytes)", b.len));
|
||||
}
|
||||
|
||||
static struct adb_scalar_schema scalar_hexblob = {
|
||||
.kind = ADB_KIND_BLOB,
|
||||
.tostring = hexblob_tostring,
|
||||
};
|
||||
|
||||
static apk_blob_t int_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
|
||||
{
|
||||
return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "%u", adb_r_int(db, val)));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static struct adb_scalar_schema scalar_int = {
|
||||
.kind = ADB_KIND_INT,
|
||||
.tostring = int_tostring,
|
||||
.fromstring = int_fromstring,
|
||||
};
|
||||
|
||||
static apk_blob_t oct_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
|
||||
{
|
||||
return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "%o", adb_r_int(db, val)));
|
||||
}
|
||||
|
||||
static struct adb_scalar_schema scalar_oct = {
|
||||
.kind = ADB_KIND_INT,
|
||||
.tostring = oct_tostring,
|
||||
};
|
||||
|
||||
static apk_blob_t hsize_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
|
||||
{
|
||||
off_t v = adb_r_int(db, val);
|
||||
const char *unit = apk_get_human_size(v, &v);
|
||||
|
||||
return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "%jd %s", (intmax_t)v, unit));
|
||||
}
|
||||
|
||||
static struct adb_scalar_schema scalar_hsize = {
|
||||
.kind = ADB_KIND_INT,
|
||||
.tostring = hsize_tostring,
|
||||
.fromstring = int_fromstring,
|
||||
};
|
||||
|
||||
static apk_blob_t dependency_tostring(struct adb_obj *obj, char *buf, size_t bufsz)
|
||||
{
|
||||
apk_blob_t name, ver;
|
||||
unsigned int mask;
|
||||
|
||||
name = adb_ro_blob(obj, ADBI_DEP_NAME);
|
||||
ver = adb_ro_blob(obj, ADBI_DEP_VERSION);
|
||||
|
||||
if (APK_BLOB_IS_NULL(name)) return APK_BLOB_NULL;
|
||||
if (APK_BLOB_IS_NULL(ver)) return name;
|
||||
|
||||
mask = adb_ro_int(obj, ADBI_DEP_MATCH) ?: APK_VERSION_EQUAL;
|
||||
return APK_BLOB_PTR_LEN(buf,
|
||||
snprintf(buf, bufsz, "%s"BLOB_FMT"%s"BLOB_FMT,
|
||||
(mask & APK_VERSION_CONFLICT) ? "!" : "",
|
||||
BLOB_PRINTF(name),
|
||||
apk_version_op_string(mask & ~APK_VERSION_CONFLICT),
|
||||
BLOB_PRINTF(ver)));
|
||||
}
|
||||
|
||||
static int dependency_fromstring(struct adb_obj *obj, apk_blob_t bdep)
|
||||
{
|
||||
extern const apk_spn_match_def apk_spn_dependency_comparer;
|
||||
extern const apk_spn_match_def apk_spn_repotag_separator;
|
||||
apk_blob_t bname, bop, bver = APK_BLOB_NULL, btag;
|
||||
int mask = APK_DEPMASK_ANY;
|
||||
|
||||
/* [!]name[<,<=,<~,=,~,>~,>=,>,><]ver */
|
||||
|
||||
/* parse the version */
|
||||
if (bdep.ptr[0] == '!') {
|
||||
bdep.ptr++;
|
||||
bdep.len--;
|
||||
mask |= APK_VERSION_CONFLICT;
|
||||
}
|
||||
|
||||
if (apk_blob_cspn(bdep, apk_spn_dependency_comparer, &bname, &bop)) {
|
||||
int i;
|
||||
|
||||
if (mask == 0)
|
||||
goto fail;
|
||||
if (!apk_blob_spn(bop, apk_spn_dependency_comparer, &bop, &bver))
|
||||
goto fail;
|
||||
|
||||
mask = 0;
|
||||
for (i = 0; i < bop.len; i++) {
|
||||
switch (bop.ptr[i]) {
|
||||
case '<':
|
||||
mask |= APK_VERSION_LESS;
|
||||
break;
|
||||
case '>':
|
||||
mask |= APK_VERSION_GREATER;
|
||||
break;
|
||||
case '~':
|
||||
mask |= APK_VERSION_FUZZY|APK_VERSION_EQUAL;
|
||||
break;
|
||||
case '=':
|
||||
mask |= APK_VERSION_EQUAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((mask & APK_DEPMASK_CHECKSUM) != APK_DEPMASK_CHECKSUM &&
|
||||
!apk_version_validate(bver))
|
||||
goto fail;
|
||||
} else {
|
||||
bname = bdep;
|
||||
bop = APK_BLOB_NULL;
|
||||
bver = APK_BLOB_NULL;
|
||||
}
|
||||
|
||||
if (apk_blob_cspn(bname, apk_spn_repotag_separator, &bname, &btag))
|
||||
; /* tag = repository tag */
|
||||
|
||||
adb_wo_blob(obj, ADBI_DEP_NAME, bname);
|
||||
if (mask != APK_DEPMASK_ANY) {
|
||||
adb_wo_blob(obj, ADBI_DEP_VERSION, bver);
|
||||
if (mask != APK_VERSION_EQUAL)
|
||||
adb_wo_int(obj, ADBI_DEP_MATCH, mask);
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -EAPKDEPFORMAT;
|
||||
}
|
||||
|
||||
static int dependency_cmp(struct adb_obj *o1, struct adb_obj *o2)
|
||||
{
|
||||
return apk_blob_sort(
|
||||
adb_ro_blob(o1, ADBI_DEP_NAME),
|
||||
adb_ro_blob(o2, ADBI_DEP_NAME));
|
||||
}
|
||||
|
||||
const struct adb_object_schema schema_dependency = {
|
||||
.kind = ADB_KIND_OBJECT,
|
||||
.num_fields = ADBI_DEP_MAX,
|
||||
.tostring = dependency_tostring,
|
||||
.fromstring = dependency_fromstring,
|
||||
.compare = dependency_cmp,
|
||||
.fields = {
|
||||
ADB_FIELD(ADBI_DEP_NAME, "name", scalar_string),
|
||||
ADB_FIELD(ADBI_DEP_VERSION, "version", scalar_string),
|
||||
ADB_FIELD(ADBI_DEP_MATCH, "match", scalar_int),
|
||||
},
|
||||
};
|
||||
|
||||
static int dependencies_fromstring(struct adb_obj *obj, apk_blob_t b)
|
||||
{
|
||||
struct adb_obj dep;
|
||||
apk_blob_t bdep;
|
||||
|
||||
adb_wo_alloca(&dep, &schema_dependency, obj->db);
|
||||
|
||||
while (apk_dep_split(&b, &bdep)) {
|
||||
adb_wo_fromstring(&dep, bdep);
|
||||
adb_wa_append_obj(obj, &dep);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct adb_object_schema schema_dependency_array = {
|
||||
.kind = ADB_KIND_ARRAY,
|
||||
.fromstring = dependencies_fromstring,
|
||||
.num_fields = APK_MAX_PKG_DEPENDENCIES,
|
||||
.pre_commit = adb_wa_sort_unique,
|
||||
.fields = ADB_ARRAY_ITEM(schema_dependency),
|
||||
};
|
||||
|
||||
static int pkginfo_cmp(struct adb_obj *o1, struct adb_obj *o2)
|
||||
{
|
||||
int r;
|
||||
r = apk_blob_sort(
|
||||
adb_ro_blob(o1, ADBI_PI_NAME),
|
||||
adb_ro_blob(o2, ADBI_PI_NAME));
|
||||
if (r) return r;
|
||||
|
||||
r = apk_version_compare_blob(
|
||||
adb_ro_blob(o1, ADBI_PI_VERSION),
|
||||
adb_ro_blob(o2, ADBI_PI_VERSION));
|
||||
switch (r) {
|
||||
case APK_VERSION_LESS: return -1;
|
||||
case APK_VERSION_GREATER: return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct adb_object_schema schema_pkginfo = {
|
||||
.kind = ADB_KIND_OBJECT,
|
||||
.num_fields = ADBI_PI_MAX,
|
||||
.compare = pkginfo_cmp,
|
||||
.fields = {
|
||||
ADB_FIELD(ADBI_PI_NAME, "name", scalar_string),
|
||||
ADB_FIELD(ADBI_PI_VERSION, "version", scalar_string),
|
||||
ADB_FIELD(ADBI_PI_UNIQUE_ID, "unique-id", scalar_int),
|
||||
ADB_FIELD(ADBI_PI_DESCRIPTION, "description", scalar_string),
|
||||
ADB_FIELD(ADBI_PI_ARCH, "arch", scalar_string),
|
||||
ADB_FIELD(ADBI_PI_LICENSE, "license", scalar_string),
|
||||
ADB_FIELD(ADBI_PI_ORIGIN, "origin", scalar_string),
|
||||
ADB_FIELD(ADBI_PI_MAINTAINER, "maintainer", scalar_string),
|
||||
ADB_FIELD(ADBI_PI_URL, "url", scalar_string),
|
||||
ADB_FIELD(ADBI_PI_REPO_COMMIT, "repo-commit", scalar_hexblob),
|
||||
ADB_FIELD(ADBI_PI_BUILD_TIME, "build-time", scalar_int),
|
||||
ADB_FIELD(ADBI_PI_INSTALLED_SIZE,"installed-size",scalar_hsize),
|
||||
ADB_FIELD(ADBI_PI_FILE_SIZE, "file-size", scalar_hsize),
|
||||
ADB_FIELD(ADBI_PI_PRIORITY, "priority", scalar_int),
|
||||
ADB_FIELD(ADBI_PI_DEPENDS, "depends", schema_dependency_array),
|
||||
ADB_FIELD(ADBI_PI_PROVIDES, "provides", schema_dependency_array),
|
||||
ADB_FIELD(ADBI_PI_REPLACES, "replaces", schema_dependency_array),
|
||||
ADB_FIELD(ADBI_PI_INSTALL_IF, "install-if", schema_dependency_array),
|
||||
ADB_FIELD(ADBI_PI_RECOMMENDS, "recommends", schema_dependency_array),
|
||||
},
|
||||
};
|
||||
|
||||
const struct adb_object_schema schema_pkginfo_array = {
|
||||
.kind = ADB_KIND_ARRAY,
|
||||
.num_fields = APK_MAX_INDEX_PACKAGES,
|
||||
.pre_commit = adb_wa_sort,
|
||||
.fields = ADB_ARRAY_ITEM(schema_pkginfo),
|
||||
};
|
||||
|
||||
const struct adb_object_schema schema_index = {
|
||||
.kind = ADB_KIND_OBJECT,
|
||||
.num_fields = ADBI_NDX_MAX,
|
||||
.fields = {
|
||||
ADB_FIELD(ADBI_NDX_DESCRIPTION, "description", scalar_string),
|
||||
ADB_FIELD(ADBI_NDX_PACKAGES, "packages", schema_pkginfo_array),
|
||||
},
|
||||
};
|
||||
|
||||
static uint32_t file_get_default_int(unsigned i)
|
||||
{
|
||||
switch (i) {
|
||||
case ADBI_FI_UID:
|
||||
case ADBI_FI_GID:
|
||||
return 0;
|
||||
case ADBI_FI_MODE:
|
||||
return 0644;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int file_cmp(struct adb_obj *o1, struct adb_obj *o2)
|
||||
{
|
||||
return apk_blob_sort(
|
||||
adb_ro_blob(o1, ADBI_FI_NAME),
|
||||
adb_ro_blob(o2, ADBI_FI_NAME));
|
||||
}
|
||||
|
||||
const struct adb_object_schema schema_file = {
|
||||
.kind = ADB_KIND_OBJECT,
|
||||
.num_fields = ADBI_FI_MAX,
|
||||
.get_default_int = file_get_default_int,
|
||||
.compare = file_cmp,
|
||||
.fields = {
|
||||
ADB_FIELD(ADBI_FI_NAME, "name", scalar_string),
|
||||
ADB_FIELD(ADBI_FI_HASHES, "hash", scalar_hexblob),
|
||||
ADB_FIELD(ADBI_FI_UID, "uid", scalar_int),
|
||||
ADB_FIELD(ADBI_FI_GID, "gid", scalar_int),
|
||||
ADB_FIELD(ADBI_FI_MODE, "mode", scalar_oct),
|
||||
ADB_FIELD(ADBI_FI_XATTRS, "xattr", scalar_hexblob),
|
||||
},
|
||||
};
|
||||
|
||||
const struct adb_object_schema schema_file_array = {
|
||||
.kind = ADB_KIND_ARRAY,
|
||||
.pre_commit = adb_wa_sort,
|
||||
.num_fields = APK_MAX_MANIFEST_FILES,
|
||||
.fields = ADB_ARRAY_ITEM(schema_file),
|
||||
};
|
||||
|
||||
static uint32_t path_get_default_int(unsigned i)
|
||||
{
|
||||
switch (i) {
|
||||
case ADBI_FI_UID:
|
||||
case ADBI_FI_GID:
|
||||
return 0;
|
||||
case ADBI_FI_MODE:
|
||||
return 0755;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct adb_object_schema schema_path = {
|
||||
.kind = ADB_KIND_OBJECT,
|
||||
.num_fields = ADBI_FI_MAX,
|
||||
.get_default_int = path_get_default_int,
|
||||
.compare = file_cmp,
|
||||
.fields = {
|
||||
ADB_FIELD(ADBI_FI_NAME, "name", scalar_string),
|
||||
ADB_FIELD(ADBI_FI_FILES, "files", schema_file_array),
|
||||
ADB_FIELD(ADBI_FI_UID, "uid", scalar_int),
|
||||
ADB_FIELD(ADBI_FI_GID, "gid", scalar_int),
|
||||
ADB_FIELD(ADBI_FI_MODE, "mode", scalar_oct),
|
||||
ADB_FIELD(ADBI_FI_XATTRS, "xattr", scalar_hexblob),
|
||||
},
|
||||
};
|
||||
|
||||
const struct adb_object_schema schema_path_array = {
|
||||
.kind = ADB_KIND_ARRAY,
|
||||
.pre_commit = adb_wa_sort,
|
||||
.num_fields = APK_MAX_MANIFEST_PATHS,
|
||||
.fields = ADB_ARRAY_ITEM(schema_path),
|
||||
};
|
||||
|
||||
const struct adb_object_schema schema_scripts = {
|
||||
.kind = ADB_KIND_OBJECT,
|
||||
.num_fields = ADBI_SCRPT_MAX,
|
||||
.fields = {
|
||||
ADB_FIELD(ADBI_SCRPT_TRIGGER, "trigger", scalar_string),
|
||||
ADB_FIELD(ADBI_SCRPT_PREINST, "pre-install", scalar_string),
|
||||
ADB_FIELD(ADBI_SCRPT_POSTINST, "post-install", scalar_string),
|
||||
ADB_FIELD(ADBI_SCRPT_PREDEINST, "pre-deinstall",scalar_string),
|
||||
ADB_FIELD(ADBI_SCRPT_POSTDEINST,"post-deinstall",scalar_string),
|
||||
ADB_FIELD(ADBI_SCRPT_PREUPGRADE,"pre-upgrade", scalar_string),
|
||||
ADB_FIELD(ADBI_SCRPT_POSTUPGRADE,"post-upgrade",scalar_string),
|
||||
},
|
||||
};
|
||||
|
||||
const struct adb_object_schema schema_package = {
|
||||
.kind = ADB_KIND_OBJECT,
|
||||
.num_fields = ADBI_PKG_MAX,
|
||||
.fields = {
|
||||
ADB_FIELD(ADBI_PKG_PKGINFO, "info", schema_pkginfo),
|
||||
ADB_FIELD(ADBI_PKG_PATHS, "paths", schema_path_array),
|
||||
ADB_FIELD(ADBI_PKG_SCRIPTS, "scripts", schema_scripts),
|
||||
ADB_FIELD(ADBI_PKG_TRIGGERS, "triggers", schema_string_array),
|
||||
//ADB_FIELD(ADBI_PKG_PASSWD, "passwd", schema_string_array),
|
||||
},
|
||||
};
|
||||
|
||||
const struct adb_adb_schema schema_package_adb = {
|
||||
.kind = ADB_KIND_ADB,
|
||||
.schema_id = ADB_SCHEMA_PACKAGE,
|
||||
};
|
||||
|
||||
const struct adb_object_schema schema_package_adb_array = {
|
||||
.kind = ADB_KIND_ARRAY,
|
||||
.num_fields = APK_MAX_INDEX_PACKAGES,
|
||||
.fields = ADB_ARRAY_ITEM(schema_package_adb),
|
||||
};
|
||||
|
||||
const struct adb_object_schema schema_idb = {
|
||||
.kind = ADB_KIND_OBJECT,
|
||||
.num_fields = ADBI_IDB_MAX,
|
||||
.fields = {
|
||||
ADB_FIELD(ADBI_IDB_PACKAGES, "packages", schema_package_adb_array),
|
||||
},
|
||||
};
|
|
@ -0,0 +1,91 @@
|
|||
#include "adb.h"
|
||||
|
||||
/* Schemas */
|
||||
#define ADB_SCHEMA_INDEX 0x78646e69 // indx
|
||||
#define ADB_SCHEMA_PACKAGE 0x676b6370 // pckg
|
||||
#define ADB_SCHEMA_INSTALLED_DB 0x00626469 // idb
|
||||
|
||||
/* Dependency */
|
||||
#define ADBI_DEP_NAME 0x01
|
||||
#define ADBI_DEP_VERSION 0x02
|
||||
#define ADBI_DEP_MATCH 0x03
|
||||
#define ADBI_DEP_MAX 0x04
|
||||
|
||||
/* Package Info */
|
||||
#define ADBI_PI_NAME 0x01
|
||||
#define ADBI_PI_VERSION 0x02
|
||||
#define ADBI_PI_UNIQUE_ID 0x03
|
||||
#define ADBI_PI_DESCRIPTION 0x04
|
||||
#define ADBI_PI_ARCH 0x05
|
||||
#define ADBI_PI_LICENSE 0x06
|
||||
#define ADBI_PI_ORIGIN 0x07
|
||||
#define ADBI_PI_MAINTAINER 0x08
|
||||
#define ADBI_PI_URL 0x09
|
||||
#define ADBI_PI_REPO_COMMIT 0x0a
|
||||
#define ADBI_PI_BUILD_TIME 0x0b
|
||||
#define ADBI_PI_INSTALLED_SIZE 0x0c
|
||||
#define ADBI_PI_FILE_SIZE 0x0d
|
||||
#define ADBI_PI_PRIORITY 0x0e
|
||||
#define ADBI_PI_DEPENDS 0x0f
|
||||
#define ADBI_PI_PROVIDES 0x10
|
||||
#define ADBI_PI_REPLACES 0x11
|
||||
#define ADBI_PI_INSTALL_IF 0x12
|
||||
#define ADBI_PI_RECOMMENDS 0x13
|
||||
#define ADBI_PI_MAX 0x14
|
||||
|
||||
/* File Info */
|
||||
#define ADBI_FI_NAME 0x01
|
||||
#define ADBI_FI_HASHES 0x02
|
||||
#define ADBI_FI_FILES 0x02
|
||||
#define ADBI_FI_MODE 0x03
|
||||
#define ADBI_FI_UID 0x04
|
||||
#define ADBI_FI_GID 0x05
|
||||
#define ADBI_FI_XATTRS 0x06
|
||||
#define ADBI_FI_MAX 0x07
|
||||
|
||||
/* Scripts */
|
||||
#define ADBI_SCRPT_TRIGGER 0x01
|
||||
#define ADBI_SCRPT_PREINST 0x02
|
||||
#define ADBI_SCRPT_POSTINST 0x03
|
||||
#define ADBI_SCRPT_PREDEINST 0x04
|
||||
#define ADBI_SCRPT_POSTDEINST 0x05
|
||||
#define ADBI_SCRPT_PREUPGRADE 0x06
|
||||
#define ADBI_SCRPT_POSTUPGRADE 0x07
|
||||
#define ADBI_SCRPT_MAX 0x08
|
||||
|
||||
/* Package */
|
||||
#define ADBI_PKG_PKGINFO 0x01
|
||||
#define ADBI_PKG_PATHS 0x02
|
||||
#define ADBI_PKG_SCRIPTS 0x03
|
||||
#define ADBI_PKG_TRIGGERS 0x04
|
||||
#define ADBI_PKG_PASSWD 0x05
|
||||
#define ADBI_PKG_MAX 0x06
|
||||
|
||||
/* Index */
|
||||
#define ADBI_NDX_DESCRIPTION 0x01
|
||||
#define ADBI_NDX_PACKAGES 0x02
|
||||
#define ADBI_NDX_MAX 0x03
|
||||
|
||||
/* Installed DB */
|
||||
#define ADBI_IDB_PACKAGES 0x01
|
||||
#define ADBI_IDB_MAX 0x02
|
||||
|
||||
/* */
|
||||
#define APK_MAX_PKG_DEPENDENCIES 512
|
||||
#define APK_MAX_PKG_REPLACES 32
|
||||
#define APK_MAX_PKG_TRIGGERS 32
|
||||
#define APK_MAX_INDEX_PACKAGES 20000
|
||||
#define APK_MAX_MANIFEST_FILES 8000
|
||||
#define APK_MAX_MANIFEST_PATHS 6000
|
||||
|
||||
extern const struct adb_object_schema
|
||||
schema_dependency, schema_dependency_array,
|
||||
schema_pkginfo, schema_pkginfo_array,
|
||||
schema_file, schema_file_array, schema_path, schema_path_array,
|
||||
schema_string_array, schema_scripts, schema_package, schema_package_adb_array,
|
||||
schema_index, schema_idb;
|
||||
|
||||
/* */
|
||||
int apk_dep_split(apk_blob_t *b, apk_blob_t *bdep);
|
||||
adb_val_t adb_wo_pkginfo(struct adb_obj *obj, unsigned int f, apk_blob_t val);
|
||||
unsigned int adb_pkg_field_index(char f);
|
|
@ -61,7 +61,7 @@ struct apk_applet {
|
|||
int (*main)(void *ctx, struct apk_database *db, struct apk_string_array *args);
|
||||
};
|
||||
|
||||
extern const struct apk_option_group optgroup_global, optgroup_commit;
|
||||
extern const struct apk_option_group optgroup_global, optgroup_commit, optgroup_signing;
|
||||
|
||||
void apk_help(struct apk_applet *applet);
|
||||
void apk_applet_register(struct apk_applet *);
|
||||
|
|
|
@ -68,6 +68,7 @@ static inline const EVP_MD *apk_checksum_default(void)
|
|||
#define APK_BLOB_BUF(buf) ((apk_blob_t){sizeof(buf), (char *)(buf)})
|
||||
#define APK_BLOB_CSUM(csum) ((apk_blob_t){(csum).type, (char *)(csum).data})
|
||||
#define APK_BLOB_STRUCT(s) ((apk_blob_t){sizeof(s), (char*)&(s)})
|
||||
#define APK_BLOB_STRLIT(s) ((apk_blob_t){sizeof(s)-1, (char *)(s)})
|
||||
#define APK_BLOB_PTR_LEN(beg,len) ((apk_blob_t){(len), (beg)})
|
||||
#define APK_BLOB_PTR_PTR(beg,end) APK_BLOB_PTR_LEN((beg),(end)-(beg)+1)
|
||||
|
||||
|
@ -95,6 +96,7 @@ apk_blob_t apk_blob_pushed(apk_blob_t buffer, apk_blob_t left);
|
|||
unsigned long apk_blob_hash_seed(apk_blob_t, unsigned long seed);
|
||||
unsigned long apk_blob_hash(apk_blob_t str);
|
||||
int apk_blob_compare(apk_blob_t a, apk_blob_t b);
|
||||
int apk_blob_sort(apk_blob_t a, apk_blob_t b);
|
||||
int apk_blob_ends_with(apk_blob_t str, apk_blob_t suffix);
|
||||
int apk_blob_for_each_segment(apk_blob_t blob, const char *split,
|
||||
apk_blob_cb cb, void *ctx);
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "apk_provider_data.h"
|
||||
#include "apk_solver_data.h"
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
struct apk_name;
|
||||
APK_ARRAY(apk_name_array, struct apk_name *);
|
||||
|
||||
|
@ -130,6 +132,7 @@ struct apk_db_options {
|
|||
const char *cache_dir;
|
||||
const char *repositories_file;
|
||||
struct list_head repository_list;
|
||||
struct apk_string_array *private_keys;
|
||||
};
|
||||
|
||||
#define APK_REPOSITORY_CACHED 0
|
||||
|
@ -162,6 +165,8 @@ struct apk_database {
|
|||
int compat_newfeatures : 1;
|
||||
int compat_notinstallable : 1;
|
||||
|
||||
struct adb_trust trust;
|
||||
|
||||
struct apk_dependency_array *world;
|
||||
struct apk_protected_path_array *protected_paths;
|
||||
struct apk_repository repos[APK_MAX_REPOS];
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
|
||||
#define EAPKBADURL 1024
|
||||
#define EAPKSTALEINDEX 1025
|
||||
#define EAPKFORMAT 1026
|
||||
#define EAPKDEPFORMAT 1027
|
||||
|
||||
static inline void *ERR_PTR(long error) { return (void*) error; }
|
||||
static inline void *ERR_CAST(const void *ptr) { return (void*) ptr; }
|
||||
|
@ -63,6 +65,9 @@ static inline int IS_ERR_OR_NULL(const void *ptr) { return IS_ERR(ptr) || !ptr;
|
|||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
#endif
|
||||
|
||||
#define ROUND_DOWN(x,a) ((x) & ~(a-1))
|
||||
#define ROUND_UP(x,a) (((x)+(a)-1) & ~((a)-1))
|
||||
|
||||
extern int apk_verbosity;
|
||||
extern unsigned int apk_flags, apk_force;
|
||||
extern const char *apk_arch;
|
||||
|
|
|
@ -141,6 +141,7 @@ struct apk_ostream_ops {
|
|||
|
||||
struct apk_ostream {
|
||||
const struct apk_ostream_ops *ops;
|
||||
int rc;
|
||||
};
|
||||
|
||||
struct apk_ostream *apk_ostream_gzip(struct apk_ostream *);
|
||||
|
@ -149,13 +150,15 @@ struct apk_ostream *apk_ostream_to_fd(int fd);
|
|||
struct apk_ostream *apk_ostream_to_file(int atfd, const char *file, const char *tmpfile, mode_t mode);
|
||||
struct apk_ostream *apk_ostream_to_file_gz(int atfd, const char *file, const char *tmpfile, mode_t mode);
|
||||
size_t apk_ostream_write_string(struct apk_ostream *ostream, const char *string);
|
||||
static inline void apk_ostream_cancel(struct apk_ostream *os, int rc) { if (!os->rc) os->rc = rc; }
|
||||
static inline ssize_t apk_ostream_write(struct apk_ostream *os, const void *buf, size_t size)
|
||||
{
|
||||
return os->ops->write(os, buf, size);
|
||||
}
|
||||
static inline int apk_ostream_close(struct apk_ostream *os)
|
||||
{
|
||||
return os->ops->close(os);
|
||||
int rc = os->rc;
|
||||
return os->ops->close(os) ?: rc;
|
||||
}
|
||||
|
||||
apk_blob_t apk_blob_from_istream(struct apk_istream *istream, size_t size);
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include "apk_adb.h"
|
||||
#include "apk_applet.h"
|
||||
#include "apk_print.h"
|
||||
|
||||
struct adb_dump_ctx {
|
||||
struct adb *db;
|
||||
struct adb_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 || apk_blob_chr(b, '\n')) {
|
||||
/* 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 adb_trust *trust, int fd)
|
||||
{
|
||||
struct adb db;
|
||||
struct adb_dump_ctx ctx = {
|
||||
.db = &db,
|
||||
.pfx = ctx.prefix,
|
||||
.trust = trust,
|
||||
};
|
||||
int r;
|
||||
|
||||
r = adb_m_map(&db, fd, 0, NULL);
|
||||
if (r) return r;
|
||||
|
||||
dump_adb(&ctx);
|
||||
adb_free(&db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adbdump_main(void *pctx, struct apk_database *db, struct apk_string_array *args)
|
||||
{
|
||||
char **arg;
|
||||
int r;
|
||||
|
||||
foreach_array_item(arg, args) {
|
||||
r = mmap_and_dump_adb(&db->trust, open(*arg, O_RDONLY));
|
||||
if (r) {
|
||||
apk_error("%s: %s", *arg, apk_error_str(r));
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct apk_applet apk_adbdump = {
|
||||
.name = "adbdump",
|
||||
.open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_NO_REPOS,
|
||||
.main = adbdump_main,
|
||||
};
|
||||
APK_DEFINE_APPLET(apk_adbdump);
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "apk_applet.h"
|
||||
#include "apk_print.h"
|
||||
|
||||
struct sign_ctx {
|
||||
struct adb_xfrm xfrm;
|
||||
struct apk_database *db;
|
||||
int reset_signatures : 1;
|
||||
int signatures_written : 1;
|
||||
};
|
||||
|
||||
#define ADBSIGN_OPTIONS(OPT) \
|
||||
OPT(OPT_ADBSIGN_reset_signatures, "reset-signatures")
|
||||
|
||||
APK_OPT_APPLET(option_desc, ADBSIGN_OPTIONS);
|
||||
|
||||
static int option_parse_applet(void *pctx, struct apk_db_options *dbopts, int optch, const char *optarg)
|
||||
{
|
||||
struct sign_ctx *ctx = (struct sign_ctx *) pctx;
|
||||
|
||||
switch (optch) {
|
||||
case OPT_ADBSIGN_reset_signatures:
|
||||
ctx->reset_signatures = 1;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct apk_option_group optgroup_applet = {
|
||||
.desc = option_desc,
|
||||
.parse = option_parse_applet,
|
||||
};
|
||||
|
||||
static int update_signatures(struct adb_xfrm *xfrm, struct adb_block *blk, struct apk_istream *is)
|
||||
{
|
||||
struct sign_ctx *ctx = container_of(xfrm, struct sign_ctx, xfrm);
|
||||
int r;
|
||||
|
||||
switch (blk ? ADB_BLOCK_TYPE(blk) : -1) {
|
||||
case ADB_BLOCK_ADB:
|
||||
return adb_c_block_copy(xfrm->os, blk, is, &xfrm->vfy);
|
||||
case ADB_BLOCK_SIG:
|
||||
if (ctx->reset_signatures)
|
||||
break;
|
||||
return adb_c_block_copy(xfrm->os, blk, is, NULL);
|
||||
default:
|
||||
if (!ctx->signatures_written) {
|
||||
ctx->signatures_written = 1;
|
||||
r = adb_trust_write_signatures(&ctx->db->trust, &xfrm->db, &xfrm->vfy, xfrm->os);
|
||||
if (r) return r;
|
||||
}
|
||||
if (!blk) break;
|
||||
return adb_c_block_copy(xfrm->os, blk, is, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adbsign_main(void *pctx, struct apk_database *db, struct apk_string_array *args)
|
||||
{
|
||||
char tmpname[PATH_MAX];
|
||||
struct sign_ctx *ctx = pctx;
|
||||
char **arg;
|
||||
int r;
|
||||
|
||||
ctx->db = db;
|
||||
foreach_array_item(arg, args) {
|
||||
if (snprintf(tmpname, sizeof tmpname, "%s.tmp", *arg) >= sizeof tmpname) {
|
||||
r = ENAMETOOLONG;
|
||||
} else {
|
||||
ctx->xfrm.is = apk_istream_from_file(AT_FDCWD, *arg);
|
||||
ctx->xfrm.os = apk_ostream_to_file(AT_FDCWD, *arg, tmpname, 0644);
|
||||
adb_c_xfrm(&ctx->xfrm, update_signatures);
|
||||
apk_istream_close(ctx->xfrm.is);
|
||||
r = apk_ostream_close(ctx->xfrm.os);
|
||||
}
|
||||
if (r) apk_error("%s: %s", *arg, apk_error_str(r));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct apk_applet apk_adbsign = {
|
||||
.name = "adbsign",
|
||||
.open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_NO_REPOS,
|
||||
.context_size = sizeof(struct sign_ctx),
|
||||
.optgroups = { &optgroup_global, &optgroup_signing, &optgroup_applet },
|
||||
.main = adbsign_main,
|
||||
};
|
||||
|
||||
APK_DEFINE_APPLET(apk_adbsign);
|
|
@ -0,0 +1,238 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "apk_adb.h"
|
||||
#include "apk_applet.h"
|
||||
|
||||
struct conv_script {
|
||||
struct list_head script_node;
|
||||
char csum_len;
|
||||
char csum[32];
|
||||
int type;
|
||||
size_t size;
|
||||
apk_blob_t *triggers;
|
||||
char script[];
|
||||
};
|
||||
|
||||
struct conv_ctx {
|
||||
struct apk_database *db;
|
||||
struct adb_obj pkgs;
|
||||
|
||||
struct list_head script_head;
|
||||
struct adb dbi;
|
||||
struct adb dbp;
|
||||
int found;
|
||||
};
|
||||
|
||||
static int read_script(void *pctx, const struct apk_file_info *ae, struct apk_istream *is)
|
||||
{
|
||||
struct conv_ctx *ctx = pctx;
|
||||
struct conv_script *s;
|
||||
char *fncsum, *fnaction;
|
||||
apk_blob_t blob;
|
||||
int type;
|
||||
|
||||
/* The scripts db expects regular file with name in format:
|
||||
* pkgname-version.identity.action */
|
||||
if (!S_ISREG(ae->mode)) return 0;
|
||||
fnaction = memrchr(ae->name, '.', strlen(ae->name));
|
||||
if (!fnaction || fnaction == ae->name) return 0;
|
||||
fncsum = memrchr(ae->name, '.', fnaction - ae->name - 1);
|
||||
if (!fncsum) return 0;
|
||||
fnaction++;
|
||||
fncsum++;
|
||||
|
||||
/* Parse it */
|
||||
type = adb_s_field_by_name(&schema_scripts, fnaction);
|
||||
if (!type) return 0;
|
||||
|
||||
blob = APK_BLOB_PTR_PTR(fncsum, fnaction - 2);
|
||||
if (blob.len+1 > sizeof s->csum) return 0;
|
||||
|
||||
s = malloc(sizeof(struct conv_script) + ae->size);
|
||||
if (!s) return 0;
|
||||
memset(s, 0, sizeof *s);
|
||||
list_init(&s->script_node);
|
||||
s->csum_len = blob.len;
|
||||
memcpy(s->csum, blob.ptr, blob.len);
|
||||
s->type = type;
|
||||
s->size = ae->size;
|
||||
apk_istream_read(is, s->script, s->size);
|
||||
if (s->script[s->size-1] == '\n') {
|
||||
while (s->size > 1 && s->script[s->size-2] == '\n')
|
||||
s->size--;
|
||||
}
|
||||
list_add_tail(&s->script_node, &ctx->script_head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct conv_script *find_pkg(struct conv_ctx *ctx, apk_blob_t identity, int type)
|
||||
{
|
||||
struct conv_script *s;
|
||||
list_for_each_entry(s, &ctx->script_head, script_node)
|
||||
if (apk_blob_compare(APK_BLOB_PTR_LEN(s->csum, s->csum_len), identity) == 0)
|
||||
return s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_triggers(struct conv_ctx *ctx, struct apk_istream *is)
|
||||
{
|
||||
apk_blob_t l, r, nl = APK_BLOB_STR("\n"), spc = APK_BLOB_STR(" ");
|
||||
struct conv_script *s;
|
||||
|
||||
if (IS_ERR(is)) return PTR_ERR(is);
|
||||
|
||||
while (!APK_BLOB_IS_NULL(l = apk_istream_get_delim(is, nl))) {
|
||||
if (!apk_blob_split(l, spc, &l, &r)) continue;
|
||||
s = find_pkg(ctx, l, ADBI_SCRPT_TRIGGER);
|
||||
if (!s) continue;
|
||||
|
||||
s->triggers = apk_atomize_dup(&ctx->db->atoms, r);
|
||||
}
|
||||
|
||||
apk_istream_close(is);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void convert_idb(struct conv_ctx *ctx, struct apk_istream *is)
|
||||
{
|
||||
struct apk_checksum csum;
|
||||
struct adb_obj pkg, pkginfo, files, file, paths, path, scripts, triggers;
|
||||
apk_blob_t l, val, spc = APK_BLOB_STR(" "), nl = APK_BLOB_STR("\n");
|
||||
struct conv_script *s;
|
||||
int i;
|
||||
|
||||
adb_wo_alloca(&scripts, &schema_scripts, &ctx->dbp);
|
||||
adb_wo_alloca(&triggers, &schema_string_array, &ctx->dbp);
|
||||
adb_wo_alloca(&pkginfo, &schema_pkginfo, &ctx->dbp);
|
||||
adb_wo_alloca(&files, &schema_file_array, &ctx->dbp);
|
||||
adb_wo_alloca(&file, &schema_file, &ctx->dbp);
|
||||
adb_wo_alloca(&paths, &schema_path_array, &ctx->dbp);
|
||||
adb_wo_alloca(&path, &schema_path, &ctx->dbp);
|
||||
adb_wo_alloca(&pkg, &schema_package, &ctx->dbp);
|
||||
|
||||
while (!APK_BLOB_IS_NULL(l = apk_istream_get_delim(is, nl))) {
|
||||
if (l.len < 2) {
|
||||
adb_wa_append_obj(&files, &file);
|
||||
adb_wo_obj(&path, ADBI_FI_FILES, &files);
|
||||
adb_wa_append_obj(&paths, &path);
|
||||
|
||||
adb_wo_obj(&pkg, ADBI_PKG_PKGINFO, &pkginfo);
|
||||
adb_wo_obj(&pkg, ADBI_PKG_PATHS, &paths);
|
||||
adb_w_rootobj(&pkg);
|
||||
|
||||
adb_wa_append(&ctx->pkgs, adb_w_adb(&ctx->dbi, &ctx->dbp));
|
||||
adb_reset(&ctx->dbp);
|
||||
continue;
|
||||
}
|
||||
val = APK_BLOB_PTR_LEN(l.ptr+2, l.len-2);
|
||||
i = adb_pkg_field_index(l.ptr[0]);
|
||||
if (i > 0) adb_wo_pkginfo(&pkginfo, i, val);
|
||||
|
||||
switch (l.ptr[0]) {
|
||||
case 'C': // pkg checksum
|
||||
list_for_each_entry(s, &ctx->script_head, script_node) {
|
||||
if (apk_blob_compare(APK_BLOB_PTR_LEN(s->csum, s->csum_len), val) != 0)
|
||||
continue;
|
||||
|
||||
adb_wo_blob(&scripts, s->type, APK_BLOB_PTR_LEN(s->script, s->size));
|
||||
if (s->type == ADBI_SCRPT_TRIGGER && s->triggers) {
|
||||
apk_blob_t r = *s->triggers, l = *s->triggers;
|
||||
while (apk_blob_split(r, spc, &l, &r))
|
||||
adb_wa_append(&triggers, adb_w_blob(&ctx->dbp, l));
|
||||
adb_wa_append(&triggers, adb_w_blob(&ctx->dbp, r));
|
||||
adb_wo_obj(&pkg, ADBI_PKG_TRIGGERS, &triggers);
|
||||
}
|
||||
}
|
||||
adb_wo_obj(&pkg, ADBI_PKG_SCRIPTS, &scripts);
|
||||
break;
|
||||
case 'F': // directory name
|
||||
adb_wa_append_obj(&files, &file);
|
||||
adb_wo_obj(&path, ADBI_FI_FILES, &files);
|
||||
adb_wa_append_obj(&paths, &path);
|
||||
|
||||
adb_wo_blob(&path, ADBI_FI_NAME, val);
|
||||
break;
|
||||
case 'M': // directory mode: uid:gid:mode:xattrcsum
|
||||
adb_wo_int(&path, ADBI_FI_UID, apk_blob_pull_uint(&val, 10));
|
||||
apk_blob_pull_char(&val, ':');
|
||||
adb_wo_int(&path, ADBI_FI_GID, apk_blob_pull_uint(&val, 10));
|
||||
apk_blob_pull_char(&val, ':');
|
||||
adb_wo_int(&path, ADBI_FI_MODE, apk_blob_pull_uint(&val, 8));
|
||||
break;
|
||||
case 'R': // file name
|
||||
adb_wa_append_obj(&files, &file);
|
||||
adb_wo_blob(&file, ADBI_FI_NAME, val);
|
||||
break;
|
||||
case 'a': // file mode: uid:gid:mode:xattrcsum
|
||||
adb_wo_int(&file, ADBI_FI_UID, apk_blob_pull_uint(&val, 10));
|
||||
apk_blob_pull_char(&val, ':');
|
||||
adb_wo_int(&file, ADBI_FI_GID, apk_blob_pull_uint(&val, 10));
|
||||
apk_blob_pull_char(&val, ':');
|
||||
adb_wo_int(&file, ADBI_FI_MODE, apk_blob_pull_uint(&val, 8));
|
||||
break;
|
||||
case 'Z': // file content hash
|
||||
apk_blob_pull_csum(&val, &csum);
|
||||
adb_wo_blob(&file, ADBI_FI_HASHES, APK_BLOB_CSUM(csum));
|
||||
break;
|
||||
case 's': // repository_tag
|
||||
case 'f': // fix required (flags: fsx)
|
||||
/* FIXME */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int conv_main(void *pctx, struct apk_database *db, struct apk_string_array *args)
|
||||
{
|
||||
struct conv_ctx *ctx = pctx;
|
||||
struct adb_obj idb;
|
||||
int r;
|
||||
|
||||
ctx->db = db;
|
||||
list_init(&ctx->script_head);
|
||||
|
||||
adb_w_init_alloca(&ctx->dbi, ADB_SCHEMA_INSTALLED_DB, 10);
|
||||
adb_w_init_alloca(&ctx->dbp, ADB_SCHEMA_PACKAGE, 100);
|
||||
adb_wo_alloca(&idb, &schema_idb, &ctx->dbi);
|
||||
adb_wo_alloca(&ctx->pkgs, &schema_package_adb_array, &ctx->dbi);
|
||||
|
||||
apk_tar_parse(
|
||||
apk_istream_from_file(db->root_fd, "lib/apk/db/scripts.tar"),
|
||||
read_script, ctx, &db->id_cache);
|
||||
|
||||
read_triggers(ctx, apk_istream_from_file(db->root_fd, "lib/apk/db/triggers"));
|
||||
|
||||
convert_idb(ctx, apk_istream_from_fd_url(db->root_fd, "lib/apk/db/installed"));
|
||||
|
||||
adb_wo_obj(&idb, ADBI_IDB_PACKAGES, &ctx->pkgs);
|
||||
adb_w_rootobj(&idb);
|
||||
|
||||
r = adb_c_create(
|
||||
//apk_ostream_to_file(db->root_fd, "lib/apk/db/installed.adb", 0644),
|
||||
apk_ostream_to_file(AT_FDCWD, "installed.adb", 0, 0644),
|
||||
&ctx->dbi, &db->trust);
|
||||
if (r == 0) {
|
||||
// unlink old files
|
||||
}
|
||||
|
||||
adb_free(&ctx->dbi);
|
||||
adb_free(&ctx->dbp);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct apk_applet apk_convdb = {
|
||||
.name = "convdb",
|
||||
.open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_NO_REPOS,
|
||||
.context_size = sizeof(struct conv_ctx),
|
||||
.optgroups = { &optgroup_global, &optgroup_signing },
|
||||
.main = conv_main,
|
||||
};
|
||||
APK_DEFINE_APPLET(apk_convdb);
|
|
@ -0,0 +1,105 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "apk_adb.h"
|
||||
#include "apk_applet.h"
|
||||
|
||||
struct conv_ctx {
|
||||
struct apk_database *db;
|
||||
struct adb_obj pkgs;
|
||||
struct adb dbi;
|
||||
struct apk_sign_ctx sctx;
|
||||
int found;
|
||||
};
|
||||
|
||||
static void convert_index(struct conv_ctx *ctx, struct apk_istream *is)
|
||||
{
|
||||
struct adb_obj pkginfo;
|
||||
apk_blob_t token = APK_BLOB_STR("\n"), l;
|
||||
int i;
|
||||
|
||||
adb_wo_alloca(&pkginfo, &schema_pkginfo, &ctx->dbi);
|
||||
|
||||
while (!APK_BLOB_IS_NULL(l = apk_istream_get_delim(is, token))) {
|
||||
if (l.len < 2) {
|
||||
adb_wa_append_obj(&ctx->pkgs, &pkginfo);
|
||||
continue;
|
||||
}
|
||||
i = adb_pkg_field_index(l.ptr[0]);
|
||||
if (i > 0) adb_wo_pkginfo(&pkginfo, i, APK_BLOB_PTR_LEN(l.ptr+2, l.len-2));
|
||||
}
|
||||
}
|
||||
|
||||
static int load_apkindex(void *sctx, const struct apk_file_info *fi,
|
||||
struct apk_istream *is)
|
||||
{
|
||||
struct conv_ctx *ctx = sctx;
|
||||
int r;
|
||||
|
||||
r = apk_sign_ctx_process_file(&ctx->sctx, fi, is);
|
||||
if (r <= 0) return r;
|
||||
|
||||
if (strcmp(fi->name, "APKINDEX") == 0) {
|
||||
ctx->found = 1;
|
||||
convert_index(ctx, is);
|
||||
apk_istream_close(is);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_index(struct conv_ctx *ctx, struct apk_istream *is)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
if (IS_ERR_OR_NULL(is)) return is ? PTR_ERR(is) : -EINVAL;
|
||||
|
||||
ctx->found = 0;
|
||||
apk_sign_ctx_init(&ctx->sctx, APK_SIGN_VERIFY, NULL, ctx->db->keys_fd);
|
||||
r = apk_tar_parse(
|
||||
apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &ctx->sctx),
|
||||
load_apkindex, ctx, &ctx->db->id_cache);
|
||||
apk_sign_ctx_free(&ctx->sctx);
|
||||
if (r >= 0 && ctx->found == 0) r = -ENOMSG;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int conv_main(void *pctx, struct apk_database *db, struct apk_string_array *args)
|
||||
{
|
||||
char **arg;
|
||||
struct conv_ctx *ctx = pctx;
|
||||
struct adb_obj ndx;
|
||||
int r;
|
||||
|
||||
ctx->db = db;
|
||||
adb_w_init_alloca(&ctx->dbi, ADB_SCHEMA_INDEX, 1000);
|
||||
adb_wo_alloca(&ndx, &schema_index, &ctx->dbi);
|
||||
adb_wo_alloca(&ctx->pkgs, &schema_pkginfo_array, &ctx->dbi);
|
||||
|
||||
foreach_array_item(arg, args) {
|
||||
r = load_index(ctx, apk_istream_from_url(*arg));
|
||||
if (r) goto err;
|
||||
fprintf(stderr, "%s: %u packages\n", *arg, adb_ra_num(&ctx->pkgs));
|
||||
}
|
||||
|
||||
adb_wo_obj(&ndx, ADBI_NDX_PACKAGES, &ctx->pkgs);
|
||||
adb_w_rootobj(&ndx);
|
||||
|
||||
r = adb_c_create(apk_ostream_to_fd(STDOUT_FILENO), &ctx->dbi, &db->trust);
|
||||
err:
|
||||
adb_free(&ctx->dbi);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct apk_applet apk_convndx = {
|
||||
.name = "convndx",
|
||||
.open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_NO_REPOS,
|
||||
.context_size = sizeof(struct conv_ctx),
|
||||
.optgroups = { &optgroup_global, &optgroup_signing },
|
||||
.main = conv_main,
|
||||
};
|
||||
APK_DEFINE_APPLET(apk_convndx);
|
|
@ -0,0 +1,329 @@
|
|||
/* app_mkndx.c - Alpine Package Keeper (APK)
|
||||
*
|
||||
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
|
||||
* Copyright (C) 2008-2020 Timo Teräs <timo.teras@iki.fi>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation. See http://www.gnu.org/ for details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "apk_adb.h"
|
||||
#include "apk_applet.h"
|
||||
#include "apk_database.h"
|
||||
#include "apk_print.h"
|
||||
|
||||
struct mkndx_ctx {
|
||||
const char *index;
|
||||
const char *output;
|
||||
const char *description;
|
||||
apk_blob_t rewrite_arch;
|
||||
|
||||
apk_blob_t r;
|
||||
struct adb db;
|
||||
struct adb_obj pkgs;
|
||||
time_t index_mtime;
|
||||
|
||||
struct apk_sign_ctx sctx;
|
||||
size_t file_size;
|
||||
};
|
||||
|
||||
#define MKNDX_OPTIONS(OPT) \
|
||||
OPT(OPT_MKNDX_description, APK_OPT_ARG APK_OPT_SH("d") "description") \
|
||||
OPT(OPT_MKNDX_index, APK_OPT_ARG APK_OPT_SH("x") "index") \
|
||||
OPT(OPT_MKNDX_output, APK_OPT_ARG APK_OPT_SH("o") "output") \
|
||||
OPT(OPT_MKNDX_rewrite_arch, APK_OPT_ARG "rewrite-arch")
|
||||
|
||||
APK_OPT_APPLET(option_desc, MKNDX_OPTIONS);
|
||||
|
||||
static int option_parse_applet(void *ctx, struct apk_db_options *dbopts, int optch, const char *optarg)
|
||||
{
|
||||
struct mkndx_ctx *ictx = ctx;
|
||||
|
||||
switch (optch) {
|
||||
case OPT_MKNDX_index:
|
||||
ictx->index = optarg;
|
||||
break;
|
||||
case OPT_MKNDX_output:
|
||||
ictx->output = optarg;
|
||||
break;
|
||||
case OPT_MKNDX_description:
|
||||
ictx->description = optarg;
|
||||
break;
|
||||
case OPT_MKNDX_rewrite_arch:
|
||||
ictx->rewrite_arch = APK_BLOB_STR(optarg);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct apk_option_group optgroup_applet = {
|
||||
.desc = option_desc,
|
||||
.parse = option_parse_applet,
|
||||
};
|
||||
|
||||
struct field {
|
||||
apk_blob_t str;
|
||||
unsigned int ndx;
|
||||
};
|
||||
#define FIELD(s, n) { .str = APK_BLOB_STRLIT(s), .ndx = n }
|
||||
|
||||
static int cmpfield(const void *pa, const void *pb)
|
||||
{
|
||||
const struct field *a = pa, *b = pb;
|
||||
return apk_blob_sort(a->str, b->str);
|
||||
}
|
||||
|
||||
static adb_val_t mkndx_read_v2_pkginfo(struct adb *db, struct apk_istream *is, size_t file_size, struct apk_sign_ctx *sctx, apk_blob_t rewrite_arch)
|
||||
{
|
||||
static struct field fields[] = {
|
||||
FIELD("arch", ADBI_PI_ARCH),
|
||||
FIELD("builddate", ADBI_PI_BUILD_TIME),
|
||||
FIELD("commit", ADBI_PI_REPO_COMMIT),
|
||||
FIELD("datahash", 0),
|
||||
FIELD("depend", ADBI_PI_DEPENDS),
|
||||
FIELD("install_if", ADBI_PI_INSTALL_IF),
|
||||
FIELD("license", ADBI_PI_LICENSE),
|
||||
FIELD("maintainer", ADBI_PI_MAINTAINER),
|
||||
FIELD("origin", ADBI_PI_ORIGIN),
|
||||
FIELD("packager", 0),
|
||||
FIELD("pkgdesc", ADBI_PI_DESCRIPTION),
|
||||
FIELD("pkgname", ADBI_PI_NAME),
|
||||
FIELD("pkgver", ADBI_PI_VERSION),
|
||||
FIELD("provider_priority", 0),
|
||||
FIELD("provides", ADBI_PI_PROVIDES),
|
||||
FIELD("replaces", ADBI_PI_REPLACES),
|
||||
FIELD("replaces_priority", ADBI_PI_PRIORITY),
|
||||
FIELD("size", ADBI_PI_INSTALLED_SIZE),
|
||||
FIELD("triggers", 0),
|
||||
FIELD("url", ADBI_PI_URL),
|
||||
};
|
||||
struct field *f, key;
|
||||
struct adb_obj pkginfo, deps[3];
|
||||
apk_blob_t line, l, r, token = APK_BLOB_STR("\n"), bdep;
|
||||
int e = 0, i = 0;
|
||||
|
||||
adb_wo_alloca(&pkginfo, &schema_pkginfo, db);
|
||||
adb_wo_alloca(&deps[0], &schema_dependency_array, db);
|
||||
adb_wo_alloca(&deps[1], &schema_dependency_array, db);
|
||||
adb_wo_alloca(&deps[2], &schema_dependency_array, db);
|
||||
|
||||
while (!APK_BLOB_IS_NULL(line = apk_istream_get_delim(is, token))) {
|
||||
if (sctx) apk_sign_ctx_parse_pkginfo_line(sctx, line);
|
||||
if (line.len < 1 || line.ptr[0] == '#') continue;
|
||||
if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r)) continue;
|
||||
|
||||
key.str = l;
|
||||
f = bsearch(&key, fields, ARRAY_SIZE(fields), sizeof(fields[0]), cmpfield);
|
||||
if (!f || f->ndx == 0) continue;
|
||||
|
||||
if (adb_ro_val(&pkginfo, f->ndx) != ADB_NULL) {
|
||||
/* Workaround abuild bug that emitted multiple license lines */
|
||||
if (f->ndx == ADBI_PI_LICENSE) continue;
|
||||
e = ADB_ERROR(EAPKFORMAT);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (f->ndx) {
|
||||
case ADBI_PI_ARCH:
|
||||
if (!APK_BLOB_IS_NULL(rewrite_arch)) r = rewrite_arch;
|
||||
break;
|
||||
case ADBI_PI_DEPENDS:
|
||||
i = 0;
|
||||
goto parse_deps;
|
||||
case ADBI_PI_PROVIDES:
|
||||
i = 1;
|
||||
goto parse_deps;
|
||||
case ADBI_PI_REPLACES:
|
||||
i = 2;
|
||||
parse_deps:
|
||||
while (!ADB_IS_ERROR(e) && apk_dep_split(&r, &bdep))
|
||||
e = adb_wa_append_fromstring(&deps[i], bdep);
|
||||
continue;
|
||||
}
|
||||
adb_wo_pkginfo(&pkginfo, f->ndx, r);
|
||||
}
|
||||
if (ADB_IS_ERROR(e)) return e;
|
||||
|
||||
adb_wo_arr(&pkginfo, ADBI_PI_DEPENDS, &deps[0]);
|
||||
adb_wo_arr(&pkginfo, ADBI_PI_PROVIDES, &deps[1]);
|
||||
adb_wo_arr(&pkginfo, ADBI_PI_REPLACES, &deps[2]);
|
||||
adb_wo_int(&pkginfo, ADBI_PI_FILE_SIZE, file_size);
|
||||
|
||||
return adb_w_obj(&pkginfo);
|
||||
}
|
||||
|
||||
static int mkndx_parse_v2_tar(void *pctx, const struct apk_file_info *ae, struct apk_istream *is)
|
||||
{
|
||||
struct mkndx_ctx *ctx = pctx;
|
||||
adb_val_t o;
|
||||
int r;
|
||||
|
||||
r = apk_sign_ctx_process_file(&ctx->sctx, ae, is);
|
||||
if (r <= 0) return r;
|
||||
if (ctx->sctx.control_verified) return -ECANCELED;
|
||||
if (!ctx->sctx.control_started || ctx->sctx.data_started) return 0;
|
||||
|
||||
if (strcmp(ae->name, ".PKGINFO") == 0) {
|
||||
o = adb_wa_append(
|
||||
&ctx->pkgs,
|
||||
mkndx_read_v2_pkginfo(
|
||||
&ctx->db, is, ctx->file_size, &ctx->sctx,
|
||||
ctx->rewrite_arch));
|
||||
if (ADB_IS_ERROR(o)) return -ADB_VAL_VALUE(o);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mkndx_main(void *pctx, struct apk_database *db, struct apk_string_array *args)
|
||||
{
|
||||
struct adb odb, tmpdb;
|
||||
struct adb_obj oroot, opkgs, ndx, tmpl;
|
||||
struct apk_file_info fi;
|
||||
adb_val_t match;
|
||||
int r, found, errors = 0, newpkgs = 0, numpkgs;
|
||||
struct mkndx_ctx *ctx = pctx;
|
||||
char **parg;
|
||||
time_t index_mtime = 0;
|
||||
|
||||
if (ctx->output == NULL) {
|
||||
apk_error("Please specify --output FILE");
|
||||
return -1;
|
||||
}
|
||||
|
||||
adb_w_init_tmp(&tmpdb, 200);
|
||||
adb_wo_alloca(&tmpl, &schema_pkginfo, &tmpdb);
|
||||
|
||||
adb_w_init_alloca(&ctx->db, ADB_SCHEMA_INDEX, 1000);
|
||||
adb_wo_alloca(&ndx, &schema_index, &ctx->db);
|
||||
adb_wo_alloca(&ctx->pkgs, &schema_pkginfo_array, &ctx->db);
|
||||
|
||||
if (ctx->index) {
|
||||
apk_fileinfo_get(AT_FDCWD, ctx->index, APK_CHECKSUM_NONE, &fi, 0);
|
||||
index_mtime = fi.mtime;
|
||||
|
||||
r = adb_m_map(&odb, open(ctx->index, O_RDONLY), ADB_SCHEMA_INDEX, &db->trust);
|
||||
if (r) {
|
||||
apk_error("%s: %s", ctx->index, apk_error_str(r));
|
||||
return r;
|
||||
}
|
||||
adb_ro_obj(adb_r_rootobj(&odb, &oroot, &schema_index), ADBI_NDX_PACKAGES, &opkgs);
|
||||
}
|
||||
|
||||
foreach_array_item(parg, args) {
|
||||
r = apk_fileinfo_get(AT_FDCWD, *parg, APK_CHECKSUM_NONE, &fi, 0);
|
||||
if (r < 0) {
|
||||
err_pkg:
|
||||
apk_error("%s: %s", *parg, apk_error_str(r));
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
ctx->file_size = fi.size;
|
||||
|
||||
found = FALSE;
|
||||
if (index_mtime >= fi.mtime) {
|
||||
char *fname, *fend;
|
||||
apk_blob_t bname, bver;
|
||||
|
||||
/* Check that it looks like a package name */
|
||||
fname = strrchr(*parg, '/');
|
||||
if (fname == NULL)
|
||||
fname = *parg;
|
||||
else
|
||||
fname++;
|
||||
fend = strstr(fname, ".apk");
|
||||
if (!fend) goto do_file;
|
||||
if (fend-fname > 10 && fend[-9] == '.') fend -= 9;
|
||||
if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(fname, fend-1),
|
||||
&bname, &bver) < 0)
|
||||
goto do_file;
|
||||
|
||||
adb_wo_resetdb(&tmpl);
|
||||
adb_wo_blob(&tmpl, ADBI_PI_NAME, bname);
|
||||
adb_wo_blob(&tmpl, ADBI_PI_VERSION, bver);
|
||||
match = adb_w_obj(&tmpl);
|
||||
|
||||
for (int i = 0; (i = adb_ra_find(&opkgs, i, &tmpdb, match)) > 0; ) {
|
||||
struct adb_obj pkg;
|
||||
adb_val_t val;
|
||||
|
||||
adb_ro_obj(&opkgs, i, &pkg);
|
||||
if (apk_blob_compare(bname, adb_ro_blob(&pkg, ADBI_PI_NAME))) continue;
|
||||
if (apk_blob_compare(bver, adb_ro_blob(&pkg, ADBI_PI_VERSION))) continue;
|
||||
if (fi.size != adb_ro_int(&pkg, ADBI_PI_FILE_SIZE)) continue;
|
||||
|
||||
val = adb_wa_append(&ctx->pkgs, adb_w_copy(&ctx->db, &odb, adb_ro_val(&opkgs, i)));
|
||||
if (ADB_IS_ERROR(val))
|
||||
errors++;
|
||||
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
do_file:
|
||||
apk_sign_ctx_init(&ctx->sctx, APK_SIGN_VERIFY, NULL, db->keys_fd);
|
||||
r = apk_tar_parse(
|
||||
apk_istream_gunzip_mpart(apk_istream_from_file(AT_FDCWD, *parg), apk_sign_ctx_mpart_cb, &ctx->sctx),
|
||||
mkndx_parse_v2_tar, ctx, &db->id_cache);
|
||||
apk_sign_ctx_free(&ctx->sctx);
|
||||
if (r < 0 && r != -ECANCELED) goto err_pkg;
|
||||
newpkgs++;
|
||||
}
|
||||
}
|
||||
if (errors) {
|
||||
apk_error("%d errors, not creating index", errors);
|
||||
return -1;
|
||||
}
|
||||
|
||||
numpkgs = adb_ra_num(&ctx->pkgs);
|
||||
adb_wo_blob(&ndx, ADBI_NDX_DESCRIPTION, APK_BLOB_STR(ctx->description));
|
||||
adb_wo_obj(&ndx, ADBI_NDX_PACKAGES, &ctx->pkgs);
|
||||
adb_w_rootobj(&ndx);
|
||||
|
||||
r = adb_c_create(
|
||||
apk_ostream_to_file(AT_FDCWD, ctx->output, NULL, 0644),
|
||||
&ctx->db, &db->trust);
|
||||
|
||||
adb_free(&ctx->db);
|
||||
adb_free(&odb);
|
||||
|
||||
if (r == 0)
|
||||
apk_message("Index has %d packages (of which %d are new)",
|
||||
numpkgs, newpkgs);
|
||||
else
|
||||
apk_error("Index creation failed: %s", apk_error_str(r));
|
||||
|
||||
|
||||
#if 0
|
||||
apk_hash_foreach(&db->available.names, warn_if_no_providers, &counts);
|
||||
|
||||
if (counts.unsatisfied != 0)
|
||||
apk_warning("Total of %d unsatisfiable package "
|
||||
"names. Your repository may be broken.",
|
||||
counts.unsatisfied);
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct apk_applet apk_mkndx = {
|
||||
.name = "mkndx",
|
||||
.open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_NO_REPOS,
|
||||
.context_size = sizeof(struct mkndx_ctx),
|
||||
.optgroups = { &optgroup_global, &optgroup_signing, &optgroup_applet },
|
||||
.main = mkndx_main,
|
||||
};
|
||||
|
||||
APK_DEFINE_APPLET(apk_mkndx);
|
|
@ -256,6 +256,13 @@ int apk_blob_compare(apk_blob_t a, apk_blob_t b)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int apk_blob_sort(apk_blob_t a, apk_blob_t b)
|
||||
{
|
||||
int s = memcmp(a.ptr, b.ptr, min(a.len, b.len));
|
||||
if (s != 0) return s;
|
||||
return a.len - b.len;
|
||||
}
|
||||
|
||||
int apk_blob_ends_with(apk_blob_t a, apk_blob_t b)
|
||||
{
|
||||
if (a.len < b.len) return 0;
|
||||
|
|
|
@ -1676,6 +1676,12 @@ int apk_db_open(struct apk_database *db, struct apk_db_options *dbopts)
|
|||
dbopts->keys_dir ?: "etc/apk/keys",
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
|
||||
r = adb_trust_init(&db->trust, dup(db->keys_fd), dbopts->private_keys);
|
||||
if (r) {
|
||||
msg = "Unable to read trusted keys";
|
||||
goto ret_r;
|
||||
}
|
||||
|
||||
if (apk_flags & APK_OVERLAY_FROM_STDIN) {
|
||||
apk_flags &= ~APK_OVERLAY_FROM_STDIN;
|
||||
apk_db_read_overlay(db, apk_istream_from_fd(STDIN_FILENO));
|
||||
|
@ -1852,6 +1858,8 @@ void apk_db_close(struct apk_database *db)
|
|||
db->cache_remount_dir = NULL;
|
||||
}
|
||||
|
||||
adb_trust_free(&db->trust);
|
||||
|
||||
if (db->keys_fd)
|
||||
close(db->keys_fd);
|
||||
if (db->cache_fd)
|
||||
|
|
9
src/io.c
9
src/io.c
|
@ -827,7 +827,7 @@ struct apk_istream *apk_istream_from_file_gz(int atfd, const char *file)
|
|||
|
||||
struct apk_fd_ostream {
|
||||
struct apk_ostream os;
|
||||
int fd, rc;
|
||||
int fd;
|
||||
|
||||
const char *file, *tmpfile;
|
||||
int atfd;
|
||||
|
@ -860,7 +860,7 @@ static ssize_t fdo_flush(struct apk_fd_ostream *fos)
|
|||
return 0;
|
||||
|
||||
if ((r = safe_write(fos->fd, fos->buffer, fos->bytes)) != fos->bytes) {
|
||||
fos->rc = r < 0 ? r : -EIO;
|
||||
apk_ostream_cancel(&fos->os, r < 0 ? r : -EIO);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -879,8 +879,7 @@ static ssize_t fdo_write(struct apk_ostream *os, const void *ptr, size_t size)
|
|||
return r;
|
||||
if (size >= sizeof(fos->buffer) / 2) {
|
||||
r = safe_write(fos->fd, ptr, size);
|
||||
if (r != size)
|
||||
fos->rc = r < 0 ? r : -EIO;
|
||||
if (r != size) apk_ostream_cancel(&fos->os, r < 0 ? r : -EIO);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
@ -897,7 +896,7 @@ static int fdo_close(struct apk_ostream *os)
|
|||
int rc;
|
||||
|
||||
fdo_flush(fos);
|
||||
rc = fos->rc;
|
||||
rc = fos->os.rc;
|
||||
|
||||
if (fos->fd > STDERR_FILENO &&
|
||||
close(fos->fd) < 0)
|
||||
|
|
|
@ -28,17 +28,17 @@
|
|||
#include "apk_database.h"
|
||||
#include "apk_print.h"
|
||||
|
||||
static const apk_spn_match_def apk_spn_dependency_comparer = {
|
||||
const apk_spn_match_def apk_spn_dependency_comparer = {
|
||||
[7] = (1<<4) /*<*/ | (1<<5) /*=*/ | (1<<6) /*<*/,
|
||||
[15] = (1<<6) /*~*/
|
||||
};
|
||||
|
||||
static const apk_spn_match_def apk_spn_dependency_separator = {
|
||||
const apk_spn_match_def apk_spn_dependency_separator = {
|
||||
[1] = (1<<2) /*\n*/,
|
||||
[4] = (1<<0) /* */,
|
||||
};
|
||||
|
||||
static const apk_spn_match_def apk_spn_repotag_separator = {
|
||||
const apk_spn_match_def apk_spn_repotag_separator = {
|
||||
[8] = (1<<0) /*@*/
|
||||
};
|
||||
|
||||
|
|
|
@ -179,6 +179,10 @@ const char *apk_error_str(int error)
|
|||
return "invalid URL (check your repositories file)";
|
||||
case EAPKSTALEINDEX:
|
||||
return "package mentioned in index not found (try 'apk update')";
|
||||
case EAPKFORMAT:
|
||||
return "package file format error";
|
||||
case EAPKDEPFORMAT:
|
||||
return "package dependency format error";
|
||||
default:
|
||||
return strerror(error);
|
||||
}
|
||||
|
|
|
@ -154,6 +154,8 @@ const char *apk_version_op_string(int mask)
|
|||
return ">";
|
||||
case APK_DEPMASK_CHECKSUM:
|
||||
return "><";
|
||||
case APK_DEPMASK_ANY:
|
||||
return "";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue