#include #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(APKE_ADB_PACKAGE_FORMAT); /* 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_blob(obj->db, APK_BLOB_CSUM(csum)); 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 int string_compare(struct adb *db1, adb_val_t v1, struct adb *db2, adb_val_t v2) { return apk_blob_sort(adb_r_blob(db1, v1), adb_r_blob(db2, v2)); } static struct adb_scalar_schema scalar_string = { .kind = ADB_KIND_BLOB, .tostring = string_tostring, .fromstring = string_fromstring, .compare = string_compare, }; static struct adb_scalar_schema scalar_mstring = { .kind = ADB_KIND_BLOB, .multiline = 1, .tostring = string_tostring, .fromstring = string_fromstring, .compare = string_compare, }; 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 adb_val_t version_fromstring(struct adb *db, apk_blob_t val) { if (!apk_version_validate(val)) return ADB_ERROR(APKE_PKGVERSION_FORMAT); return adb_w_blob(db, val); } static int version_compare(struct adb *db1, adb_val_t v1, struct adb *db2, adb_val_t v2) { switch (apk_version_compare_blob(adb_r_blob(db1, v1), adb_r_blob(db2, v2))) { case APK_VERSION_LESS: return -1; case APK_VERSION_GREATER: return 1; default: return 0; } } static struct adb_scalar_schema scalar_version = { .kind = ADB_KIND_BLOB, .tostring = string_tostring, .fromstring = version_fromstring, .compare = version_compare, }; 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 adb_val_t hexblob_fromstring(struct adb *db, apk_blob_t val) { char buf[256]; if (val.len & 1) return ADB_ERROR(EINVAL); if (val.len > sizeof buf) return ADB_ERROR(E2BIG); apk_blob_t b = APK_BLOB_PTR_LEN(buf, val.len / 2); apk_blob_pull_hexdump(&val, b); if (APK_BLOB_IS_NULL(val)) return ADB_ERROR(EINVAL); return adb_w_blob(db, b); } static struct adb_scalar_schema scalar_hexblob = { .kind = ADB_KIND_BLOB, .tostring = hexblob_tostring, .fromstring = hexblob_fromstring, .compare = string_compare, }; 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) ?: ADB_VAL_NULL; } static int int_compare(struct adb *db1, adb_val_t v1, struct adb *db2, adb_val_t v2) { uint32_t r1 = adb_r_int(db1, v1); uint32_t r2 = adb_r_int(db1, v2); if (r1 < r2) return -1; if (r1 > r2) return 1; return 0; } static struct adb_scalar_schema scalar_int = { .kind = ADB_KIND_INT, .tostring = int_tostring, .fromstring = int_fromstring, .compare = int_compare, }; 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 adb_val_t oct_fromstring(struct adb *db, apk_blob_t val) { uint32_t n = apk_blob_pull_uint(&val, 8); if (val.len) return ADB_ERROR(EINVAL); return adb_w_int(db, n) ?: ADB_VAL_NULL; } static struct adb_scalar_schema scalar_oct = { .kind = ADB_KIND_INT, .tostring = oct_tostring, .fromstring = oct_fromstring, .compare = int_compare, }; 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 adb_val_t hsize_fromstring(struct adb *db, apk_blob_t val) { apk_blob_t l, r; if (!apk_blob_split(val, APK_BLOB_STR(" "), &l, &r)) return int_fromstring(db, val); uint64_t n = apk_blob_pull_uint(&l, 10); int sz = apk_get_human_size_unit(r); n *= sz; return adb_w_int(db, n); } static struct adb_scalar_schema scalar_hsize = { .kind = ADB_KIND_INT, .tostring = hsize_tostring, .fromstring = hsize_fromstring, .compare = int_compare, }; 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 -APKE_DEPENDENCY_FORMAT; } static int dependency_cmp(const struct adb_obj *o1, const struct adb_obj *o2) { return adb_ro_cmp(o1, 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_version), 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)) { int r = adb_wo_fromstring(&dep, bdep); if (r) return r; 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(const struct adb_obj *o1, const struct adb_obj *o2) { int r; r = adb_ro_cmp(o1, o2, ADBI_PI_NAME); if (r) return r; r = adb_ro_cmp(o1, o2, ADBI_PI_VERSION); if (r) return r; return adb_ro_cmp(o1, o2, ADBI_PI_UNIQUE_ID); } 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_version), ADB_FIELD(ADBI_PI_UNIQUE_ID, "unique-id", scalar_hexblob), 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), }, }; const struct adb_object_schema schema_acl = { .kind = ADB_KIND_OBJECT, .num_fields = ADBI_ACL_MAX, .fields = { ADB_FIELD(ADBI_ACL_MODE, "mode", scalar_oct), ADB_FIELD(ADBI_ACL_USER, "user", scalar_string), ADB_FIELD(ADBI_ACL_GROUP, "group", scalar_string), //ADB_FIELD(ADBI_ACL_XATTRS, "xattr", schema_string_array), }, }; static int file_cmp(const struct adb_obj *o1, const struct adb_obj *o2) { return adb_ro_cmp(o1, o2, ADBI_FI_NAME); } const struct adb_object_schema schema_file = { .kind = ADB_KIND_OBJECT, .num_fields = ADBI_FI_MAX, .compare = file_cmp, .fields = { ADB_FIELD(ADBI_FI_NAME, "name", scalar_string), ADB_FIELD(ADBI_FI_ACL, "acl", schema_acl), ADB_FIELD(ADBI_FI_SIZE, "size", scalar_int), ADB_FIELD(ADBI_FI_MTIME, "mtime", scalar_int), ADB_FIELD(ADBI_FI_HASHES, "hash", scalar_hexblob), ADB_FIELD(ADBI_FI_TARGET, "target", 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), }; const struct adb_object_schema schema_dir = { .kind = ADB_KIND_OBJECT, .num_fields = ADBI_DI_MAX, .compare = file_cmp, .fields = { ADB_FIELD(ADBI_DI_NAME, "name", scalar_string), ADB_FIELD(ADBI_DI_ACL, "acl", schema_acl), ADB_FIELD(ADBI_DI_FILES, "files", schema_file_array), }, }; const struct adb_object_schema schema_dir_array = { .kind = ADB_KIND_ARRAY, .pre_commit = adb_wa_sort, .num_fields = APK_MAX_MANIFEST_PATHS, .fields = ADB_ARRAY_ITEM(schema_dir), }; const struct adb_object_schema schema_scripts = { .kind = ADB_KIND_OBJECT, .num_fields = ADBI_SCRPT_MAX, .fields = { ADB_FIELD(ADBI_SCRPT_TRIGGER, "trigger", scalar_mstring), ADB_FIELD(ADBI_SCRPT_PREINST, "pre-install", scalar_mstring), ADB_FIELD(ADBI_SCRPT_POSTINST, "post-install", scalar_mstring), ADB_FIELD(ADBI_SCRPT_PREDEINST, "pre-deinstall",scalar_mstring), ADB_FIELD(ADBI_SCRPT_POSTDEINST,"post-deinstall",scalar_mstring), ADB_FIELD(ADBI_SCRPT_PREUPGRADE,"pre-upgrade", scalar_mstring), ADB_FIELD(ADBI_SCRPT_POSTUPGRADE,"post-upgrade",scalar_mstring), }, }; static int package_cmp(const struct adb_obj *o1, const struct adb_obj *o2) { return adb_ro_cmp(o1, o2, ADBI_PKG_PKGINFO); } const struct adb_object_schema schema_package = { .kind = ADB_KIND_OBJECT, .num_fields = ADBI_PKG_MAX, .compare = package_cmp, .fields = { ADB_FIELD(ADBI_PKG_PKGINFO, "info", schema_pkginfo), ADB_FIELD(ADBI_PKG_PATHS, "paths", schema_dir_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, .schema = &schema_package, }; const struct adb_object_schema schema_package_adb_array = { .kind = ADB_KIND_ARRAY, .pre_commit = adb_wa_sort, .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), }, };