diff --git a/src/Makefile b/src/Makefile index fe962e7..04cf60e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,7 +3,7 @@ apk-objs := state.o database.o package.o archive.o \ version.o io.o url.o gunzip.o blob.o \ hash.o md5.o apk.o \ add.o del.o update.o info.o search.o upgrade.o \ - ver.o index.o fetch.o audit.o + cache.o ver.o index.o fetch.o audit.o CFLAGS_apk.o := -DAPK_VERSION=\"$(FULL_VERSION)\" progs-$(STATIC) += apk.static diff --git a/src/apk.c b/src/apk.c index 25e7b07..024c11d 100644 --- a/src/apk.c +++ b/src/apk.c @@ -328,5 +328,8 @@ int main(int argc, char **argv) argv++; } - return applet->main(ctx, argc, argv); + r = applet->main(ctx, argc, argv); + if (r == -100) + return usage(applet); + return r; } diff --git a/src/apk_database.h b/src/apk_database.h index de7ce4b..4fb6ac8 100644 --- a/src/apk_database.h +++ b/src/apk_database.h @@ -19,6 +19,8 @@ #define APK_MAX_REPOS 32 +extern const char * const apk_index_gz; + struct apk_name; APK_ARRAY(apk_name_array, struct apk_name *); @@ -120,6 +122,7 @@ struct apk_db_file *apk_db_file_query(struct apk_database *db, int apk_db_open(struct apk_database *db, const char *root, unsigned int flags); 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); @@ -133,6 +136,10 @@ int apk_db_add_repository(apk_database_t db, apk_blob_t repository); int apk_repository_update(struct apk_database *db, struct apk_repository *repo); int apk_repository_update_all(struct apk_database *db); +int apk_cache_download(struct apk_database *db, csum_t csum, + const char *url, const char *item); +int apk_cache_exists(struct apk_database *db, csum_t csum, const char *item); + int apk_db_install_pkg(struct apk_database *db, struct apk_package *oldpkg, struct apk_package *newpkg, diff --git a/src/cache.c b/src/cache.c new file mode 100644 index 0000000..152008b --- /dev/null +++ b/src/cache.c @@ -0,0 +1,150 @@ +/* cache.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2005-2008 Natanael Copa + * Copyright (C) 2008 Timo Teräs + * 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 +#include + +#include "apk_defines.h" +#include "apk_applet.h" +#include "apk_database.h" + +#define CACHE_CLEAN BIT(0) +#define CACHE_DOWNLOAD BIT(1) + +static int cache_download(struct apk_database *db) +{ + struct apk_package *pkg; + char pkgfile[256]; + int i, r; + + list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) { + snprintf(pkgfile, sizeof(pkgfile), "%s-%s.apk", + pkg->name->name, pkg->version); + if (apk_cache_exists(db, pkg->csum, pkgfile)) + continue; + for (i = 0; i < db->num_repos; i++) { + if (!(pkg->repos & BIT(i))) + continue; + + r = apk_cache_download(db, pkg->csum, db->repos[i].url, + pkgfile); + if (r != 0) + return r; + } + } + return 0; +} + +static int cache_clean(struct apk_database *db) +{ + DIR *dir; + struct dirent *de; + char path[256]; + int delete, i; + csum_t csum; + + snprintf(path, sizeof(path), "%s/%s", db->root, db->cache_dir); + if (chdir(path) != 0) + return -1; + + dir = opendir(path); + if (dir == NULL) + return -1; + + while ((de = readdir(dir)) != NULL) { + if (de->d_name[0] == '.') + continue; + delete = TRUE; + do { + if (strlen(de->d_name) <= sizeof(csum_t)*2+2) + break; + if (apk_hexdump_parse(APK_BLOB_BUF(csum), + APK_BLOB_PTR_LEN(de->d_name, + sizeof(csum_t) * 2)) != 0) + break; + if (de->d_name[sizeof(csum_t)*2] != '.') + break; + if (strcmp(&de->d_name[sizeof(csum_t)*2+1], + apk_index_gz) == 0) { + /* Index - check for matching repository */ + for (i = 0; i < db->num_repos; i++) + if (memcmp(db->repos[i].url_csum, + csum, sizeof(csum_t)) == 0) + break; + delete = (i >= db->num_repos); + } else { + /* Package - search for it */ + delete = (apk_db_get_pkg(db, csum) == NULL); + } + } while (0); + + if (delete) { + if (apk_verbosity >= 2) + apk_message("Deleting %s", de->d_name); + if (!(apk_flags & APK_SIMULATE)) + unlink(de->d_name); + } + } + + closedir(dir); + return 0; +} + +static int cache_main(void *ctx, int argc, char **argv) +{ + struct apk_database db; + int actions = 0; + int r; + + if (argc != 1) + return -100; + + if (strcmp(argv[0], "sync") == 0) + actions = CACHE_CLEAN | CACHE_DOWNLOAD; + else if (strcmp(argv[0], "clean") == 0) + actions = CACHE_CLEAN; + else if (strcmp(argv[0], "download") == 0) + actions = CACHE_DOWNLOAD; + else + return -100; + + r = apk_db_open(&db, apk_root, + (actions & CACHE_DOWNLOAD) ? 0 : APK_OPENF_EMPTY_STATE); + if (r != 0) + return r; + + if (!apk_db_cache_active(&db)) { + apk_error("Package cache is not enabled.\n"); + r = 2; + goto err; + } + + if (r == 0 && (actions & CACHE_CLEAN)) + r = cache_clean(&db); + if (r == 0 && (actions & CACHE_DOWNLOAD)) + r = cache_download(&db); + +err: + apk_db_close(&db); + return r; +} + +static struct apk_applet apk_cache = { + .name = "cache", + .help = "Download missing PACKAGEs to cache directory, or delete " + "files no longer required. Package caching is enabled by " + "making /etc/apk/cache a symlink to the directory (on boot " + "media) that will be used as package cache.", + .arguments = "sync | clean | download", + .main = cache_main, +}; + +APK_DEFINE_APPLET(apk_cache); diff --git a/src/database.c b/src/database.c index 28da3b9..fda50db 100644 --- a/src/database.c +++ b/src/database.c @@ -25,7 +25,7 @@ #include "apk_state.h" #include "apk_applet.h" -static const char * const apk_index_gz = "APK_INDEX.gz"; +const char * const apk_index_gz = "APK_INDEX.gz"; static const char * const apk_static_cache_dir = "var/lib/apk"; static const char * const apk_linked_cache_dir = "etc/apk/cache"; @@ -832,6 +832,11 @@ void apk_db_close(struct apk_database *db) free(db->root); } +int apk_db_cache_active(struct apk_database *db) +{ + return db->cache_dir != apk_static_cache_dir; +} + struct apk_package *apk_db_get_pkg(struct apk_database *db, csum_t sum) { return apk_hash_get(&db->available.packages, @@ -908,7 +913,8 @@ static void apk_db_cache_get_name(char *buf, size_t bufsz, { char csumstr[sizeof(csum_t)*2+1]; - apk_hexdump_format(sizeof(csumstr), csumstr, APK_BLOB_BUF(csum)); + apk_hexdump_format(sizeof(csumstr), csumstr, + APK_BLOB_PTR_LEN((void *)csum, sizeof(csum_t))); snprintf(buf, bufsz, "%s/%s/%s.%s%s", db->root, db->cache_dir, csumstr, file, temp ? ".new" : ""); } @@ -935,32 +941,49 @@ static struct apk_bstream *apk_repository_file_open(struct apk_repository *repo, return apk_bstream_from_url(tmp); } -int apk_repository_update(struct apk_database *db, struct apk_repository *repo) +int apk_cache_download(struct apk_database *db, csum_t csum, + const char *url, const char *item) { char tmp[256], tmp2[256]; int r; - if (!csum_valid(repo->url_csum)) + snprintf(tmp, sizeof(tmp), "%s/%s", url, item); + apk_message("fetch %s", tmp); + + if (apk_flags & APK_SIMULATE) return 0; - apk_message("fetch index %s", repo->url); - - snprintf(tmp, sizeof(tmp), "%s/%s", repo->url, apk_index_gz); - apk_db_cache_get_name(tmp2, sizeof(tmp2), db, repo->url_csum, - apk_index_gz, TRUE); - + apk_db_cache_get_name(tmp2, sizeof(tmp2), db, csum, item, TRUE); r = apk_url_download(tmp, tmp2); if (r < 0) return r; - apk_db_cache_get_name(tmp, sizeof(tmp), db, repo->url_csum, - apk_index_gz, FALSE); + apk_db_cache_get_name(tmp, sizeof(tmp), db, csum, item, FALSE); if (rename(tmp2, tmp) < 0) return -errno; return 0; } +int apk_cache_exists(struct apk_database *db, csum_t csum, const char *item) +{ + char tmp[256]; + + if (db->root == NULL) + return 0; + + apk_db_cache_get_name(tmp, sizeof(tmp), db, csum, item, FALSE); + return access(tmp, R_OK | W_OK) == 0; +} + +int apk_repository_update(struct apk_database *db, struct apk_repository *repo) +{ + if (!csum_valid(repo->url_csum)) + return 0; + + return apk_cache_download(db, repo->url_csum, repo->url, apk_index_gz); +} + int apk_repository_update_all(struct apk_database *db) { int i, ret; @@ -1238,7 +1261,7 @@ static int apk_db_unpack_pkg(struct apk_database *db, struct install_ctx ctx; struct apk_bstream *bs = NULL; char pkgname[256], file[256]; - int i, need_copy = TRUE; + int i, need_copy = FALSE; size_t length; snprintf(pkgname, sizeof(pkgname), "%s-%s.apk", @@ -1258,8 +1281,7 @@ static int apk_db_unpack_pkg(struct apk_database *db, } repo = &db->repos[i]; - if (db->cache_dir != apk_static_cache_dir && - csum_valid(repo->url_csum)) + if (apk_db_cache_active(db) && csum_valid(repo->url_csum)) bs = apk_db_cache_open(db, newpkg->csum, pkgname); if (bs == NULL) { @@ -1273,6 +1295,8 @@ static int apk_db_unpack_pkg(struct apk_database *db, bs = apk_bstream_from_file(newpkg->filename); need_copy = TRUE; } + if (!apk_db_cache_active(db)) + need_copy = FALSE; if (need_copy) { apk_db_cache_get_name(file, sizeof(file), db, newpkg->csum, pkgname, TRUE); diff --git a/src/io.c b/src/io.c index 22a69e5..722bccb 100644 --- a/src/io.c +++ b/src/io.c @@ -331,7 +331,7 @@ static size_t tee_read(void *stream, void **ptr) { struct apk_tee_bstream *tbs = container_of(stream, struct apk_tee_bstream, bs); - size_t size; + ssize_t size; size = tbs->inner_bs->read(tbs->inner_bs, ptr); if (size >= 0) @@ -373,6 +373,7 @@ struct apk_bstream *apk_bstream_tee(struct apk_bstream *from, const char *to) }; tbs->inner_bs = from; tbs->fd = fd; + tbs->size = 0; return &tbs->bs; }