diff --git a/src/add.c b/src/add.c index 180615c..8f067b7 100644 --- a/src/add.c +++ b/src/add.c @@ -118,7 +118,7 @@ static int add_main(void *ctx, int argc, char **argv) if (strstr(argv[i], ".apk") != NULL) { struct apk_package *pkg; - pkg = apk_db_pkg_add_file(&db, argv[i]); + pkg = apk_pkg_read(&db, argv[i], APK_SIGN_VERIFY); if (pkg == NULL) { apk_error("Unable to read '%s'", argv[i]); goto err; diff --git a/src/apk_archive.h b/src/apk_archive.h index f1787dc..a0a289a 100644 --- a/src/apk_archive.h +++ b/src/apk_archive.h @@ -20,8 +20,9 @@ typedef int (*apk_archive_entry_parser)(void *ctx, const struct apk_file_info *ae, struct apk_istream *istream); -int apk_parse_tar(struct apk_istream *, apk_archive_entry_parser parser, void *ctx); -int apk_write_tar_entry(struct apk_ostream *, const struct apk_file_info *ae, char *data); +int apk_tar_parse(struct apk_istream *, apk_archive_entry_parser parser, void *ctx); +int apk_tar_write_entry(struct apk_ostream *, const struct apk_file_info *ae, char *data); +int apk_tar_write_padding(struct apk_ostream *, const struct apk_file_info *ae); int apk_archive_entry_extract(const struct apk_file_info *ae, struct apk_istream *is, diff --git a/src/apk_database.h b/src/apk_database.h index b4c3f04..979d0e8 100644 --- a/src/apk_database.h +++ b/src/apk_database.h @@ -134,7 +134,6 @@ int apk_db_write_config(struct apk_database *db); void apk_db_close(struct apk_database *db); int apk_db_cache_active(struct apk_database *db); -struct apk_package *apk_db_pkg_add_file(struct apk_database *db, const char *file); struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg); struct apk_package *apk_db_get_pkg(struct apk_database *db, struct apk_checksum *csum); struct apk_package *apk_db_get_file_owner(struct apk_database *db, apk_blob_t filename); diff --git a/src/apk_io.h b/src/apk_io.h index 49d9fcf..e547668 100644 --- a/src/apk_io.h +++ b/src/apk_io.h @@ -59,6 +59,7 @@ static inline struct apk_istream *apk_bstream_gunzip(struct apk_bstream *bs) } struct apk_ostream *apk_ostream_gzip(struct apk_ostream *); +struct apk_ostream *apk_ostream_counter(off_t *); struct apk_istream *apk_istream_from_fd(int fd); struct apk_istream *apk_istream_from_file(const char *file); diff --git a/src/apk_package.h b/src/apk_package.h index b874e0c..bf30350 100644 --- a/src/apk_package.h +++ b/src/apk_package.h @@ -30,6 +30,10 @@ struct apk_name; #define APK_PKG_NOT_INSTALLED 0 #define APK_PKG_INSTALLED 1 +#define APK_SIGN_VERIFY 0 +#define APK_SIGN_GENERATE_V1 1 +#define APK_SIGN_GENERATE 2 + struct apk_script { struct hlist_node script_list; unsigned int type; @@ -80,7 +84,7 @@ int apk_deps_write(struct apk_dependency_array *deps, struct apk_ostream *os); int apk_script_type(const char *name); struct apk_package *apk_pkg_new(void); -struct apk_package *apk_pkg_read(struct apk_database *db, const char *name); +struct apk_package *apk_pkg_read(struct apk_database *db, const char *name, int indexstyle); void apk_pkg_free(struct apk_package *pkg); int apk_pkg_parse_name(apk_blob_t apkname, apk_blob_t *name, apk_blob_t *version); diff --git a/src/archive.c b/src/archive.c index 8e55295..f30463f 100644 --- a/src/archive.c +++ b/src/archive.c @@ -93,7 +93,7 @@ static size_t tar_entry_read(void *stream, void *ptr, size_t size) return size; } -int apk_parse_tar(struct apk_istream *is, apk_archive_entry_parser parser, +int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser, void *ctx) { struct apk_file_info entry; @@ -205,11 +205,9 @@ err: return r; } -int apk_write_tar_entry(struct apk_ostream *os, const struct apk_file_info *ae, char *data) +int apk_tar_write_entry(struct apk_ostream *os, const struct apk_file_info *ae, char *data) { - static char padding[512]; struct tar_header buf; - int pad; memset(&buf, 0, sizeof(buf)); if (ae != NULL) { @@ -221,15 +219,17 @@ int apk_write_tar_entry(struct apk_ostream *os, const struct apk_file_info *ae, else return -1; - strncpy(buf.name, ae->name, sizeof(buf.name)); - strncpy(buf.uname, ae->uname, sizeof(buf.uname)); - strncpy(buf.gname, ae->gname, sizeof(buf.gname)); + if (ae->name != NULL) + strncpy(buf.name, ae->name, sizeof(buf.name)); + + strncpy(buf.uname, ae->uname ?: "root", sizeof(buf.uname)); + strncpy(buf.gname, ae->gname ?: "root", sizeof(buf.gname)); PUT_OCTAL(buf.size, ae->size); PUT_OCTAL(buf.uid, ae->uid); PUT_OCTAL(buf.gid, ae->gid); PUT_OCTAL(buf.mode, ae->mode & 07777); - PUT_OCTAL(buf.mtime, ae->mtime); + PUT_OCTAL(buf.mtime, ae->mtime ?: time(NULL)); /* Checksum */ strcpy(buf.magic, "ustar "); @@ -243,18 +243,33 @@ int apk_write_tar_entry(struct apk_ostream *os, const struct apk_file_info *ae, if (os->write(os, &buf, sizeof(buf)) != sizeof(buf)) return -1; - if (data != NULL) { + if (ae == NULL) { + /* End-of-archive is two empty headers */ + if (os->write(os, &buf, sizeof(buf)) != sizeof(buf)) + return -1; + } else if (data != NULL) { if (os->write(os, data, ae->size) != ae->size) return -1; - pad = 512 - (ae->size & 511); - if (pad != 512 && - os->write(os, padding, pad) != pad) + if (apk_tar_write_padding(os, ae) != 0) return -1; } return 0; } +int apk_tar_write_padding(struct apk_ostream *os, const struct apk_file_info *ae) +{ + static char padding[512]; + int pad; + + pad = 512 - (ae->size & 511); + if (pad != 512 && + os->write(os, padding, pad) != pad) + return -1; + + return 0; +} + int apk_archive_entry_extract(const struct apk_file_info *ae, struct apk_istream *is, const char *fn, apk_progress_cb cb, diff --git a/src/database.c b/src/database.c index 2a15385..5a1348e 100644 --- a/src/database.c +++ b/src/database.c @@ -545,19 +545,14 @@ static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os struct apk_file_info fi; char filename[256]; apk_blob_t bfn; - int r, i; + int r; list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) { hlist_for_each_entry(script, c2, &pkg->scripts, script_list) { fi = (struct apk_file_info) { .name = filename, - .uname = "root", - .gname = "root", .size = script->size, - .uid = 0, - .gid = 0, .mode = 0755 | S_IFREG, - .mtime = time(NULL), }; /* The scripts db expects file names in format: * pkg-version..action */ @@ -571,18 +566,13 @@ static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os apk_blob_push_blob(&bfn, APK_BLOB_STR(apk_script_types[script->type])); apk_blob_push_blob(&bfn, APK_BLOB_PTR_LEN("", 1)); - r = apk_write_tar_entry(os, &fi, script->script); + r = apk_tar_write_entry(os, &fi, script->script); if (r < 0) return r; } } - for (i = 0; i < 2; i++) { - r = apk_write_tar_entry(os, NULL, NULL); - if (r < 0) - return r; - } - return 0; + return apk_tar_write_entry(os, NULL, NULL); } static int apk_db_scriptdb_read_v1(struct apk_database *db, struct apk_istream *is) @@ -688,7 +678,7 @@ static int apk_db_read_state(struct apk_database *db, int flags) if (!(flags & APK_OPENF_NO_SCRIPTS)) { is = apk_istream_from_file("var/lib/apk/scripts.tar"); if (is != NULL) { - apk_parse_tar(is, apk_read_script_archive_entry, db); + apk_tar_parse(is, apk_read_script_archive_entry, db); } else { is = apk_istream_from_file("var/lib/apk/scripts"); if (is != NULL) @@ -970,16 +960,6 @@ struct apk_package *apk_db_get_file_owner(struct apk_database *db, return dbf->diri->pkg; } -struct apk_package *apk_db_pkg_add_file(struct apk_database *db, const char *file) -{ - struct apk_package *info; - - info = apk_pkg_read(db, file); - if (info != NULL) - info = apk_db_pkg_add(db, info); - return info; -} - struct index_write_ctx { struct apk_ostream *os; int count; @@ -1433,7 +1413,7 @@ static int apk_db_unpack_pkg(struct apk_database *db, }; tar = apk_bstream_gunzip_mpart(bs, apk_db_gzip_part, &ctx); - if (apk_parse_tar(tar, apk_db_install_archive_entry, &ctx) != 0) + if (apk_tar_parse(tar, apk_db_install_archive_entry, &ctx) != 0) goto err_close; tar->close(tar); diff --git a/src/gunzip.c b/src/gunzip.c index af906d1..2c4387e 100644 --- a/src/gunzip.c +++ b/src/gunzip.c @@ -30,7 +30,7 @@ struct apk_gzip_istream { void *cbctx; }; -static size_t gz_read(void *stream, void *ptr, size_t size) +static size_t gzi_read(void *stream, void *ptr, size_t size) { struct apk_gzip_istream *gis = container_of(stream, struct apk_gzip_istream, is); @@ -78,11 +78,13 @@ static size_t gz_read(void *stream, void *ptr, size_t size) EVP_DigestUpdate(&gis->mdctx, gis->mdblock, (void *)gis->zs.next_in - gis->mdblock); gis->mdblock = gis->zs.next_in; - gis->cb(gis->cbctx, &gis->mdctx, - APK_MPART_BOUNDARY); + if (gis->cb(gis->cbctx, &gis->mdctx, + APK_MPART_BOUNDARY)) { + gis->z_err = Z_STREAM_END; + break; + } } inflateEnd(&gis->zs); - if (inflateInit2(&gis->zs, 15+32) != Z_OK) return -1; gis->z_err = Z_OK; @@ -95,7 +97,7 @@ static size_t gz_read(void *stream, void *ptr, size_t size) return size - gis->zs.avail_out; } -static void gz_close(void *stream) +static void gzi_close(void *stream) { struct apk_gzip_istream *gis = container_of(stream, struct apk_gzip_istream, is); @@ -117,11 +119,11 @@ struct apk_istream *apk_bstream_gunzip_mpart(struct apk_bstream *bs, gis = malloc(sizeof(struct apk_gzip_istream)); if (gis == NULL) - return NULL; + goto err; *gis = (struct apk_gzip_istream) { - .is.read = gz_read, - .is.close = gz_close, + .is.read = gzi_read, + .is.close = gzi_close, .bs = bs, .z_err = 0, .cb = cb, @@ -130,7 +132,7 @@ struct apk_istream *apk_bstream_gunzip_mpart(struct apk_bstream *bs, if (inflateInit2(&gis->zs, 15+32) != Z_OK) { free(gis); - return NULL; + goto err; } if (gis->cb != NULL) { @@ -139,5 +141,83 @@ struct apk_istream *apk_bstream_gunzip_mpart(struct apk_bstream *bs, } return &gis->is; +err: + bs->close(bs, NULL); + return NULL; +} + +struct apk_gzip_ostream { + struct apk_ostream os; + struct apk_ostream *output; + z_stream zs; + unsigned char buffer[8*1024]; +}; + +static size_t gzo_write(void *stream, const void *ptr, size_t size) +{ + struct apk_gzip_ostream *gos = (struct apk_gzip_ostream *) stream; + size_t have; + int r; + + gos->zs.avail_in = size; + gos->zs.next_in = (void *) ptr; + while (gos->zs.avail_in) { + gos->zs.avail_out = sizeof(gos->buffer); + gos->zs.next_out = gos->buffer; + r = deflate(&gos->zs, Z_NO_FLUSH); + if (r == Z_STREAM_ERROR) + return -1; + have = sizeof(gos->buffer) - gos->zs.avail_out; + if (have != 0) { + r = gos->output->write(gos->output, gos->buffer, have); + if (r != have) + return -1; + } + } + + return size; +} + +static void gzo_close(void *stream) +{ + struct apk_gzip_ostream *gos = (struct apk_gzip_ostream *) stream; + size_t have; + + deflate(&gos->zs, Z_FINISH); + have = sizeof(gos->buffer) - gos->zs.avail_out; + gos->output->write(gos->output, gos->buffer, have); + gos->output->close(gos->output); + + deflateEnd(&gos->zs); + free(stream); +} + +struct apk_ostream *apk_ostream_gzip(struct apk_ostream *output) +{ + struct apk_gzip_ostream *gos; + + if (output == NULL) + return NULL; + + gos = malloc(sizeof(struct apk_gzip_ostream)); + if (gos == NULL) + goto err; + + *gos = (struct apk_gzip_ostream) { + .os.write = gzo_write, + .os.close = gzo_close, + .output = output, + }; + + if (deflateInit2(&gos->zs, 9, Z_DEFLATED, 15 | 16, 8, + Z_DEFAULT_STRATEGY) != Z_OK) { + free(gos); + goto err; + } + + return &gos->os; +err: + output->close(output); + return NULL; } diff --git a/src/index.c b/src/index.c index d7402f9..86c1f44 100644 --- a/src/index.c +++ b/src/index.c @@ -10,18 +10,23 @@ */ #include +#include #include #include "apk_applet.h" #include "apk_database.h" +#define INDEX_OLD_FORMAT 0x10000 + struct counts { int unsatisfied; }; struct index_ctx { const char *index; + const char *output; time_t index_mtime; + int method; }; static int index_parse(void *ctx, int optch, int optindex, const char *optarg) @@ -32,6 +37,12 @@ static int index_parse(void *ctx, int optch, int optindex, const char *optarg) case 'x': ictx->index = optarg; break; + case 'o': + ictx->output = optarg; + break; + case INDEX_OLD_FORMAT: + ictx->method = APK_SIGN_GENERATE_V1; + break; default: return -1; } @@ -83,6 +94,16 @@ static int index_main(void *ctx, int argc, char **argv) int total, i, j, found, newpkgs = 0; struct index_ctx *ictx = (struct index_ctx *) ctx; + if (isatty(STDOUT_FILENO) && ictx->output == NULL && + !(apk_flags & APK_FORCE)) { + apk_error("Will not write binary index to console " + "without --force"); + return -1; + } + + if (ictx->method == 0) + ictx->method = APK_SIGN_GENERATE; + apk_db_open(&db, NULL, APK_OPENF_READ); if (index_read_file(&db, ictx) < 0) { apk_db_close(&db); @@ -137,13 +158,33 @@ static int index_main(void *ctx, int argc, char **argv) } while (0); if (!found) { - apk_db_pkg_add_file(&db, argv[i]); - newpkgs++; + if (apk_pkg_read(&db, argv[i], ictx->method) != NULL) + newpkgs++; } } - os = apk_ostream_to_fd(STDOUT_FILENO); + if (ictx->method == APK_SIGN_GENERATE) { + memset(&fi, 0, sizeof(fi)); + fi.name = "APKINDEX"; + fi.mode = 0755 | S_IFREG; + os = apk_ostream_counter(&fi.size); + apk_db_index_write(&db, os); + os->close(os); + } + + if (ictx->output != NULL) + os = apk_ostream_to_file(ictx->output, 0755); + else + os = apk_ostream_to_fd(STDOUT_FILENO); + if (ictx->method == APK_SIGN_GENERATE) { + os = apk_ostream_gzip(os); + apk_tar_write_entry(os, &fi, NULL); + } total = apk_db_index_write(&db, os); + if (ictx->method == APK_SIGN_GENERATE) { + apk_tar_write_padding(os, &fi); + apk_tar_write_entry(os, NULL, NULL); + } os->close(os); apk_hash_foreach(&db.available.names, warn_if_no_providers, &counts); @@ -160,9 +201,13 @@ static int index_main(void *ctx, int argc, char **argv) } static struct apk_option index_options[] = { + { 'o', "output", "Write the generated index to FILE", + required_argument, "FILE" }, { 'x', "index", "Read INDEX to speed up new index creation by reusing " "the information from an old index", required_argument, "INDEX" }, + { INDEX_OLD_FORMAT, "old-format", + "Specify to create old style index files" } }; static struct apk_applet apk_index = { diff --git a/src/io.c b/src/io.c index 26d51b9..c7846c9 100644 --- a/src/io.c +++ b/src/io.c @@ -578,6 +578,45 @@ struct apk_ostream *apk_ostream_to_file(const char *file, mode_t mode) return apk_ostream_to_fd(fd); } +struct apk_counter_ostream { + struct apk_ostream os; + off_t *counter; +}; + +static size_t co_write(void *stream, const void *ptr, size_t size) +{ + struct apk_counter_ostream *cos = + container_of(stream, struct apk_counter_ostream, os); + + *cos->counter += size; + return size; +} + +static void co_close(void *stream) +{ + struct apk_counter_ostream *cos = + container_of(stream, struct apk_counter_ostream, os); + + free(cos); +} + +struct apk_ostream *apk_ostream_counter(off_t *counter) +{ + struct apk_counter_ostream *cos; + + cos = malloc(sizeof(struct apk_counter_ostream)); + if (cos == NULL) + return NULL; + + *cos = (struct apk_counter_ostream) { + .os.write = co_write, + .os.close = co_close, + .counter = counter, + }; + + return &cos->os; +} + size_t apk_ostream_write_string(struct apk_ostream *os, const char *string) { size_t len; diff --git a/src/package.c b/src/package.c index c01a5e8..95c4cc0 100644 --- a/src/package.c +++ b/src/package.c @@ -257,8 +257,13 @@ int apk_script_type(const char *name) struct read_info_ctx { struct apk_database *db; struct apk_package *pkg; - int version; - int has_install; + const EVP_MD *md; + int version, action; + int has_signature : 1; + int has_install : 1; + int has_data_checksum : 1; + int data_started : 1; + int in_signatures : 1; }; int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg, @@ -325,12 +330,16 @@ static int read_info_line(void *ctx, apk_blob_t line) return 0; for (i = 0; i < ARRAY_SIZE(fields); i++) { - if (strncmp(fields[i].str, l.ptr, l.len) == 0) { + if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) { apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r); - break; + return 0; } } + if (ri->data_started == 0 && + apk_blob_compare(APK_BLOB_STR("sha256"), l) == 0) + ri->has_data_checksum = 1; + return 0; } @@ -354,21 +363,27 @@ static int read_info_entry(void *ctx, const struct apk_file_info *ae, int i; /* Meta info and scripts */ - if (ae->name[0] == '.') { + if (ri->in_signatures && strncmp(ae->name, ".SIGN.", 6) != 0) + ri->in_signatures = 0; + + if (ri->data_started == 0 && ae->name[0] == '.') { /* APK 2.0 format */ - ri->version = 2; if (strcmp(ae->name, ".PKGINFO") == 0) { apk_blob_t blob = apk_blob_from_istream(is, ae->size); apk_blob_for_each_segment(blob, "\n", read_info_line, ctx); free(blob.ptr); - return 0; - } - if (strcmp(ae->name, ".INSTALL") == 0) { + ri->version = 2; + } else if (strncmp(ae->name, ".SIGN.", 6) == 0) { + ri->has_signature = 1; + } else if (strcmp(ae->name, ".INSTALL") == 0) { apk_warning("Package '%s-%s' contains deprecated .INSTALL", pkg->name->name, pkg->version); - return 0; } - } else if (strncmp(ae->name, "var/db/apk/", 11) == 0) { + return 0; + } + + ri->data_started = 1; + if (strncmp(ae->name, "var/db/apk/", 11) == 0) { /* APK 1.0 format */ ri->version = 1; if (!S_ISREG(ae->mode)) @@ -399,10 +414,7 @@ static int read_info_entry(void *ctx, const struct apk_file_info *ae, if (apk_script_type(slash+1) == APK_SCRIPT_POST_INSTALL || apk_script_type(slash+1) == APK_SCRIPT_PRE_INSTALL) ri->has_install = 1; - } else if (ri->version == 2) { - /* All metdata of version 2.x package handled */ - return 0; - } else { + } else if (ri->version < 2) { /* Version 1.x packages do not contain installed size * in metadata, so we calculate it here */ pkg->installed_size += apk_calc_installed_size(ae->size); @@ -417,29 +429,59 @@ static int apk_pkg_gzip_part(void *ctx, EVP_MD_CTX *mdctx, int part) switch (part) { case APK_MPART_BEGIN: - EVP_DigestInit_ex(mdctx, EVP_md5(), NULL); + EVP_DigestInit_ex(mdctx, ri->md, NULL); break; + case APK_MPART_BOUNDARY: + if (ri->in_signatures) { + EVP_DigestFinal_ex(mdctx, ri->pkg->csum.data, NULL); + EVP_DigestInit_ex(mdctx, ri->md, NULL); + return 0; + } + + if (ri->action == APK_SIGN_GENERATE_V1 || + !ri->has_data_checksum) + break; + /* Fallthrough to calculate checksum */ case APK_MPART_END: ri->pkg->csum.type = EVP_MD_CTX_size(mdctx); EVP_DigestFinal_ex(mdctx, ri->pkg->csum.data, NULL); - break; + return 1; } return 0; } -struct apk_package *apk_pkg_read(struct apk_database *db, const char *file) +struct apk_package *apk_pkg_read(struct apk_database *db, const char *file, + int action) { struct read_info_ctx ctx; struct apk_file_info fi; struct apk_bstream *bs; struct apk_istream *tar; char realfile[PATH_MAX]; + int r; if (realpath(file, realfile) < 0) return NULL; if (apk_file_get_info(realfile, APK_CHECKSUM_NONE, &fi) < 0) return NULL; + memset(&ctx, 0, sizeof(ctx)); + switch (action) { + case APK_SIGN_VERIFY: + ctx.in_signatures = 1; + ctx.md = EVP_md_null(); + break; + case APK_SIGN_GENERATE: + ctx.in_signatures = 1; + ctx.md = EVP_sha1(); + break; + case APK_SIGN_GENERATE_V1: + ctx.md = EVP_md5(); + break; + default: + return NULL; + } + ctx.pkg = apk_pkg_new(); if (ctx.pkg == NULL) return NULL; @@ -450,15 +492,22 @@ struct apk_package *apk_pkg_read(struct apk_database *db, const char *file) ctx.db = db; ctx.has_install = 0; + ctx.action = action; ctx.pkg->size = fi.size; tar = apk_bstream_gunzip_mpart(bs, apk_pkg_gzip_part, &ctx); - if (apk_parse_tar(tar, read_info_entry, &ctx) < 0) { + r = apk_tar_parse(tar, read_info_entry, &ctx); + tar->close(tar); + switch (r) { + case 0: + break; + case -2: + apk_error("File %s does not have a signature", file); + goto err; + default: apk_error("File %s is not an APK archive", file); - bs->close(bs, NULL); goto err; } - tar->close(tar); if (ctx.pkg->name == NULL) { apk_error("File %s is corrupted", file); @@ -474,7 +523,7 @@ struct apk_package *apk_pkg_read(struct apk_database *db, const char *file) } ctx.pkg->filename = strdup(realfile); - return ctx.pkg; + return apk_db_pkg_add(db, ctx.pkg); err: apk_pkg_free(ctx.pkg); return NULL;