diff options
-rw-r--r-- | src/adb.c | 89 | ||||
-rw-r--r-- | src/adb.h | 3 | ||||
-rw-r--r-- | src/apk_adb.h | 5 | ||||
-rw-r--r-- | src/apk_archive.h | 11 | ||||
-rw-r--r-- | src/apk_crypto.h | 19 | ||||
-rw-r--r-- | src/apk_io.h | 20 | ||||
-rw-r--r-- | src/app_extract.c | 239 | ||||
-rw-r--r-- | src/app_fetch.c | 2 | ||||
-rw-r--r-- | src/app_mkpkg.c | 9 | ||||
-rw-r--r-- | src/crypto_openssl.c | 34 | ||||
-rw-r--r-- | src/database.c | 4 | ||||
-rw-r--r-- | src/io.c | 27 | ||||
-rw-r--r-- | src/io_archive.c | 15 | ||||
-rw-r--r-- | src/meson.build | 1 |
14 files changed, 413 insertions, 65 deletions
@@ -44,7 +44,6 @@ int adb_free(struct adb *db) } 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); @@ -135,6 +134,79 @@ err: return r; } +int adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expected_schema, + struct apk_trust *t, int (*datacb)(struct adb *, size_t, struct apk_istream *)) +{ + struct adb_verify_ctx vfy = {}; + struct adb_block blk; + struct apk_segment_istream seg; + void *sig; + int r, block_no = 0; + int trusted = t ? 0 : 1; + size_t sz; + + if (IS_ERR(is)) return PTR_ERR(is); + if ((r = apk_istream_read(is, &db->hdr, sizeof db->hdr)) != sizeof db->hdr) goto err; + if (db->hdr.magic != htole32(ADB_FORMAT_MAGIC)) goto bad_msg; + + do { + r = apk_istream_read(is, &blk, sizeof blk); + if (r == 0) { + if (!trusted) r = -ENOKEY; + else if (!db->adb.ptr) r = -ENOMSG; + goto done; + } + if (r < 0 || r != sizeof blk) goto err; + + if ((block_no++ == 0) != (adb_block_type(&blk) == ADB_BLOCK_ADB)) + goto bad_msg; + + sz = adb_block_size(&blk) - sizeof blk; + switch (adb_block_type(&blk)) { + case ADB_BLOCK_ADB: + if (!APK_BLOB_IS_NULL(db->adb)) goto bad_msg; + db->adb.ptr = malloc(sz); + db->adb.len = adb_block_length(&blk); + if ((r = apk_istream_read(is, db->adb.ptr, sz)) != sz) goto err; + break; + case ADB_BLOCK_SIG: + if (APK_BLOB_IS_NULL(db->adb)) goto bad_msg; + sig = apk_istream_get(is, sz); + if (IS_ERR(sig)) { + r = PTR_ERR(sig); + goto err; + } + if (!trusted && + adb_trust_verify_signature(t, db, &vfy, APK_BLOB_PTR_LEN(sig, adb_block_length(&blk))) == 0) + trusted = 1; + break; + case ADB_BLOCK_DATA: + if (APK_BLOB_IS_NULL(db->adb)) goto bad_msg; + if (!trusted) { + r = -ENOKEY; + goto err; + } + r = datacb(db, adb_block_length(&blk), + apk_istream_segment(&seg, is, adb_block_size(&blk) - sizeof blk, 0)); + if (r < 0) goto err; + if (seg.bytes_left) { + r = apk_istream_read(is, NULL, seg.bytes_left); + if (r < 0) goto err; + } + break; + default: + goto bad_msg; + } + } while (1); +bad_msg: + r = -EBADMSG; +err: + if (r >= 0) r = -EBADMSG; +done: + apk_istream_close(is); + return r; +} + int adb_w_init_dynamic(struct adb *db, uint32_t schema, void *buckets, size_t num_buckets) { size_t i; @@ -947,7 +1019,7 @@ int adb_trust_write_signatures(struct apk_trust *trust, struct adb *db, struct a union { struct adb_sign_hdr hdr; struct adb_sign_v0 v0; - unsigned char buf[8192]; + unsigned char buf[ADB_MAX_SIGNATURE_LEN]; } sig; struct apk_trust_key *tkey; apk_blob_t md; @@ -1023,7 +1095,7 @@ int adb_c_xfrm(struct adb_xfrm *x, int (*cb)(struct adb_xfrm *, struct adb_block size_t sz; r = apk_istream_read(x->is, &x->db.hdr, sizeof x->db.hdr); - if (r < 0) goto err; + if (r != sizeof x->db.hdr) 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); @@ -1031,9 +1103,9 @@ int adb_c_xfrm(struct adb_xfrm *x, int (*cb)(struct adb_xfrm *, struct adb_block do { r = apk_istream_read(x->is, &blk, sizeof blk); - if (r <= 0) { - if (r == 0) r = cb(x, NULL, NULL); - goto err; + if (r != sizeof blk) { + if (r != 0) goto err; + return cb(x, NULL, NULL); } if ((block_no++ == 0) != (adb_block_type(&blk) == ADB_BLOCK_ADB)) goto bad_msg; @@ -1048,13 +1120,14 @@ int adb_c_xfrm(struct adb_xfrm *x, int (*cb)(struct adb_xfrm *, struct adb_block 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); - if (r < 0) goto err; + r = apk_istream_read(x->is, NULL, seg.bytes_left); + if (r != seg.bytes_left) goto err; } } while (1); bad_msg: r = -EBADMSG; err: + if (r >= 0) r = -EBADMSG; apk_ostream_cancel(x->os, r); return r; } @@ -74,6 +74,8 @@ 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)); } +#define ADB_MAX_SIGNATURE_LEN 256 + struct adb_sign_hdr { uint8_t sign_ver, hash_alg; }; @@ -161,6 +163,7 @@ void adb_reset(struct adb *); int adb_m_blob(struct adb *, apk_blob_t, struct apk_trust *); int adb_m_map(struct adb *, int fd, uint32_t expected_schema, struct apk_trust *); +int adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expected_schema, struct apk_trust *trust, int (*datacb)(struct adb *, size_t, struct apk_istream *)); #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); diff --git a/src/apk_adb.h b/src/apk_adb.h index 1908cba..68bc92d 100644 --- a/src/apk_adb.h +++ b/src/apk_adb.h @@ -73,6 +73,11 @@ #define ADBI_PKG_PASSWD 0x05 #define ADBI_PKG_MAX 0x06 +struct adb_data_package { + uint32_t path_idx; + uint32_t file_idx; +}; + /* Index */ #define ADBI_NDX_DESCRIPTION 0x01 #define ADBI_NDX_PACKAGES 0x02 diff --git a/src/apk_archive.h b/src/apk_archive.h index fdce49e..3dbd284 100644 --- a/src/apk_archive.h +++ b/src/apk_archive.h @@ -15,12 +15,6 @@ #include "apk_print.h" #include "apk_io.h" -#define APK_EXTRACTF_NO_CHOWN 0x0001 - -typedef int (*apk_archive_entry_parser)(void *ctx, - const struct apk_file_info *ae, - struct apk_istream *istream); - int apk_tar_parse(struct apk_istream *, apk_archive_entry_parser parser, void *ctx, struct apk_id_cache *); @@ -28,10 +22,13 @@ int apk_tar_write_entry(struct apk_ostream *, const struct apk_file_info *ae, const char *data); int apk_tar_write_padding(struct apk_ostream *, const struct apk_file_info *ae); +#define APK_EXTRACTF_NO_CHOWN 0x0001 +#define APK_EXTRACTF_NO_OVERWRITE 0x0002 + int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae, const char *extract_name, const char *hardlink_name, struct apk_istream *is, - apk_progress_cb cb, void *cb_ctx, + apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx, unsigned int extract_flags, struct apk_out *out); diff --git a/src/apk_crypto.h b/src/apk_crypto.h index 35d27aa..ac6e828 100644 --- a/src/apk_crypto.h +++ b/src/apk_crypto.h @@ -52,21 +52,17 @@ static inline const EVP_MD *apk_digest_alg_to_evp(uint8_t alg) { } } -static inline int apk_digest_alg_len(uint8_t alg) { - switch (alg) { - case APK_DIGEST_MD5: return 16; - case APK_DIGEST_SHA1: return 20; - case APK_DIGEST_SHA256: return 32; - case APK_DIGEST_SHA512: return 64; - default: - assert(alg); - return 0; - } +int apk_digest_alg_len(uint8_t alg); +uint8_t apk_digest_alg_by_len(int len); + +static inline int apk_digest_cmp(struct apk_digest *a, struct apk_digest *b) { + if (a->alg != b->alg) return b->alg - a->alg; + return memcmp(a->data, b->data, a->len); } static inline void apk_digest_reset(struct apk_digest *d) { - d->len = 0; d->alg = APK_DIGEST_NONE; + d->len = 0; } static inline void apk_digest_set(struct apk_digest *d, uint8_t alg) { @@ -114,6 +110,7 @@ static inline int apk_digest_ctx_final(struct apk_digest_ctx *dctx, struct apk_d } #include "apk_blob.h" +uint8_t apk_digest_from_blob(struct apk_digest *d, apk_blob_t b); static inline int apk_digest_cmp_csum(const struct apk_digest *d, const struct apk_checksum *csum) { return apk_blob_compare(APK_DIGEST_BLOB(*d), APK_BLOB_CSUM(*csum)); diff --git a/src/apk_io.h b/src/apk_io.h index a0025be..64f1efe 100644 --- a/src/apk_io.h +++ b/src/apk_io.h @@ -41,10 +41,10 @@ struct apk_file_meta { void apk_file_meta_to_fd(int fd, struct apk_file_meta *meta); struct apk_file_info { - char *name; - char *link_target; - char *uname; - char *gname; + const char *name; + const char *link_target; + const char *uname; + const char *gname; off_t size; uid_t uid; gid_t gid; @@ -77,6 +77,10 @@ struct apk_istream { const struct apk_istream_ops *ops; }; +typedef int (*apk_archive_entry_parser)(void *ctx, + const struct apk_file_info *ae, + struct apk_istream *istream); + #define APK_IO_ALL ((size_t)-1) #define APK_ISTREAM_FORCE_REFRESH ((time_t) -1) @@ -87,12 +91,12 @@ struct apk_istream *apk_istream_from_fd(int fd); struct apk_istream *apk_istream_from_fd_url_if_modified(int atfd, const char *url, time_t since); static inline int apk_istream_error(struct apk_istream *is, int err) { if (!is->err) is->err = err; return err; } ssize_t apk_istream_read(struct apk_istream *is, void *ptr, size_t size); -apk_blob_t apk_istream_get(struct apk_istream *is, size_t len); +void *apk_istream_get(struct apk_istream *is, size_t len); apk_blob_t apk_istream_get_max(struct apk_istream *is, size_t size); apk_blob_t apk_istream_get_delim(struct apk_istream *is, apk_blob_t token); static inline apk_blob_t apk_istream_get_all(struct apk_istream *is) { return apk_istream_get_max(is, APK_IO_ALL); } ssize_t apk_istream_splice(struct apk_istream *is, int fd, size_t size, - apk_progress_cb cb, void *cb_ctx); + apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx); ssize_t apk_stream_copy(struct apk_istream *is, struct apk_ostream *os, size_t size, apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx); @@ -172,9 +176,9 @@ int apk_blob_to_file(int atfd, const char *file, apk_blob_t b, unsigned int flag #define APK_FI_NOFOLLOW 0x80000000 #define APK_FI_XATTR_DIGEST(x) (((x) & 0xff) << 8) -#define APK_FI_XATTR_CSUM(x) APK_FI_XATTR_DIGEST(apk_digest_alg_from_csum(x)) +#define APK_FI_XATTR_CSUM(x) APK_FI_XATTR_DIGEST(apk_digest_alg_by_len(x)) #define APK_FI_DIGEST(x) (((x) & 0xff)) -#define APK_FI_CSUM(x) APK_FI_DIGEST(apk_digest_alg_from_csum(x)) +#define APK_FI_CSUM(x) APK_FI_DIGEST(apk_digest_alg_by_len(x)) int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags, struct apk_file_info *fi, struct apk_atom_pool *atoms); void apk_fileinfo_hash_xattr(struct apk_file_info *fi, uint8_t alg); diff --git a/src/app_extract.c b/src/app_extract.c new file mode 100644 index 0000000..acee37b --- /dev/null +++ b/src/app_extract.c @@ -0,0 +1,239 @@ +/* extract.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2008-2021 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "apk_applet.h" +#include "apk_print.h" +#include "apk_adb.h" +#include "apk_pathbuilder.h" + +struct extract_ctx { + const char *destination; + unsigned int extract_flags; + + struct apk_ctx *ac; + struct adb db; + int root_fd; + + struct adb_obj pkg, paths, path, files, file; + unsigned int cur_path, cur_file; + + struct apk_pathbuilder pb; +}; + + +#define EXTRACT_OPTIONS(OPT) \ + OPT(OPT_EXTRACT_destination, APK_OPT_ARG "destination") \ + OPT(OPT_EXTRACT_no_chown, "no-chown") + +APK_OPT_APPLET(option_desc, EXTRACT_OPTIONS); + +static int option_parse_applet(void *pctx, struct apk_ctx *ac, int opt, const char *optarg) +{ + struct extract_ctx *ctx = (struct extract_ctx *) pctx; + + switch (opt) { + case OPT_EXTRACT_destination: + ctx->destination = optarg; + break; + case OPT_EXTRACT_no_chown: + ctx->extract_flags |= APK_EXTRACTF_NO_CHOWN; + break; + default: + return -ENOTSUP; + } + return 0; +} + +static const struct apk_option_group optgroup_applet = { + .desc = option_desc, + .parse = option_parse_applet, +}; + +static void apk_extract_acl(struct apk_file_info *fi, struct adb_obj *o, struct apk_id_cache *idc) +{ + fi->mode = adb_ro_int(o, ADBI_ACL_MODE); + fi->uid = apk_id_cache_resolve_uid(idc, adb_ro_blob(o, ADBI_ACL_USER), 65534); + fi->gid = apk_id_cache_resolve_gid(idc, adb_ro_blob(o, ADBI_ACL_GROUP), 65534); +} + +static int apk_extract_file(struct extract_ctx *ctx, off_t sz, struct apk_istream *is) +{ + struct apk_ctx *ac = ctx->ac; + struct apk_out *out = &ac->out; + struct apk_file_info fi = { + .name = apk_pathbuilder_cstr(&ctx->pb), + .size = adb_ro_int(&ctx->file, ADBI_FI_SIZE), + .mtime = adb_ro_int(&ctx->file, ADBI_FI_MTIME), + }; + struct adb_obj acl; + struct apk_digest_ctx dctx; + struct apk_digest d; + int r; + + apk_digest_from_blob(&fi.digest, adb_ro_blob(&ctx->file, ADBI_FI_HASHES)); + if (fi.digest.alg == APK_DIGEST_NONE) return -EAPKFORMAT; + apk_extract_acl(&fi, adb_ro_obj(&ctx->file, ADBI_FI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac)); + fi.mode |= S_IFREG; + + apk_digest_ctx_init(&dctx, fi.digest.alg); + r = apk_archive_entry_extract( + ctx->root_fd, &fi, 0, 0, is, 0, 0, &dctx, + ctx->extract_flags, out); + apk_digest_ctx_final(&dctx, &d); + apk_digest_ctx_free(&dctx); + if (r != 0) return r; + if (apk_digest_cmp(&fi.digest, &d) != 0) return -EAPKDBFORMAT; + return 0; +} + +static int apk_extract_directory(struct extract_ctx *ctx) +{ + struct apk_ctx *ac = ctx->ac; + struct apk_out *out = &ac->out; + struct apk_file_info fi = { + .name = apk_pathbuilder_cstr(&ctx->pb), + }; + struct adb_obj acl; + + apk_extract_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac)); + fi.mode |= S_IFDIR; + + return apk_archive_entry_extract( + ctx->root_fd, &fi, 0, 0, 0, 0, 0, 0, + ctx->extract_flags, out); +} + +static int apk_extract_next_file(struct extract_ctx *ctx) +{ + apk_blob_t target; + int r; + + if (!ctx->cur_path) { + // one time init + ctx->cur_path = ADBI_FIRST; + ctx->cur_file = 0; + adb_r_rootobj(&ctx->db, &ctx->pkg, &schema_package); + adb_ro_obj(&ctx->pkg, ADBI_PKG_PATHS, &ctx->paths); + adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path); + adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files); + } + + do { + ctx->cur_file++; + while (ctx->cur_file > adb_ra_num(&ctx->files)) { + ctx->cur_path++; + ctx->cur_file = ADBI_FIRST; + if (ctx->cur_path > adb_ra_num(&ctx->paths)) return 1; + adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path); + apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME)); + adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files); + r = apk_extract_directory(ctx); + if (r != 0) return r; + } + adb_ro_obj(&ctx->files, ctx->cur_file, &ctx->file); + apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME)); + apk_pathbuilder_pushb(&ctx->pb, adb_ro_blob(&ctx->file, ADBI_FI_NAME)); + target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET); + if (adb_ro_int(&ctx->file, ADBI_FI_SIZE) != 0 && + APK_BLOB_IS_NULL(target)) { + return 0; + } + r = apk_extract_file(ctx, 0, 0); + if (r != 0) return r; + } while (1); +} + +static int apk_extract_data_block(struct adb *db, size_t sz, struct apk_istream *is) +{ + struct extract_ctx *ctx = container_of(db, struct extract_ctx, db); + struct adb_data_package *hdr; + int r; + + r = apk_extract_next_file(ctx); + if (r != 0) { + if (r > 0) r = -EAPKFORMAT; + return r; + } + + hdr = apk_istream_get(is, sizeof *hdr); + sz -= sizeof *hdr; + if (IS_ERR(hdr)) return PTR_ERR(hdr); + + if (hdr->path_idx != ctx->cur_path || + hdr->file_idx != ctx->cur_file || + sz != adb_ro_int(&ctx->file, ADBI_FI_SIZE)) { + // got data for some unexpected file + return -EAPKFORMAT; + } + + return apk_extract_file(ctx, sz, is); +} + +static int apk_extract_pkg(struct extract_ctx *ctx, const char *fn) +{ + struct apk_ctx *ac = ctx->ac; + struct apk_trust *trust = apk_ctx_get_trust(ac); + int r; + + r = adb_m_stream(&ctx->db, + apk_istream_gunzip(apk_istream_from_fd_url(AT_FDCWD, fn, apk_ctx_since(ac, 0))), + ADB_SCHEMA_PACKAGE, trust, apk_extract_data_block); + if (r == 0) { + r = apk_extract_next_file(ctx); + if (r == 0) r = -EAPKFORMAT; + if (r == 1) r = 0; + } + adb_free(&ctx->db); + return r; +} + +static int extract_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args) +{ + struct extract_ctx *ctx = pctx; + struct apk_out *out = &ac->out; + char **parg; + int r; + + ctx->ac = ac; + ctx->extract_flags |= APK_EXTRACTF_NO_OVERWRITE; + if (!ctx->destination) ctx->destination = "."; + ctx->root_fd = openat(AT_FDCWD, ctx->destination, O_RDONLY); + if (ctx->root_fd < 0) { + r = -errno; + apk_err(out, "Error opening destination '%s': %s", + ctx->destination, apk_error_str(r)); + return r; + } + + foreach_array_item(parg, args) { + apk_out(out, "Extracting %s...", *parg); + r = apk_extract_pkg(ctx, *parg); + if (r != 0) { + apk_err(out, "%s: %s", *parg, apk_error_str(r)); + break; + } + } + close(ctx->root_fd); + return r; +} + +static struct apk_applet apk_extract = { + .name = "extract", + .context_size = sizeof(struct extract_ctx), + .optgroups = { &optgroup_global, &optgroup_applet }, + .main = extract_main, +}; + +APK_DEFINE_APPLET(apk_extract); + diff --git a/src/app_fetch.c b/src/app_fetch.c index d1c527d..64e6634 100644 --- a/src/app_fetch.c +++ b/src/app_fetch.c @@ -176,7 +176,7 @@ static int fetch_package(apk_hash_item item, void *pctx) goto err; } - r = apk_istream_splice(is, fd, pkg->size, progress_cb, ctx); + r = apk_istream_splice(is, fd, pkg->size, progress_cb, ctx, 0); if (fd != STDOUT_FILENO) { struct apk_file_meta meta; apk_istream_get_meta(is, &meta); diff --git a/src/app_mkpkg.c b/src/app_mkpkg.c index 82ba208..3c3972b 100644 --- a/src/app_mkpkg.c +++ b/src/app_mkpkg.c @@ -251,11 +251,10 @@ static int mkpkg_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a 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 }; - + struct adb_data_package hdr = { + .path_idx = i, + .file_idx = j, + }; apk_pathbuilder_pushb(&ctx->pb, filename); adb_c_block_data( os, APK_BLOB_STRUCT(hdr), sz, diff --git a/src/crypto_openssl.c b/src/crypto_openssl.c index 97d80b8..5512a49 100644 --- a/src/crypto_openssl.c +++ b/src/crypto_openssl.c @@ -23,6 +23,29 @@ const char *apk_digest_alg_str(uint8_t alg) return alg_str; } +int apk_digest_alg_len(uint8_t alg) +{ + switch (alg) { + case APK_DIGEST_MD5: return 16; + case APK_DIGEST_SHA1: return 20; + case APK_DIGEST_SHA256: return 32; + case APK_DIGEST_SHA512: return 64; + default: return 0; + } +} + +uint8_t apk_digest_alg_by_len(int len) +{ + switch (len) { + case 0: return APK_DIGEST_NONE; + case 16: return APK_DIGEST_MD5; + case 20: return APK_DIGEST_SHA1; + case 32: return APK_DIGEST_SHA256; + case 64: return APK_DIGEST_SHA512; + default: return APK_DIGEST_NONE; + } +} + uint8_t apk_digest_alg_from_csum(int csum) { switch (csum) { @@ -33,6 +56,17 @@ uint8_t apk_digest_alg_from_csum(int csum) } } +uint8_t apk_digest_from_blob(struct apk_digest *d, apk_blob_t b) +{ + d->alg = apk_digest_alg_by_len(b.len); + d->len = 0; + if (d->alg != APK_DIGEST_NONE) { + d->len = b.len; + memcpy(d->data, b.ptr, d->len); + } + return d->alg; +} + int apk_pkey_init(struct apk_pkey *pkey, EVP_PKEY *key) { unsigned char dig[EVP_MAX_MD_SIZE], *pub = NULL; diff --git a/src/database.c b/src/database.c index 04733b8..0c5cd43 100644 --- a/src/database.c +++ b/src/database.c @@ -672,7 +672,7 @@ int apk_cache_download(struct apk_database *db, struct apk_repository *repo, if (fd >= 0) { struct apk_file_meta meta; - r = apk_istream_splice(is, fd, APK_IO_ALL, cb, cb_ctx); + r = apk_istream_splice(is, fd, APK_IO_ALL, cb, cb_ctx, 0); if (!autoupdate) { apk_istream_get_meta(is, &meta); apk_file_meta_to_fd(fd, &meta); @@ -2566,7 +2566,7 @@ static int apk_db_install_archive_entry(void *_ctx, db->root_fd, ae, format_tmpname(pkg, file, tmpname_file), format_tmpname(pkg, link_target_file, tmpname_link_target), - is, extract_cb, ctx, db->extract_flags, out); + is, extract_cb, ctx, 0, db->extract_flags, out); switch (r) { case 0: @@ -114,27 +114,21 @@ static int __apk_istream_fill(struct apk_istream *is) return 0; } -apk_blob_t apk_istream_get(struct apk_istream *is, size_t len) +void *apk_istream_get(struct apk_istream *is, size_t len) { - apk_blob_t ret = APK_BLOB_NULL; - do { if (is->end - is->ptr >= len) { - ret = APK_BLOB_PTR_LEN((char*)is->ptr, len); - break; - } - if (is->err>0 || is->end-is->ptr == is->buf_size) { - ret = APK_BLOB_PTR_LEN((char*)is->ptr, is->end - is->ptr); - break; + void *ptr = is->ptr; + is->ptr += len; + return ptr; } } while (!__apk_istream_fill(is)); - if (!APK_BLOB_IS_NULL(ret)) { - is->ptr = (uint8_t*)ret.ptr + ret.len; - return ret; - } - - return (struct apk_blob) { .len = is->err < 0 ? is->err : 0 }; + if (is->end-is->ptr == is->buf_size) + return ERR_PTR(-ENOBUFS); + if (is->err > 0) + return ERR_PTR(-ENOMSG); + return ERR_PTR(-EIO); } apk_blob_t apk_istream_get_max(struct apk_istream *is, size_t max) @@ -518,7 +512,7 @@ ssize_t apk_stream_copy(struct apk_istream *is, struct apk_ostream *os, size_t s } ssize_t apk_istream_splice(struct apk_istream *is, int fd, size_t size, - apk_progress_cb cb, void *cb_ctx) + apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx) { static void *splice_buffer = NULL; unsigned char *buf, *mmapbase = MAP_FAILED; @@ -558,6 +552,7 @@ ssize_t apk_istream_splice(struct apk_istream *is, int fd, size_t size, } break; } + if (dctx) apk_digest_ctx_update(dctx, buf, r); if (mmapbase == MAP_FAILED) { if (write(fd, buf, r) != r) { diff --git a/src/io_archive.c b/src/io_archive.c index 5e1498a..57d8b83 100644 --- a/src/io_archive.c +++ b/src/io_archive.c @@ -184,8 +184,8 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser, if ((r = blob_realloc(&longname, entry.size+1)) != 0 || (r = apk_istream_read(is, longname.ptr, entry.size)) != entry.size) goto err; + longname.ptr[entry.size] = 0; entry.name = longname.ptr; - entry.name[entry.size] = 0; toskip -= entry.size; break; case 'K': /* GNU long link target extension - ignored */ @@ -336,15 +336,16 @@ int apk_tar_write_padding(struct apk_ostream *os, const struct apk_file_info *ae int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae, const char *extract_name, const char *link_target, struct apk_istream *is, - apk_progress_cb cb, void *cb_ctx, - unsigned int apk_extract_flags, - struct apk_out *out) + apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx, + unsigned int extract_flags, struct apk_out *out) { struct apk_xattr *xattr; const char *fn = extract_name ?: ae->name; int fd, r = -1, atflags = 0, ret = 0; - if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno; + if (!(extract_flags & APK_EXTRACTF_NO_OVERWRITE)) { + if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno; + } switch (ae->mode & S_IFMT) { case S_IFDIR: @@ -361,7 +362,7 @@ int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae, ret = -errno; break; } - r = apk_istream_splice(is, fd, ae->size, cb, cb_ctx); + r = apk_istream_splice(is, fd, ae->size, cb, cb_ctx, dctx); if (r != ae->size) ret = r < 0 ? r : -ENOSPC; close(fd); } else { @@ -386,7 +387,7 @@ int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae, return ret; } - if (!(apk_extract_flags & APK_EXTRACTF_NO_CHOWN)) { + if (!(extract_flags & APK_EXTRACTF_NO_CHOWN)) { r = fchownat(atfd, fn, ae->uid, ae->gid, atflags); if (r < 0) { apk_err(out, "Failed to set ownership on %s: %s", diff --git a/src/meson.build b/src/meson.build index 27dc566..b744a1a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -57,6 +57,7 @@ apk_src = [ 'app_convndx.c', 'app_del.c', 'app_dot.c', + 'app_extract.c', 'app_fetch.c', 'app_fix.c', 'app_index.c', |