diff options
-rw-r--r-- | src/adb.c | 90 | ||||
-rw-r--r-- | src/adb.h | 22 | ||||
-rw-r--r-- | src/adb_walk_adb.c | 29 | ||||
-rw-r--r-- | src/adb_walk_genadb.c | 2 | ||||
-rw-r--r-- | src/apk_adb.c | 62 | ||||
-rw-r--r-- | src/apk_adb.h | 26 | ||||
-rw-r--r-- | src/apk_defines.h | 8 | ||||
-rw-r--r-- | src/apk_io.h | 18 | ||||
-rw-r--r-- | src/apk_pathbuilder.h | 44 | ||||
-rw-r--r-- | src/app_adbdump.c | 2 | ||||
-rw-r--r-- | src/app_adbsign.c | 2 | ||||
-rw-r--r-- | src/app_convdb.c | 30 | ||||
-rw-r--r-- | src/app_mkpkg.c | 284 | ||||
-rw-r--r-- | src/io.c | 202 | ||||
-rw-r--r-- | src/io_archive.c | 4 | ||||
-rw-r--r-- | src/meson.build | 3 | ||||
-rw-r--r-- | src/pathbuilder.c | 29 |
17 files changed, 647 insertions, 210 deletions
@@ -13,13 +13,16 @@ #include "apk_blob.h" #include "apk_trust.h" +static char padding_zeroes[ADB_BLOCK_ALIGNMENT] = {0}; + /* 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); + if (sizeof(struct adb_block) > b.len - pos) return ERR_PTR(-EBADMSG); + if (adb_block_rawsize(blk) < sizeof(struct adb_block)) return ERR_PTR(-EBADMSG); + if (adb_block_size(blk) > b.len - pos) return ERR_PTR(-EBADMSG); return blk; } @@ -30,7 +33,7 @@ struct adb_block *adb_block_first(apk_blob_t 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); + return adb_block_validate((struct adb_block*)((char*)cur + adb_block_size(cur)), b); } /* Init stuff */ @@ -71,8 +74,8 @@ static int __adb_m_parse(struct adb *db, struct apk_trust *t) int trusted = t ? 0 : 1; 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)) { + apk_blob_t b = adb_block_blob(blk); + switch (adb_block_type(blk)) { case ADB_BLOCK_ADB: if (!APK_BLOB_IS_NULL(db->adb)) break; db->adb = b; @@ -575,13 +578,12 @@ copy: 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 adb_block blk = adb_block_init(ADB_BLOCK_ADB, 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 }, + { .iov_base = padding_zeroes, .iov_len = adb_block_padding(&blk) }, }; if (valdb->adb.len <= 4) return ADB_NULL; bsz = htole32(iovec_len(vec, ARRAY_SIZE(vec)) - sizeof bsz); @@ -817,9 +819,8 @@ 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 val) { - struct adb_block blk = { - .type_size = htole32((type << 30) + val.len) - }; + struct adb_block blk = adb_block_init(type, val.len); + size_t padding = adb_block_padding(&blk); int r; r = apk_ostream_write(os, &blk, sizeof blk); @@ -827,6 +828,39 @@ int adb_c_block(struct apk_ostream *os, uint32_t type, apk_blob_t val) r = apk_ostream_write(os, val.ptr, val.len); if (r < 0) return r; + + if (padding) { + r = apk_ostream_write(os, padding_zeroes, padding); + if (r < 0) return r; + } + + return 0; +} + +int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint32_t size, struct apk_istream *is) +{ + struct adb_block blk = adb_block_init(ADB_BLOCK_DATA, size + hdr.len); + size_t padding = adb_block_padding(&blk); + int r; + + if (IS_ERR(os)) return PTR_ERR(os); + if (IS_ERR(is)) return apk_ostream_cancel(os, PTR_ERR(is)); + + r = apk_ostream_write(os, &blk, sizeof blk); + if (r < 0) return r; + + r = apk_ostream_write(os, hdr.ptr, hdr.len); + if (r < 0) return r; + + r = apk_stream_copy(is, os, size, 0, 0, 0); + if (r < 0) return r; + + if (padding) { + r = apk_ostream_write(os, padding_zeroes, padding); + if (r < 0) return r; + } + + apk_istream_close(is); return 0; } @@ -842,7 +876,7 @@ int adb_c_block_copy(struct apk_ostream *os, struct adb_block *b, struct apk_ist 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); + 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); @@ -851,17 +885,23 @@ int adb_c_block_copy(struct apk_ostream *os, struct adb_block *b, struct apk_ist return r; } -int adb_c_create(struct apk_ostream *os, struct adb *db, struct apk_trust *t) +int adb_c_adb(struct apk_ostream *os, struct adb *db, struct apk_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; - } + if (IS_ERR(os)) + return apk_ostream_cancel(os, PTR_ERR(os)); + if (db->hdr.magic != htole32(ADB_FORMAT_MAGIC)) + return apk_ostream_cancel(os, -EAPKFORMAT); + 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_error(os); +} + +int adb_c_create(struct apk_ostream *os, struct adb *db, struct apk_trust *t) +{ + adb_c_adb(os, db, t); return apk_ostream_close(os); } @@ -922,8 +962,7 @@ int adb_trust_write_signatures(struct apk_trust *trust, struct adb *db, struct a 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, &sig.v0, sizeof sig.v0) != 1 || EVP_DigestUpdate(trust->mdctx, md.ptr, md.len) != 1 || EVP_DigestSignFinal(trust->mdctx, sig.v0.sig, &siglen) != 1) { ERR_print_errors_fp(stdout); @@ -962,8 +1001,7 @@ int adb_trust_verify_signature(struct apk_trust *trust, struct adb *db, struct a 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, sig0, sizeof *sig0) != 1 || EVP_DigestUpdate(trust->mdctx, md.ptr, md.len) != 1 || EVP_DigestVerifyFinal(trust->mdctx, sig0->sig, sigb.len - sizeof(*sig0)) != 1) { ERR_clear_error(); @@ -998,16 +1036,16 @@ int adb_c_xfrm(struct adb_xfrm *x, int (*cb)(struct adb_xfrm *, struct adb_block goto err; } - if ((block_no++ == 0) != (ADB_BLOCK_TYPE(&blk) == ADB_BLOCK_ADB)) goto bad_msg; + if ((block_no++ == 0) != (adb_block_type(&blk) == ADB_BLOCK_ADB)) goto bad_msg; - sz = ADB_BLOCK_SIZE(&blk); + sz = adb_block_size(&blk) - sizeof 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); + r = apk_stream_copy(x->is, x->os, sz, 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); @@ -51,17 +51,29 @@ struct adb_header { }; /* Blocks */ +#define ADB_BLOCK_ALIGNMENT 8 #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) +#define ADB_BLOCK_DATA 3 struct adb_block { uint32_t type_size; }; +static inline struct adb_block adb_block_init(uint32_t type, uint32_t length) { + return (struct adb_block) { .type_size = htole32((type << 30) + sizeof(struct adb_block) + length)}; +} +static inline uint32_t adb_block_type(struct adb_block *b) { return le32toh((b)->type_size) >> 30; } +static inline uint32_t adb_block_rawsize(struct adb_block *b) { return le32toh((b)->type_size) & 0x3fffffff; } +static inline uint32_t adb_block_size(struct adb_block *b) { return ROUND_UP(adb_block_rawsize(b), ADB_BLOCK_ALIGNMENT); } +static inline uint32_t adb_block_length(struct adb_block *b) { return adb_block_rawsize(b) - sizeof(struct adb_block); } +static inline uint32_t adb_block_padding(struct adb_block *b) { return adb_block_size(b) - adb_block_rawsize(b); } +static inline void *adb_block_payload(struct adb_block *b) { return b + 1; } +static inline apk_blob_t adb_block_blob(struct adb_block *b) { + return APK_BLOB_PTR_LEN(adb_block_payload(b), adb_block_length(b)); +} + struct adb_sign_hdr { uint8_t sign_ver, hash_alg; }; @@ -81,7 +93,6 @@ struct adb_sign_v0 { /* 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)) @@ -218,7 +229,9 @@ 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_data(struct apk_ostream *os, apk_blob_t hdr, uint32_t size, struct apk_istream *is); int adb_c_block_copy(struct apk_ostream *os, struct adb_block *b, struct apk_istream *is, struct adb_verify_ctx *); +int adb_c_adb(struct apk_ostream *os, struct adb *db, struct apk_trust *t); int adb_c_create(struct apk_ostream *os, struct adb *db, struct apk_trust *t); /* Trust */ @@ -279,6 +292,7 @@ struct adb_walk_gentext { struct adb_walk_genadb { struct adb_walk d; struct adb db; + adb_val_t stored_object; struct adb idb[ADB_WALK_GENADB_MAX_IDB]; int nest, nestdb, num_vals; struct adb_obj objs[ADB_WALK_GENADB_MAX_NESTING]; diff --git a/src/adb_walk_adb.c b/src/adb_walk_adb.c index 4f99d85..b9f051c 100644 --- a/src/adb_walk_adb.c +++ b/src/adb_walk_adb.c @@ -107,11 +107,10 @@ static int dump_object(struct adb_walk_ctx *ctx, const struct adb_object_schema static int dump_adb(struct adb_walk_ctx *ctx) { - char tmp[256]; + char tmp[512]; struct adb_block *blk; struct adb_sign_hdr *s; struct adb_verify_ctx vfy = {}; - unsigned char *id; uint32_t schema_magic = ctx->db->hdr.schema; const struct adb_db_schema *ds; struct adb_walk *d = ctx->d; @@ -121,10 +120,10 @@ static int dump_adb(struct adb_walk_ctx *ctx) if (ds->magic == schema_magic) break; adb_foreach_block(blk, ctx->db->data) { - apk_blob_t b = APK_BLOB_PTR_LEN((char*)(blk+1), ADB_BLOCK_SIZE(blk)); - switch (ADB_BLOCK_TYPE(blk)) { + apk_blob_t b = adb_block_blob(blk); + switch (adb_block_type(blk)) { case ADB_BLOCK_ADB: - len = snprintf(tmp, sizeof tmp, "ADB block, size: %u", ADB_BLOCK_SIZE(blk)); + len = snprintf(tmp, sizeof tmp, "ADB block, size: %u", adb_block_length(blk)); d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len)); if (ds->root) { ctx->db->adb = b; @@ -134,23 +133,19 @@ static int dump_adb(struct adb_walk_ctx *ctx) case ADB_BLOCK_SIG: s = (struct adb_sign_hdr*) b.ptr; r = adb_trust_verify_signature(ctx->trust, ctx->db, &vfy, b); - - len = snprintf(tmp, sizeof tmp, "signature: v%d ", s->sign_ver); - switch (s->sign_ver) { - case 0: - id = (unsigned char*)(s + 1); - for (size_t j = 0; j < 16; j++) - len += snprintf(&tmp[len], sizeof tmp - len, "%02x", id[j]); - break; - default: - break; - } + len = snprintf(tmp, sizeof tmp, "sig v%02x h%02x ", s->sign_ver, s->hash_alg); + for (size_t j = sizeof *s; j < b.len; j++) + len += snprintf(&tmp[len], sizeof tmp - len, "%02x", (uint8_t)b.ptr[j]); len += snprintf(&tmp[len], sizeof tmp - len, ": %s", r ? apk_error_str(r) : "OK"); d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len)); break; + case ADB_BLOCK_DATA: + len = snprintf(tmp, sizeof tmp, "data block, size: %d", adb_block_length(blk)); + d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len)); + break; default: len = snprintf(tmp, sizeof tmp, "unknown block %d, size: %d", - ADB_BLOCK_TYPE(blk), ADB_BLOCK_SIZE(blk)); + adb_block_type(blk), adb_block_length(blk)); d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len)); } } diff --git a/src/adb_walk_genadb.c b/src/adb_walk_genadb.c index 06a3f94..4852eb6 100644 --- a/src/adb_walk_genadb.c +++ b/src/adb_walk_genadb.c @@ -74,7 +74,7 @@ static int adb_walk_genadb_end(struct adb_walk *d) dt->num_vals -= dt->objs[dt->nest].schema->num_fields; if (dt->nest == 0) { - adb_w_root(&dt->db, val); + dt->stored_object = val; return 0; } diff --git a/src/apk_adb.c b/src/apk_adb.c index 0785455..9be0e57 100644 --- a/src/apk_adb.c +++ b/src/apk_adb.c @@ -422,17 +422,16 @@ const struct adb_object_schema schema_index = { }, }; -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; -} +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) { @@ -442,15 +441,14 @@ static int file_cmp(const struct adb_obj *o1, const struct adb_obj *o2) 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_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_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), + ADB_FIELD(ADBI_FI_TARGET, "target", scalar_string), }, }; @@ -461,38 +459,22 @@ const struct adb_object_schema schema_file_array = { .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 = { +const struct adb_object_schema schema_dir = { .kind = ADB_KIND_OBJECT, - .num_fields = ADBI_FI_MAX, - .get_default_int = path_get_default_int, + .num_fields = ADBI_DI_MAX, .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), + 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_path_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_path), + .fields = ADB_ARRAY_ITEM(schema_dir), }; const struct adb_object_schema schema_scripts = { @@ -520,7 +502,7 @@ const struct adb_object_schema schema_package = { .compare = package_cmp, .fields = { ADB_FIELD(ADBI_PKG_PKGINFO, "info", schema_pkginfo), - ADB_FIELD(ADBI_PKG_PATHS, "paths", schema_path_array), + 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), diff --git a/src/apk_adb.h b/src/apk_adb.h index 557bc6e..1908cba 100644 --- a/src/apk_adb.h +++ b/src/apk_adb.h @@ -33,16 +33,28 @@ #define ADBI_PI_RECOMMENDS 0x13 #define ADBI_PI_MAX 0x14 +/* ACL entries */ +#define ADBI_ACL_MODE 0x01 +#define ADBI_ACL_USER 0x02 +#define ADBI_ACL_GROUP 0x03 +#define ADBI_ACL_XATTRS 0x04 +#define ADBI_ACL_MAX 0x05 + /* 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_ACL 0x02 +#define ADBI_FI_SIZE 0x03 +#define ADBI_FI_MTIME 0x04 +#define ADBI_FI_HASHES 0x05 +#define ADBI_FI_TARGET 0x06 #define ADBI_FI_MAX 0x07 +/* Directory Info */ +#define ADBI_DI_NAME 0x01 +#define ADBI_DI_ACL 0x02 +#define ADBI_DI_FILES 0x03 +#define ADBI_DI_MAX 0x04 + /* Scripts */ #define ADBI_SCRPT_TRIGGER 0x01 #define ADBI_SCRPT_PREINST 0x02 @@ -81,7 +93,7 @@ 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_acl, schema_file, schema_file_array, schema_dir, schema_dir_array, schema_string_array, schema_scripts, schema_package, schema_package_adb_array, schema_index, schema_idb; diff --git a/src/apk_defines.h b/src/apk_defines.h index 92e97d8..3218277 100644 --- a/src/apk_defines.h +++ b/src/apk_defines.h @@ -191,14 +191,14 @@ APK_ARRAY(apk_string_array, char *); #define LIST_POISON1 (void *) 0xdeadbeef #define LIST_POISON2 (void *) 0xabbaabba -struct hlist_head { - struct hlist_node *first; -}; - struct hlist_node { struct hlist_node *next; }; +struct hlist_head { + struct hlist_node *first; +}; + static inline int hlist_empty(const struct hlist_head *h) { return !h->first; diff --git a/src/apk_io.h b/src/apk_io.h index 8a243ac..030255e 100644 --- a/src/apk_io.h +++ b/src/apk_io.h @@ -15,14 +15,17 @@ #include "apk_defines.h" #include "apk_blob.h" -#include "apk_hash.h" #include "apk_atom.h" +struct apk_id_hash { + int empty; + struct hlist_head by_id[16], by_name[16]; +}; + struct apk_id_cache { int root_fd; - unsigned int genid; - struct apk_hash uid_cache; - struct apk_hash gid_cache; + struct apk_id_hash uid_cache; + struct apk_id_hash gid_cache; }; struct apk_xattr { @@ -148,6 +151,7 @@ struct apk_ostream *apk_ostream_to_fd(int fd); struct apk_ostream *apk_ostream_to_file(int atfd, const char *file, 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 int apk_ostream_error(struct apk_ostream *os) { return os->rc; } static inline int apk_ostream_cancel(struct apk_ostream *os, int rc) { if (!os->rc) os->rc = rc; return rc; } static inline ssize_t apk_ostream_write(struct apk_ostream *os, const void *buf, size_t size) { @@ -181,7 +185,9 @@ const char *apk_url_local_file(const char *url); void apk_id_cache_init(struct apk_id_cache *idc, int root_fd); void apk_id_cache_free(struct apk_id_cache *idc); void apk_id_cache_reset(struct apk_id_cache *idc); -uid_t apk_resolve_uid(struct apk_id_cache *idc, apk_blob_t username, uid_t default_uid); -uid_t apk_resolve_gid(struct apk_id_cache *idc, apk_blob_t groupname, uid_t default_gid); +uid_t apk_id_cache_resolve_uid(struct apk_id_cache *idc, apk_blob_t username, uid_t default_uid); +gid_t apk_id_cache_resolve_gid(struct apk_id_cache *idc, apk_blob_t groupname, gid_t default_gid); +apk_blob_t apk_id_cache_resolve_user(struct apk_id_cache *idc, uid_t uid); +apk_blob_t apk_id_cache_resolve_group(struct apk_id_cache *idc, gid_t gid); #endif diff --git a/src/apk_pathbuilder.h b/src/apk_pathbuilder.h new file mode 100644 index 0000000..34181db --- /dev/null +++ b/src/apk_pathbuilder.h @@ -0,0 +1,44 @@ +/* apk_pathbuilder.h - Alpine Package Keeper (APK) + * + * Copyright (C) 2021 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef APK_PATHBUILDER_H +#define APK_PATHBUILDER_H + +#include "apk_blob.h" + +struct apk_pathbuilder { + uint16_t namelen; + char name[PATH_MAX]; +}; + +int apk_pathbuilder_pushb(struct apk_pathbuilder *pb, apk_blob_t b); +void apk_pathbuilder_pop(struct apk_pathbuilder *pb); + + +static inline int apk_pathbuilder_setb(struct apk_pathbuilder *pb, apk_blob_t b) +{ + pb->namelen = 0; + return apk_pathbuilder_pushb(pb, b); +} + +static inline int apk_pathbuilder_push(struct apk_pathbuilder *pb, const char *name) +{ + return apk_pathbuilder_pushb(pb, APK_BLOB_STR(name)); +} + +static inline const char *apk_pathbuilder_cstr(const struct apk_pathbuilder *pb) +{ + return pb->name; +} + +static inline apk_blob_t apk_pathbuilder_get(const struct apk_pathbuilder *pb) +{ + return APK_BLOB_PTR_LEN((void*)pb->name, pb->namelen); +} + +#endif diff --git a/src/app_adbdump.c b/src/app_adbdump.c index 958c90f..5c1b69c 100644 --- a/src/app_adbdump.c +++ b/src/app_adbdump.c @@ -68,8 +68,10 @@ static int adbgen_main(void *pctx, struct apk_ctx *ac, struct apk_string_array * adb_w_init_alloca(&genadb.idb[0], 0, 100); foreach_array_item(arg, args) { adb_reset(&genadb.db); + adb_reset(&genadb.idb[0]); r = adb_walk_istream(&genadb.d, apk_istream_from_file(AT_FDCWD, *arg)); if (!r) { + adb_w_root(&genadb.db, genadb.stored_object); r = adb_c_create(apk_ostream_to_fd(STDOUT_FILENO), &genadb.db, apk_ctx_get_trust(ac)); } diff --git a/src/app_adbsign.c b/src/app_adbsign.c index cdfd6e9..bb489c0 100644 --- a/src/app_adbsign.c +++ b/src/app_adbsign.c @@ -44,7 +44,7 @@ static int update_signatures(struct adb_xfrm *xfrm, struct adb_block *blk, struc struct apk_trust *trust = apk_ctx_get_trust(ctx->ac); int r; - switch (blk ? ADB_BLOCK_TYPE(blk) : -1) { + 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: diff --git a/src/app_convdb.c b/src/app_convdb.c index 4871f67..dfb426b 100644 --- a/src/app_convdb.c +++ b/src/app_convdb.c @@ -18,6 +18,7 @@ struct conv_script { }; struct conv_ctx { + struct apk_ctx *ac; struct apk_atom_pool atoms; struct adb_obj pkgs; @@ -100,8 +101,9 @@ static int read_triggers(struct conv_ctx *ctx, struct apk_istream *is) static void convert_idb(struct conv_ctx *ctx, struct apk_istream *is) { + struct apk_id_cache *idc = apk_ctx_get_id_cache(ctx->ac); struct apk_checksum csum; - struct adb_obj pkg, pkginfo, files, file, paths, path, scripts, triggers; + struct adb_obj pkg, pkginfo, files, file, paths, path, scripts, triggers, acl; apk_blob_t l, val, spc = APK_BLOB_STR(" "), nl = APK_BLOB_STR("\n"); struct conv_script *s; int i; @@ -111,14 +113,15 @@ static void convert_idb(struct conv_ctx *ctx, struct apk_istream *is) 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(&paths, &schema_dir_array, &ctx->dbp); + adb_wo_alloca(&path, &schema_dir, &ctx->dbp); adb_wo_alloca(&pkg, &schema_package, &ctx->dbp); + adb_wo_alloca(&acl, &schema_acl, &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_wo_obj(&path, ADBI_DI_FILES, &files); adb_wa_append_obj(&paths, &path); adb_wo_obj(&pkg, ADBI_PKG_PKGINFO, &pkginfo); @@ -152,28 +155,30 @@ static void convert_idb(struct conv_ctx *ctx, struct apk_istream *is) break; case 'F': // directory name adb_wa_append_obj(&files, &file); - adb_wo_obj(&path, ADBI_FI_FILES, &files); + adb_wo_obj(&path, ADBI_DI_FILES, &files); adb_wa_append_obj(&paths, &path); - adb_wo_blob(&path, ADBI_FI_NAME, val); + adb_wo_blob(&path, ADBI_DI_NAME, val); break; case 'M': // directory mode: uid:gid:mode:xattrcsum - adb_wo_int(&path, ADBI_FI_UID, apk_blob_pull_uint(&val, 10)); + adb_wo_blob(&acl, ADBI_ACL_USER, apk_id_cache_resolve_user(idc, apk_blob_pull_uint(&val, 10))); apk_blob_pull_char(&val, ':'); - adb_wo_int(&path, ADBI_FI_GID, apk_blob_pull_uint(&val, 10)); + adb_wo_blob(&acl, ADBI_ACL_GROUP, apk_id_cache_resolve_group(idc, apk_blob_pull_uint(&val, 10))); apk_blob_pull_char(&val, ':'); - adb_wo_int(&path, ADBI_FI_MODE, apk_blob_pull_uint(&val, 8)); + adb_wo_int(&acl, ADBI_ACL_MODE, apk_blob_pull_uint(&val, 8)); + adb_wo_obj(&path, ADBI_DI_ACL, &acl); 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)); + adb_wo_blob(&acl, ADBI_ACL_USER, apk_id_cache_resolve_user(idc, apk_blob_pull_uint(&val, 10))); apk_blob_pull_char(&val, ':'); - adb_wo_int(&file, ADBI_FI_GID, apk_blob_pull_uint(&val, 10)); + adb_wo_blob(&acl, ADBI_ACL_GROUP, apk_id_cache_resolve_group(idc, apk_blob_pull_uint(&val, 10))); apk_blob_pull_char(&val, ':'); - adb_wo_int(&file, ADBI_FI_MODE, apk_blob_pull_uint(&val, 8)); + adb_wo_int(&acl, ADBI_ACL_MODE, apk_blob_pull_uint(&val, 8)); + adb_wo_obj(&file, ADBI_FI_ACL, &acl); break; case 'Z': // file content hash apk_blob_pull_csum(&val, &csum); @@ -196,6 +201,7 @@ static int conv_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *ar int r; int root_fd = apk_ctx_fd_root(ac); + ctx->ac = ac; list_init(&ctx->script_head); apk_atom_init(&ctx->atoms); diff --git a/src/app_mkpkg.c b/src/app_mkpkg.c new file mode 100644 index 0000000..872b4da --- /dev/null +++ b/src/app_mkpkg.c @@ -0,0 +1,284 @@ +/* app_mkpkg.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2008-2021 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_defines.h" +#include "apk_adb.h" +#include "apk_applet.h" +#include "apk_database.h" +#include "apk_pathbuilder.h" +#include "apk_print.h" + +#define BLOCK_SIZE 4096 + +struct mkpkg_ctx { + struct apk_ctx *ac; + const char *files_dir, *output; + struct adb db; + struct adb_obj paths, *files; + struct apk_sign_ctx sctx; + apk_blob_t info[ADBI_PI_MAX]; + uint64_t installed_size; + struct apk_pathbuilder pb; +}; + +#define MKPKG_OPTIONS(OPT) \ + OPT(OPT_MKPKG_files, APK_OPT_ARG APK_OPT_SH("f") "files") \ + OPT(OPT_MKPKG_info, APK_OPT_ARG APK_OPT_SH("i") "info") \ + OPT(OPT_MKPKG_output, APK_OPT_ARG APK_OPT_SH("o") "output") \ + +APK_OPT_APPLET(option_desc, MKPKG_OPTIONS); + +static int option_parse_applet(void *ctx, struct apk_ctx *ac, int optch, const char *optarg) +{ + struct apk_out *out = &ac->out; + struct mkpkg_ctx *ictx = ctx; + apk_blob_t l, r; + int i; + + switch (optch) { + case OPT_MKPKG_info: + apk_blob_split(APK_BLOB_STR(optarg), APK_BLOB_STRLIT(":"), &l, &r); + i = adb_s_field_by_name_blob(&schema_pkginfo, l); + if (!i || i == ADBI_PI_FILE_SIZE || i == ADBI_PI_INSTALLED_SIZE) { + apk_err(out, "invalid pkginfo field: " BLOB_FMT, BLOB_PRINTF(l)); + return -EINVAL; + } + ictx->info[i] = r; + break; + case OPT_MKPKG_files: + ictx->files_dir = optarg; + break; + case OPT_MKPKG_output: + ictx->output = optarg; + break; + default: + return -ENOTSUP; + } + return 0; +} + +static const struct apk_option_group optgroup_applet = { + .desc = option_desc, + .parse = option_parse_applet, +}; + +static int mkpkg_process_dirent(void *pctx, int dirfd, const char *entry); + +static int mkpkg_process_directory(struct mkpkg_ctx *ctx, int dirfd, struct apk_file_info *fi) +{ + struct apk_ctx *ac = ctx->ac; + struct apk_id_cache *idc = apk_ctx_get_id_cache(ac); + struct apk_out *out = &ac->out; + struct adb_obj acl, fio, files, *prev_files; + apk_blob_t dirname = apk_pathbuilder_get(&ctx->pb); + int r; + + adb_wo_alloca(&fio, &schema_dir, &ctx->db); + adb_wo_alloca(&acl, &schema_acl, &ctx->db); + adb_wo_blob(&fio, ADBI_DI_NAME, dirname); + adb_wo_int(&acl, ADBI_ACL_MODE, fi->mode & ~S_IFMT); + adb_wo_blob(&acl, ADBI_ACL_USER, apk_id_cache_resolve_user(idc, fi->uid)); + adb_wo_blob(&acl, ADBI_ACL_GROUP, apk_id_cache_resolve_group(idc, fi->gid)); + adb_wo_obj(&fio, ADBI_DI_ACL, &acl); + + adb_wo_alloca(&files, &schema_file_array, &ctx->db); + prev_files = ctx->files; + ctx->files = &files; + r = apk_dir_foreach_file(dirfd, mkpkg_process_dirent, ctx); + ctx->files = prev_files; + if (r) { + apk_err(out, "failed to process directory '%s': %d", + apk_pathbuilder_cstr(&ctx->pb), r); + return r; + } + + adb_wo_obj(&fio, ADBI_DI_FILES, &files); + adb_wa_append_obj(&ctx->paths, &fio); + return 0; +} + +static int mkpkg_process_dirent(void *pctx, int dirfd, const char *entry) +{ + struct mkpkg_ctx *ctx = pctx; + struct apk_ctx *ac = ctx->ac; + struct apk_out *out = &ac->out; + struct apk_id_cache *idc = apk_ctx_get_id_cache(ac); + struct apk_file_info fi; + struct adb_obj fio, acl; + int r; + + r = apk_fileinfo_get(dirfd, entry, APK_FI_NOFOLLOW | APK_FI_CSUM(APK_CHECKSUM_SHA1), &fi, NULL); + if (r) return r; + + switch (fi.mode & S_IFMT) { + case S_IFDIR: + apk_pathbuilder_push(&ctx->pb, entry); + r = mkpkg_process_directory(ctx, openat(dirfd, entry, O_RDONLY), &fi); + apk_pathbuilder_pop(&ctx->pb); + break; + case S_IFREG: + adb_wo_alloca(&fio, &schema_file, &ctx->db); + adb_wo_alloca(&acl, &schema_acl, &ctx->db); + adb_wo_blob(&fio, ADBI_FI_NAME, APK_BLOB_STR(entry)); + adb_wo_blob(&fio, ADBI_FI_HASHES, APK_BLOB_PTR_LEN((char*) fi.csum.data, fi.csum.type)); + adb_wo_int(&fio, ADBI_FI_MTIME, fi.mtime); + adb_wo_int(&fio, ADBI_FI_SIZE, fi.size); + ctx->installed_size += (fi.size + BLOCK_SIZE - 1) & ~(BLOCK_SIZE-1); + + adb_wo_int(&acl, ADBI_ACL_MODE, fi.mode & 07777); + adb_wo_blob(&acl, ADBI_ACL_USER, apk_id_cache_resolve_user(idc, fi.uid)); + adb_wo_blob(&acl, ADBI_ACL_GROUP, apk_id_cache_resolve_group(idc, fi.gid)); + adb_wo_obj(&fio, ADBI_FI_ACL, &acl); + + adb_wa_append_obj(ctx->files, &fio); + break; + default: + apk_pathbuilder_push(&ctx->pb, entry); + apk_err(out, "special file '%s' not supported", + apk_pathbuilder_cstr(&ctx->pb), entry); + apk_pathbuilder_pop(&ctx->pb); + r = -EINVAL; + break; + } + return r; +} + +static char *pkgi_filename(struct adb_obj *pkgi, char *buf, size_t n) +{ + apk_blob_t to = APK_BLOB_PTR_LEN(buf, n); + apk_blob_push_blob(&to, adb_ro_blob(pkgi, ADBI_PI_NAME)); + apk_blob_push_blob(&to, APK_BLOB_STR("-")); + apk_blob_push_blob(&to, adb_ro_blob(pkgi, ADBI_PI_VERSION)); + apk_blob_push_blob(&to, APK_BLOB_STR(".apk")); + apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1)); + if (APK_BLOB_IS_NULL(to)) return 0; + return buf; +} + +static int mkpkg_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args) +{ + struct apk_out *out = &ac->out; + struct apk_trust *trust = apk_ctx_get_trust(ac); + struct adb_obj pkg, pkgi; + int i, j, r; + struct mkpkg_ctx *ctx = pctx; + struct apk_ostream *os; + char outbuf[PATH_MAX]; + + ctx->ac = ac; + adb_w_init_alloca(&ctx->db, ADB_SCHEMA_PACKAGE, 40); + adb_wo_alloca(&pkg, &schema_package, &ctx->db); + adb_wo_alloca(&pkgi, &schema_pkginfo, &ctx->db); + adb_wo_alloca(&ctx->paths, &schema_dir_array, &ctx->db); + + // prepare package info + for (i = 0; i < ARRAY_SIZE(ctx->info); i++) { + apk_blob_t val = ctx->info[i]; + if (APK_BLOB_IS_NULL(val)) { + switch (i) { + case ADBI_PI_NAME: + case ADBI_PI_VERSION: + r = -EINVAL; + apk_err(out, "required pkginfo field '%s' not provided", + schema_pkginfo.fields[i-1].name); + goto err; + } + continue; + } + adb_wo_val_fromstring(&pkgi, i, val); + } + if (adb_ro_val(&pkgi, ADBI_PI_ARCH) == ADB_VAL_NULL) + adb_wo_blob(&pkgi, ADBI_PI_ARCH, APK_BLOB_STRLIT(APK_DEFAULT_ARCH)); + + // scan and add all files + if (ctx->files_dir) { + struct apk_file_info fi; + r = apk_fileinfo_get(AT_FDCWD, ctx->files_dir, APK_FI_NOFOLLOW, &fi, 0); + if (r) { + apk_err(out, "file directory '%s': %s", + ctx->files_dir, apk_error_str(r)); + goto err; + } + r = mkpkg_process_directory(ctx, openat(AT_FDCWD, ctx->files_dir, O_RDONLY), &fi); + if (r) goto err; + if (!ctx->installed_size) ctx->installed_size = BLOCK_SIZE; + } + + adb_wo_int(&pkgi, ADBI_PI_INSTALLED_SIZE, ctx->installed_size); + adb_wo_obj(&pkg, ADBI_PKG_PKGINFO, &pkgi); + adb_wo_obj(&pkg, ADBI_PKG_PATHS, &ctx->paths); + adb_w_rootobj(&pkg); + + // re-read since object resets + adb_r_rootobj(&ctx->db, &pkg, &schema_package); + adb_ro_obj(&pkg, ADBI_PKG_PKGINFO, &pkgi); + adb_ro_obj(&pkg, ADBI_PKG_PATHS, &ctx->paths); + + if (!ctx->output) { + ctx->output = pkgi_filename(&pkgi, outbuf, sizeof outbuf); + } + + // construct package with ADB as header, and the file data in + // concatenated data blocks + os = apk_ostream_gzip(apk_ostream_to_file(AT_FDCWD, ctx->output, 0644)); + adb_c_adb(os, &ctx->db, trust); + int files_fd = openat(AT_FDCWD, ctx->files_dir, O_RDONLY); + for (i = ADBI_FIRST; i <= adb_ra_num(&ctx->paths); i++) { + struct adb_obj path, files, file; + adb_ro_obj(&ctx->paths, i, &path); + adb_ro_obj(&path, ADBI_DI_FILES, &files); + apk_blob_t dirname = adb_ro_blob(&path, ADBI_DI_NAME); + + apk_pathbuilder_setb(&ctx->pb, dirname); + for (j = ADBI_FIRST; j <= adb_ra_num(&files); j++) { + adb_ro_obj(&files, j, &file); + apk_blob_t filename = adb_ro_blob(&file, ADBI_FI_NAME); + apk_blob_t target = adb_ro_blob(&file, ADBI_FI_TARGET); + size_t sz = adb_ro_int(&file, ADBI_FI_SIZE); + if (!APK_BLOB_IS_NULL(target)) continue; + if (!sz) continue; + struct { + uint32_t path_idx; + uint32_t file_idx; + } hdr = { i, j }; + + apk_pathbuilder_pushb(&ctx->pb, filename); + adb_c_block_data( + os, APK_BLOB_STRUCT(hdr), sz, + apk_istream_from_fd(openat(files_fd, + apk_pathbuilder_cstr(&ctx->pb), + O_RDONLY))); + apk_pathbuilder_pop(&ctx->pb); + } + } + close(files_fd); + r = apk_ostream_close(os); + +err: + adb_free(&ctx->db); + if (r) apk_err(out, "failed to create package: %s", apk_error_str(r)); + return r; +} + +static struct apk_applet apk_mkpkg = { + .name = "mkpkg", + .context_size = sizeof(struct mkpkg_ctx), + .optgroups = { &optgroup_global, &optgroup_signing, &optgroup_applet }, + .main = mkpkg_main, +}; + +APK_DEFINE_APPLET(apk_mkpkg); @@ -1033,72 +1033,86 @@ size_t apk_ostream_write_string(struct apk_ostream *os, const char *string) } struct cache_item { - apk_hash_node hash_node; - unsigned int genid; - union { - uid_t uid; - gid_t gid; - }; + struct hlist_node by_id, by_name; + unsigned long id; unsigned short len; char name[]; }; -static apk_blob_t cache_item_get_key(apk_hash_item item) +static void idhash_init(struct apk_id_hash *idh) { - struct cache_item *ci = (struct cache_item *) item; - return APK_BLOB_PTR_LEN(ci->name, ci->len); + memset(idh, 0, sizeof *idh); + idh->empty = 1; } -static const struct apk_hash_ops id_hash_ops = { - .node_offset = offsetof(struct cache_item, hash_node), - .get_key = cache_item_get_key, - .hash_key = apk_blob_hash, - .compare = apk_blob_compare, - .delete_item = (apk_hash_delete_f) free, -}; +static void idhash_reset(struct apk_id_hash *idh) +{ + struct hlist_node *iter, *next; + struct cache_item *ci; + int i; -static struct cache_item *resolve_cache_item(struct apk_hash *hash, apk_blob_t name) + for (i = 0; i < ARRAY_SIZE(idh->by_id); i++) + hlist_for_each_entry_safe(ci, iter, next, &idh->by_id[i], by_id) + free(ci); + idhash_init(idh); +} + +static void idcache_add(struct apk_id_hash *hash, apk_blob_t name, unsigned long id) { struct cache_item *ci; unsigned long h; - h = id_hash_ops.hash_key(name); - ci = (struct cache_item *) apk_hash_get_hashed(hash, name, h); - if (ci != NULL) - return ci; - ci = calloc(1, sizeof(struct cache_item) + name.len); - if (ci == NULL) - return NULL; + if (!ci) return; + ci->id = id; ci->len = name.len; memcpy(ci->name, name.ptr, name.len); - apk_hash_insert_hashed(hash, ci, h); - return ci; + h = apk_blob_hash(name); + hlist_add_head(&ci->by_id, &hash->by_id[id % ARRAY_SIZE(hash->by_id)]); + hlist_add_head(&ci->by_name, &hash->by_name[h % ARRAY_SIZE(hash->by_name)]); +} + +static struct cache_item *idcache_by_name(struct apk_id_hash *hash, apk_blob_t name) +{ + struct cache_item *ci; + struct hlist_node *pos; + unsigned long h = apk_blob_hash(name); + + hlist_for_each_entry(ci, pos, &hash->by_name[h % ARRAY_SIZE(hash->by_name)], by_name) + if (apk_blob_compare(name, APK_BLOB_PTR_LEN(ci->name, ci->len)) == 0) + return ci; + return 0; +} + +static struct cache_item *idcache_by_id(struct apk_id_hash *hash, unsigned long id) +{ + struct cache_item *ci; + struct hlist_node *pos; + + hlist_for_each_entry(ci, pos, &hash->by_id[id % ARRAY_SIZE(hash->by_name)], by_id) + if (ci->id == id) return ci; + return 0; } void apk_id_cache_init(struct apk_id_cache *idc, int root_fd) { idc->root_fd = root_fd; - idc->genid = 1; - apk_hash_init(&idc->uid_cache, &id_hash_ops, 256); - apk_hash_init(&idc->gid_cache, &id_hash_ops, 256); + idhash_init(&idc->uid_cache); + idhash_init(&idc->gid_cache); } -void apk_id_cache_free(struct apk_id_cache *idc) +void apk_id_cache_reset(struct apk_id_cache *idc) { - if (!idc->root_fd) return; - apk_hash_free(&idc->uid_cache); - apk_hash_free(&idc->gid_cache); - idc->root_fd = 0; + idhash_reset(&idc->uid_cache); + idhash_reset(&idc->gid_cache); } -void apk_id_cache_reset(struct apk_id_cache *idc) +void apk_id_cache_free(struct apk_id_cache *idc) { - idc->genid++; - if (idc->genid == 0) - idc->genid = 1; + apk_id_cache_reset(idc); + idc->root_fd = 0; } static FILE *fopenat(int dirfd, const char *pathname) @@ -1114,86 +1128,94 @@ static FILE *fopenat(int dirfd, const char *pathname) return f; } -uid_t apk_resolve_uid(struct apk_id_cache *idc, apk_blob_t username, uid_t default_uid) +static void idcache_load_users(int root_fd, struct apk_id_hash *idh) { #ifdef HAVE_FGETPWENT_R char buf[1024]; struct passwd pwent; #endif - struct cache_item *ci; struct passwd *pwd; FILE *in; - ci = resolve_cache_item(&idc->uid_cache, username); - if (ci == NULL) return default_uid; + if (!idh->empty) return; + idh->empty = 0; - if (ci->genid != idc->genid) { - ci->genid = idc->genid; - ci->uid = -1; + in = fopenat(root_fd, "etc/passwd"); + if (!in) return; - in = fopenat(idc->root_fd, "etc/passwd"); - if (in) { - do { + do { #ifdef HAVE_FGETPWENT_R - fgetpwent_r(in, &pwent, buf, sizeof(buf), &pwd); + fgetpwent_r(in, &pwent, buf, sizeof(buf), &pwd); #else - pwd = fgetpwent(in); + pwd = fgetpwent(in); +#endif + if (!pwd) break; + idcache_add(idh, APK_BLOB_STR(pwd->pw_name), pwd->pw_uid); + } while (1); + fclose(in); +#ifndef HAVE_FGETPWENT_R + endpwent(); #endif - if (pwd == NULL) - break; - if (apk_blob_compare(APK_BLOB_STR(pwd->pw_name), username) == 0) { - ci->uid = pwd->pw_uid; - break; - } - } while (1); - fclose(in); - } - } - - if (ci->uid != -1) - return ci->uid; - - return default_uid; } -uid_t apk_resolve_gid(struct apk_id_cache *idc, apk_blob_t groupname, uid_t default_gid) +static void idcache_load_groups(int root_fd, struct apk_id_hash *idh) { #ifdef HAVE_FGETGRENT_R char buf[1024]; struct group grent; #endif - struct cache_item *ci; struct group *grp; FILE *in; - ci = resolve_cache_item(&idc->gid_cache, groupname); - if (ci == NULL) return default_gid; + if (!idh->empty) return; + idh->empty = 0; - if (ci->genid != idc->genid) { - ci->genid = idc->genid; - ci->gid = -1; + in = fopenat(root_fd, "etc/group"); + if (!in) return; - in = fopenat(idc->root_fd, "etc/group"); - if (in) { - do { + do { #ifdef HAVE_FGETGRENT_R - fgetgrent_r(in, &grent, buf, sizeof(buf), &grp); + fgetgrent_r(in, &grent, buf, sizeof(buf), &grp); #else - grp = fgetgrent(in); + grp = fgetgrent(in); #endif - if (grp == NULL) - break; - if (apk_blob_compare(APK_BLOB_STR(grp->gr_name), groupname) == 0) { - ci->gid = grp->gr_gid; - break; - } - } while (1); - fclose(in); - } - } + if (!grp) break; + idcache_add(idh, APK_BLOB_STR(grp->gr_name), grp->gr_gid); + } while (1); + fclose(in); +#ifndef HAVE_FGETGRENT_R + endgrent(); +#endif +} - if (ci->gid != -1) - return ci->gid; +uid_t apk_id_cache_resolve_uid(struct apk_id_cache *idc, apk_blob_t username, uid_t default_uid) +{ + struct cache_item *ci; + idcache_load_users(idc->root_fd, &idc->uid_cache); + ci = idcache_by_name(&idc->uid_cache, username); + return ci ? ci->id : default_uid; +} + +gid_t apk_id_cache_resolve_gid(struct apk_id_cache *idc, apk_blob_t groupname, gid_t default_gid) +{ + struct cache_item *ci; + idcache_load_groups(idc->root_fd, &idc->gid_cache); + ci = idcache_by_name(&idc->gid_cache, groupname); + return ci ? ci->id : default_gid; +} - return default_gid; +apk_blob_t apk_id_cache_resolve_user(struct apk_id_cache *idc, uid_t uid) +{ + struct cache_item *ci; + idcache_load_users(idc->root_fd, &idc->uid_cache); + ci = idcache_by_id(&idc->uid_cache, uid); + return ci ? APK_BLOB_PTR_LEN(ci->name, ci->len) : APK_BLOB_STRLIT("nobody"); +} + +apk_blob_t apk_id_cache_resolve_group(struct apk_id_cache *idc, gid_t gid) +{ + struct cache_item *ci; + idcache_load_groups(idc->root_fd, &idc->gid_cache); + ci = idcache_by_id(&idc->gid_cache, gid); + return ci ? APK_BLOB_PTR_LEN(ci->name, ci->len) : APK_BLOB_STRLIT("nobody"); } diff --git a/src/io_archive.c b/src/io_archive.c index 303fb93..ad4f621 100644 --- a/src/io_archive.c +++ b/src/io_archive.c @@ -150,8 +150,8 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser, entry = (struct apk_file_info){ .size = GET_OCTAL(buf.size), - .uid = apk_resolve_uid(idc, TAR_BLOB(buf.uname), GET_OCTAL(buf.uid)), - .gid = apk_resolve_gid(idc, TAR_BLOB(buf.gname), GET_OCTAL(buf.gid)), + .uid = apk_id_cache_resolve_uid(idc, TAR_BLOB(buf.uname), GET_OCTAL(buf.uid)), + .gid = apk_id_cache_resolve_gid(idc, TAR_BLOB(buf.gname), GET_OCTAL(buf.gid)), .mode = GET_OCTAL(buf.mode) & 07777, .mtime = GET_OCTAL(buf.mtime), .name = entry.name, diff --git a/src/meson.build b/src/meson.build index 6c5f6ca..4a76d4c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -18,6 +18,7 @@ libapk_src = [ 'io_url.c', 'io_gunzip.c', 'package.c', + 'pathbuilder.c', 'print.c', 'solver.c', 'trust.c', @@ -35,6 +36,7 @@ libapk_headers = [ 'apk_io.h', 'apk_openssl.h', 'apk_package.h', + 'apk_pathbuilder.h', 'apk_print.h', 'apk_provider_data.h', 'apk_solver_data.h', @@ -60,6 +62,7 @@ apk_src = [ 'app_list.c', 'app_manifest.c', 'app_mkndx.c', + 'app_mkpkg.c', 'app_policy.c', 'app_update.c', 'app_upgrade.c', diff --git a/src/pathbuilder.c b/src/pathbuilder.c new file mode 100644 index 0000000..166aa22 --- /dev/null +++ b/src/pathbuilder.c @@ -0,0 +1,29 @@ +/* apk_pathbuilder.h - Alpine Package Keeper (APK) + * + * Copyright (C) 2021 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include <errno.h> +#include "apk_pathbuilder.h" + +int apk_pathbuilder_pushb(struct apk_pathbuilder *pb, apk_blob_t b) +{ + size_t i = pb->namelen; + if (i + b.len + 2 >= ARRAY_SIZE(pb->name)) return -ENAMETOOLONG; + if (i) pb->name[i++] = '/'; + memcpy(&pb->name[i], b.ptr, b.len); + pb->namelen = i + b.len; + pb->name[pb->namelen] = 0; + return 0; +} + +void apk_pathbuilder_pop(struct apk_pathbuilder *pb) +{ + char *slash = memrchr(pb->name, '/', pb->namelen); + if (slash) pb->namelen = slash - pb->name; + else pb->namelen = 0; + pb->name[pb->namelen] = 0; +} |