Newer
Older
/* database.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 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 <unistd.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <sys/file.h>
#include "apk_defines.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_state.h"
struct install_ctx {
struct apk_database *db;
struct apk_package *pkg;
int script;
apk_progress_cb cb;
void *cb_ctx;
size_t installed_size;
size_t current_file_size;
struct hlist_node **diri_node;
struct hlist_node **file_diri_node;
};
static apk_blob_t pkg_name_get_key(apk_hash_item item)
{
return APK_BLOB_STR(((struct apk_name *) item)->name);
}
static void pkg_name_free(struct apk_name *name)
{
free(name->name);
free(name->pkgs);
free(name);
}
static const struct apk_hash_ops pkg_name_hash_ops = {
.node_offset = offsetof(struct apk_name, hash_node),
.get_key = pkg_name_get_key,
.hash_key = apk_blob_hash,
.compare = apk_blob_compare,
.delete_item = (apk_hash_delete_f) pkg_name_free,
};
static apk_blob_t pkg_info_get_key(apk_hash_item item)
{
return APK_BLOB_BUF(((struct apk_package *) item)->csum);
}
static unsigned long csum_hash(apk_blob_t csum)
{
/* Checksum's highest bits have the most "randomness", use that
* directly as hash */
}
static const struct apk_hash_ops pkg_info_hash_ops = {
.node_offset = offsetof(struct apk_package, hash_node),
.get_key = pkg_info_get_key,
.hash_key = csum_hash,
.compare = apk_blob_compare,
.delete_item = (apk_hash_delete_f) apk_pkg_free,
};
static apk_blob_t apk_db_dir_get_key(apk_hash_item item)
{
return APK_BLOB_STR(((struct apk_db_dir *) item)->dirname);
}
static const struct apk_hash_ops dir_hash_ops = {
.node_offset = offsetof(struct apk_db_dir, hash_node),
.get_key = apk_db_dir_get_key,
.hash_key = apk_blob_hash,
.compare = apk_blob_compare,
.delete_item = (apk_hash_delete_f) free,
};
struct apk_db_file_hash_key {
apk_blob_t dirname;
apk_blob_t filename;
};
static unsigned long apk_db_file_hash_key(apk_blob_t _key)
struct apk_db_file_hash_key *key = (struct apk_db_file_hash_key *) _key.ptr;
return apk_blob_hash(key->dirname) ^
apk_blob_hash(key->filename);
}
static unsigned long apk_db_file_hash_item(apk_hash_item item)
{
struct apk_db_file *dbf = (struct apk_db_file *) item;
return apk_blob_hash(APK_BLOB_STR(dbf->diri->dir->dirname)) ^
apk_blob_hash(APK_BLOB_STR(dbf->filename));
}
static int apk_db_file_compare_item(apk_hash_item item, apk_blob_t _key)
{
struct apk_db_file *dbf = (struct apk_db_file *) item;
struct apk_db_file_hash_key *key = (struct apk_db_file_hash_key *) _key.ptr;
int r;
r = apk_blob_compare(key->dirname, APK_BLOB_STR(dbf->diri->dir->dirname));
if (r != 0)
return r;
return apk_blob_compare(key->filename, APK_BLOB_STR(dbf->filename));
}
static const struct apk_hash_ops file_hash_ops = {
.node_offset = offsetof(struct apk_db_file, hash_node),
.hash_key = apk_db_file_hash_key,
.hash_item = apk_db_file_hash_item,
.compare_item = apk_db_file_compare_item,
.delete_item = (apk_hash_delete_f) free,
};
struct apk_name *apk_db_query_name(struct apk_database *db, apk_blob_t name)
{
return (struct apk_name *) apk_hash_get(&db->available.names, name);
}
struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
{
struct apk_name *pn;
if (pn != NULL)
return pn;
pn = calloc(1, sizeof(struct apk_name));
if (pn == NULL)
return NULL;
apk_hash_insert(&db->available.names, pn);
return pn;
}
static void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir)
{
dir->refs--;
if (dir->refs > 0)
return;
db->installed.stats.dirs--;
if (dir->parent != NULL)
apk_db_dir_unref(db, dir->parent);
}
static struct apk_db_dir *apk_db_dir_ref(struct apk_db_dir *dir)
{
dir->refs++;
return dir;
}
struct apk_db_dir *apk_db_dir_query(struct apk_database *db,
apk_blob_t name)
{
return (struct apk_db_dir *) apk_hash_get(&db->installed.dirs, name);
}
static struct apk_db_dir *apk_db_dir_get(struct apk_database *db,
apk_blob_t name)
{
struct apk_db_dir *dir;
apk_blob_t bparent;
if (name.len && name.ptr[name.len-1] == '/')
name.len--;
dir = apk_db_dir_query(db, name);
if (dir != NULL)
return apk_db_dir_ref(dir);
dir = calloc(1, sizeof(*dir) + name.len + 1);
memcpy(dir->dirname, name.ptr, name.len);
dir->dirname[name.len] = 0;
apk_hash_insert(&db->installed.dirs, dir);
if (name.len == 0)
dir->parent = NULL;
else if (apk_blob_rsplit(name, '/', &bparent, NULL))
dir->parent = apk_db_dir_get(db, bparent);
dir->parent = apk_db_dir_get(db, APK_BLOB_NULL);
if (dir->parent != NULL)
dir->flags = dir->parent->flags;
for (i = 0; i < db->protected_paths->num; i++) {
if (db->protected_paths->item[i][0] == '-' &&
strcmp(&db->protected_paths->item[i][1], dir->dirname) == 0)
dir->flags &= ~APK_DBDIRF_PROTECTED;
else if (strcmp(db->protected_paths->item[i], dir->dirname) == 0)
dir->flags |= APK_DBDIRF_PROTECTED;
}
return dir;
}
static struct apk_db_dir_instance *apk_db_diri_new(struct apk_database *db,
struct apk_package *pkg,
apk_blob_t name,
struct hlist_node ***after)
{
struct apk_db_dir_instance *diri;
diri = calloc(1, sizeof(struct apk_db_dir_instance));
hlist_add_after(&diri->pkg_dirs_list, *after);
*after = &diri->pkg_dirs_list.next;
diri->dir = apk_db_dir_get(db, name);
return diri;
}
static void apk_db_diri_set(struct apk_db_dir_instance *diri, mode_t mode,
uid_t uid, gid_t gid)
{
diri->mode = mode;
diri->uid = uid;
diri->gid = gid;
}
static void apk_db_diri_mkdir(struct apk_db_dir_instance *diri)
if (mkdir(diri->dir->dirname, diri->mode) == 0)
chown(diri->dir->dirname, diri->uid, diri->gid);
}
static void apk_db_diri_rmdir(struct apk_db_dir_instance *diri)
{
if (diri->dir->refs == 1) {
rmdir(diri->dir->dirname);
}
}
static void apk_db_diri_free(struct apk_database *db,
struct apk_db_dir_instance *diri)
{
apk_db_dir_unref(db, diri->dir);
struct apk_db_file *apk_db_file_query(struct apk_database *db,
apk_blob_t dir,
apk_blob_t name)
{
struct apk_db_file_hash_key key;
key = (struct apk_db_file_hash_key) {
.dirname = dir,
.filename = name,
};
return (struct apk_db_file *) apk_hash_get(&db->installed.files,
APK_BLOB_BUF(&key));
}
static struct apk_db_file *apk_db_file_get(struct apk_database *db,
struct apk_db_dir_instance *diri,
apk_blob_t name,
struct hlist_node ***after)
{
struct apk_db_file *file;
struct apk_db_file_hash_key key;
key = (struct apk_db_file_hash_key) {
.dirname = APK_BLOB_STR(diri->dir->dirname),
.filename = name,
};
file = (struct apk_db_file *) apk_hash_get(&db->installed.files,
APK_BLOB_BUF(&key));
if (file != NULL)
return file;
file = calloc(1, sizeof(*file) + name.len + 1);
memcpy(file->filename, name.ptr, name.len);
file->filename[name.len] = 0;
file->diri = diri;
hlist_add_after(&file->diri_files_list, *after);
*after = &file->diri_files_list.next;
apk_hash_insert(&db->installed.files, file);
db->installed.stats.files++;
return file;
}
static void apk_db_file_change_owner(struct apk_database *db,
struct apk_db_file *file,
struct apk_db_dir_instance *diri,
struct hlist_node ***after)
{
hlist_del(&file->diri_files_list, &file->diri->owned_files);
hlist_add_after(&file->diri_files_list, *after);
*after = &file->diri_files_list.next;
}
static void apk_db_pkg_rdepends(struct apk_database *db, struct apk_package *pkg)
{
int i, j;
if (pkg->depends == NULL)
return;
for (i = 0; i < pkg->depends->num; i++) {
struct apk_name *rname = pkg->depends->item[i].name;
if (rname->rdepends) {
for (j = 0; j < rname->rdepends->num; j++)
if (rname->rdepends->item[j] == pkg->name)
return;
}
*apk_name_array_add(&rname->rdepends) = pkg->name;
}
}
static struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg)
{
struct apk_package *idb;
idb = apk_hash_get(&db->available.packages, APK_BLOB_BUF(pkg->csum));
if (idb == NULL) {
idb = pkg;
apk_hash_insert(&db->available.packages, pkg);
*apk_package_array_add(&pkg->name->pkgs) = pkg;
if (idb->filename == NULL && pkg->filename != NULL) {
idb->filename = pkg->filename;
pkg->filename = NULL;
}
apk_pkg_free(pkg);
}
return idb;
}
static int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
{
struct apk_package *pkg = NULL;
struct apk_db_dir_instance *diri = NULL;
struct apk_db_file *file = NULL;
struct hlist_node **diri_node = NULL;
struct hlist_node **file_diri_node = NULL;
char buf[1024];
apk_blob_t l, r;
int n, field;
r = APK_BLOB_PTR_LEN(buf, 0);
while (1) {
n = is->read(is, &r.ptr[r.len], sizeof(buf) - r.len);
if (n <= 0)
break;
r.len += n;
while (apk_blob_splitstr(r, "\n", &l, &r)) {
if (l.len < 2 || l.ptr[1] != ':') {
if (pkg == NULL)
continue;
if (repo != -1)
pkg->repos |= BIT(repo);
else
apk_pkg_set_state(db, pkg, APK_PKG_INSTALLED);
if (apk_db_pkg_add(db, pkg) != pkg && repo == -1) {
apk_error("Installed database load failed");
return -1;
}
pkg = NULL;
continue;
}
/* Get field */
field = l.ptr[0];
l.ptr += 2;
l.len -= 2;
/* If no package, create new */
if (pkg == NULL) {
pkg = apk_pkg_new();
diri_node = hlist_tail_ptr(&pkg->owned_dirs);
}
/* Standard index line? */
if (apk_pkg_add_info(db, pkg, field, l) == 0)
continue;
if (repo != -1) {
apk_error("Invalid index entry '%c'", field);
return -1;
}
/* Check FDB special entries */
switch (field) {
case 'F':
if (pkg->name == NULL) {
apk_error("FDB directory entry before package entry");
return -1;
}
diri = apk_db_diri_new(db, pkg, l, &diri_node);
file_diri_node = &diri->owned_files.first;
break;
apk_error("FDB directory metadata entry before directory entry");
return -1;
}
/* FIXME: sscanf may touch unallocated area */
if (sscanf(l.ptr, "%d:%d:%o",
&diri->uid, &diri->gid, &diri->mode) != 3) {
apk_error("FDB bad directory mode entry");
return -1;
}
apk_error("FDB file entry before directory entry");
return -1;
}
file = apk_db_file_get(db, diri, l,
&file_diri_node);
break;
if (file == NULL) {
apk_error("FDB checksum entry before file entry");
return -1;
}
if (apk_hexdump_parse(APK_BLOB_BUF(file->csum), l)) {
apk_error("Not a valid checksum");
return -1;
}
break;
default:
apk_error("FDB entry '%c' unsupported", n);
return -1;
}
}
memcpy(&buf[0], r.ptr, r.len);
r = APK_BLOB_PTR_LEN(buf, r.len);
}
}
static int apk_db_write_fdb(struct apk_database *db, struct apk_ostream *os)
{
struct apk_package *pkg;
struct apk_db_file *file;
char buf[1024];
list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
r = apk_pkg_write_index_entry(pkg, os);
if (r < 0)
return r;
hlist_for_each_entry(diri, c1, &pkg->owned_dirs, pkg_dirs_list) {
n += snprintf(&buf[n], sizeof(buf)-n,
"F:%s\n"
"M:%d:%d:%o\n",
diri->dir->dirname,
diri->uid, diri->gid, diri->mode);
hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
"R:%s\n",
file->filename);
if (csum_valid(file->csum)) {
n += snprintf(&buf[n], sizeof(buf)-n, "Z:");
n += apk_hexdump_format(sizeof(buf)-n, &buf[n],
APK_BLOB_BUF(file->csum));
n += snprintf(&buf[n], sizeof(buf)-n, "\n");
}
if (os->write(os, buf, n) != n)
return -1;
n = 0;
if (n != 0 && os->write(os, buf, n) != n)
return -1;
n = 0;
}
}
return 0;
}
struct apk_script_header {
csum_t csum;
unsigned int type;
unsigned int size;
};
static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os)
{
struct apk_package *pkg;
struct apk_script *script;
struct apk_script_header hdr;
list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
hlist_for_each_entry(script, c2, &pkg->scripts, script_list) {
memcpy(hdr.csum, pkg->csum, sizeof(csum_t));
hdr.type = script->type;
hdr.size = script->size;
if (os->write(os, &hdr, sizeof(hdr)) != sizeof(hdr))
return -1;
if (os->write(os, script->script, script->size) != script->size)
return -1;
}
}
return 0;
}
static int apk_db_scriptdb_read(struct apk_database *db, struct apk_istream *is)
{
struct apk_package *pkg;
struct apk_script_header hdr;
while (is->read(is, &hdr, sizeof(hdr)) == sizeof(hdr)) {
pkg = apk_db_get_pkg(db, hdr.csum);
if (pkg != NULL)
apk_pkg_add_script(pkg, is, hdr.type, hdr.size);
}
return 0;
}
static int apk_db_read_state(struct apk_database *db)
{
/* Read:
* 1. installed repository
* 2. source repositories
* 3. master dependencies
* 4. package statuses
* 5. files db
* 6. script db
*/
fchdir(db->root_fd);
blob = apk_blob_from_file("var/lib/apk/world");
if (APK_BLOB_IS_NULL(blob))
return -ENOENT;
apk_deps_parse(db, &db->world, blob);
free(blob.ptr);
for (i = 0; i < db->world->num; i++)
db->world->item[i].name->flags |= APK_NAME_TOPLEVEL;
is = apk_istream_from_file("var/lib/apk/installed");
}
is = apk_istream_from_file("var/lib/apk/scripts");
if (is != NULL) {
}
return 0;
}
static int add_protected_path(void *ctx, apk_blob_t blob)
{
struct apk_database *db = (struct apk_database *) ctx;
*apk_string_array_add(&db->protected_paths) = apk_blob_cstr(blob);
return 0;
}
static int apk_db_create(struct apk_database *db)
{
apk_blob_t deps = APK_BLOB_STR("busybox alpine-baselayout "
"apk-tools alpine-conf");
int fd;
fchdir(db->root_fd);
mkdir("tmp", 01777);
mkdir("dev", 0755);
mknod("dev/null", 0666, makedev(1, 3));
mkdir("var", 0755);
mkdir("var/lib", 0755);
mkdir("var/lib/apk", 0755);
fd = creat("var/lib/apk/world", 0644);
if (fd < 0)
return -errno;
write(fd, deps.ptr, deps.len);
close(fd);
return 0;
}
int apk_db_open(struct apk_database *db, const char *root, unsigned int flags)
const char *apk_repos = getenv("APK_REPOS"), *msg;
struct apk_repository_url *repo = NULL;
memset(db, 0, sizeof(*db));
apk_hash_init(&db->available.names, &pkg_name_hash_ops, 1000);
apk_hash_init(&db->available.packages, &pkg_info_hash_ops, 4000);
apk_hash_init(&db->installed.dirs, &dir_hash_ops, 1000);
apk_hash_init(&db->installed.files, &file_hash_ops, 4000);
fchdir(apk_cwd_fd);
db->root = strdup(root);
db->root_fd = open(root, O_RDONLY);
if (db->root_fd < 0 && (flags & APK_OPENF_CREATE)) {
mkdir(db->root, 0755);
db->root_fd = open(root, O_RDONLY);
}
msg = "Unable to open root";
goto ret_errno;
}
fchdir(db->root_fd);
if (flags & APK_OPENF_WRITE) {
db->lock_fd = open("var/lib/apk/lock",
O_CREAT | O_WRONLY, 0400);
if (db->lock_fd < 0 && errno == ENOENT &&
(flags & APK_OPENF_CREATE)) {
r = apk_db_create(db);
if (r != 0) {
msg = "Unable to create database";
goto ret_r;
}
db->lock_fd = open("var/lib/apk/lock",
O_CREAT | O_WRONLY, 0400);
}
if (db->lock_fd < 0 ||
flock(db->lock_fd, LOCK_EX | LOCK_NB) < 0) {
msg = "Unable to lock database";
goto ret_errno;
}
blob = APK_BLOB_STR("etc:-etc/init.d");
apk_blob_for_each_segment(blob, ":", add_protected_path, db);
if (root != NULL) {
if (!(flags & APK_OPENF_EMPTY_STATE)) {
r = apk_db_read_state(db);
if (r == -ENOENT && (flags & APK_OPENF_CREATE)) {
r = apk_db_create(db);
if (r != 0) {
msg = "Unable to create database";
goto ret_r;
}
r = apk_db_read_state(db);
}
msg = "Unable to read database state";
goto ret_r;
}
}
if (apk_repos == NULL)
apk_repos = "/etc/apk/repositories";
blob = apk_blob_from_file(apk_repos);
if (!APK_BLOB_IS_NULL(blob)) {
r = apk_blob_for_each_segment(blob, "\n",
apk_db_add_repository, db);
free(blob.ptr);
if (r != 0) {
msg = "Unable to load repositories";
goto ret_r;
}
}
}
list_for_each_entry(repo, &apk_repository_list.list, list) {
r = apk_db_add_repository(db, APK_BLOB_STR(repo->url));
goto ret_r;
}
}
ret_errno:
r = -errno;
ret_r:
apk_error("%s: %s", msg, strerror(-r));
apk_db_close(db);
struct write_ctx {
struct apk_database *db;
int fd;
};
int apk_db_write_config(struct apk_database *db)
{
if (db->root == NULL)
if (db->lock_fd == 0) {
apk_error("Refusing to write db without write lock!");
return -1;
}
fchdir(db->root_fd);
os = apk_ostream_to_file("var/lib/apk/world", 0644);
return -1;
apk_deps_write(db->world, os);
os->write(os, "\n", 1);
os = apk_ostream_to_file("var/lib/apk/installed.new", 0644);
return -1;
if (rename("var/lib/apk/installed.new", "var/lib/apk/installed") < 0)
return -errno;
os = apk_ostream_to_file("var/lib/apk/scripts", 0644);
return -1;
return 0;
}
void apk_db_close(struct apk_database *db)
{
struct apk_package *pkg;
struct apk_db_dir_instance *diri;
struct hlist_node *dc, *dn;
int i;
list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list) {
hlist_for_each_entry_safe(diri, dc, dn, &pkg->owned_dirs, pkg_dirs_list) {
apk_db_diri_free(db, diri);
}
}
for (i = 0; i < db->num_repos; i++) {
if (db->repos[i].cache != NULL)
free(db->repos[i].cache);
}
if (db->protected_paths) {
for (i = 0; i < db->protected_paths->num; i++)
free(db->protected_paths->item[i]);
free(db->protected_paths);
}
if (db->world)
free(db->world);
apk_hash_free(&db->available.names);
apk_hash_free(&db->available.packages);
apk_hash_free(&db->installed.files);
apk_hash_free(&db->installed.dirs);
if (db->lock_fd)
close(db->lock_fd);
if (db->root != NULL)
free(db->root);
}
struct apk_package *apk_db_get_pkg(struct apk_database *db, csum_t sum)
{
return apk_hash_get(&db->available.packages,
APK_BLOB_PTR_LEN((void*) sum, sizeof(csum_t)));
}
struct apk_package *apk_db_get_file_owner(struct apk_database *db,
apk_blob_t filename)
{
struct apk_db_file *dbf;
struct apk_db_file_hash_key key;
if (filename.len && filename.ptr[0] == '/')
filename.len--, filename.ptr++;
if (!apk_blob_rsplit(filename, '/', &key.dirname, &key.filename))
return NULL;
dbf = (struct apk_db_file *) apk_hash_get(&db->installed.files,
APK_BLOB_BUF(&key));
if (dbf == NULL)
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);
info = apk_db_pkg_add(db, info);
}
struct index_write_ctx {
struct apk_ostream *os;
int count;
};
static int write_index_entry(apk_hash_item item, void *ctx)
{
struct index_write_ctx *iwctx = (struct index_write_ctx *) ctx;
struct apk_package *pkg = (struct apk_package *) item;
if (pkg->repos != 0)
return 0;
r = apk_pkg_write_index_entry(pkg, iwctx->os);
if (r < 0)
return r;
if (iwctx->os->write(iwctx->os, "\n", 1) != 1)
return 0;
}
int apk_db_index_write(struct apk_database *db, struct apk_ostream *os)
{
struct index_write_ctx ctx = { os, 0 };
apk_hash_foreach(&db->available.packages, write_index_entry, &ctx);
return ctx.count;
}
static struct apk_bstream *apk_db_cache_open(struct apk_database *db,
const char *file)
{
char tmp[256];
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
if (db->root == NULL)
return NULL;
snprintf(tmp, sizeof(tmp), "%svar/lib/apk/%s", db->root, file);
return apk_bstream_from_file(tmp);
}
static int apk_db_cache_has(struct apk_database *db, const char *file)
{
char tmp[256];
struct apk_file_info fi;
int r;
if (db->root == NULL)
return FALSE;
snprintf(tmp, sizeof(tmp), "%svar/lib/apk/%s", db->root, file);
r = apk_file_get_info(tmp, &fi);
if (r < 0)
return FALSE;
return TRUE;
}
static struct apk_bstream *apk_repository_file_open(struct apk_repository *repo,
const char *file)
{
char tmp[256];
snprintf(tmp, sizeof(tmp), "%s/%s", repo->url, file);
return apk_bstream_from_url(tmp);
}
int apk_repository_update(struct apk_database *db, struct apk_repository *repo)
{
char tmp[256], tmp2[256];
if (repo->cache == NULL)
return 0;
apk_message("fetch index %s", repo->url);
snprintf(tmp, sizeof(tmp), "%s/APK_INDEX.gz", repo->url);
snprintf(tmp2, sizeof(tmp2), "%svar/lib/apk/%s.new",
db->root, repo->cache);
r = apk_url_download(tmp, tmp2);
if (r < 0)
return r;
snprintf(tmp, sizeof(tmp), "%svar/lib/apk/%s",
db->root, repo->cache);
if (rename(tmp2, tmp) < 0)
return -errno;
return 0;
}
int apk_db_add_repository(apk_database_t _db, apk_blob_t repository)
{
struct apk_database *db = _db.db;
struct apk_istream *is = NULL;
char buf[2*sizeof(csum_t)+32], *name;
int r, n;
if (repository.ptr == NULL || *repository.ptr == '\0'
|| *repository.ptr == '#')
return 0;
if (db->num_repos >= APK_MAX_REPOS)
return -1;
r = db->num_repos++;
db->repos[r] = (struct apk_repository) {
.url = apk_blob_cstr(repository),
.cache = NULL,
};
if (apk_url_local_file(db->repos[r].url) == NULL) {
csum_ctx_t csum;
csum_t res;
csum_init(&csum);
csum_process(&csum, repository.ptr, repository.len);
csum_finish(&csum, res);
n = apk_hexdump_format(sizeof(buf), buf, APK_BLOB_BUF(res)) - 1;
snprintf(&buf[n], sizeof(buf) - n, ".index.gz");
db->repos[r].cache = strdup(buf);
if (!apk_db_cache_has(db, db->repos[r].cache)) {
n = apk_repository_update(db, &db->repos[r]);