Newer
Older
static void apk_db_cache_get_name(char *buf, size_t bufsz,
struct apk_database *db,
struct apk_checksum *csum,
{
apk_blob_t bbuf = APK_BLOB_BUF(csumstr);
apk_blob_push_hexdump(&bbuf,
APK_BLOB_PTR_LEN((char *) csum->data, APK_CACHE_CSUM_BYTES));
apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN("", 1));
snprintf(buf, bufsz, "%s/%s/%s.%s%s",
db->root, db->cache_dir, csumstr, file, temp ? ".new" : "");
static struct apk_bstream *apk_db_cache_open(struct apk_database *db,
struct apk_checksum *csum,
const char *file)
{
char tmp[256];
if (db->root == NULL)
apk_db_cache_get_name(tmp, sizeof(tmp), db, csum, file, FALSE);
return apk_bstream_from_file(tmp);
}
static struct apk_bstream *apk_repository_file_open(struct apk_repository *repo,
const char *file)
{
char tmp[256];
snprintf(tmp, sizeof(tmp), "%s%s%s",
url, url[strlen(url)-1] == '/' ? "" : "/", file);
return apk_bstream_from_url(tmp);
}
int apk_cache_download(struct apk_database *db, struct apk_checksum *csum,
const char *url, const char *item, int verify)
snprintf(tmp, sizeof(tmp), "%s%s%s",
url, url[strlen(url)-1] == '/' ? "" : "/", item);
apk_db_cache_get_name(tmp2, sizeof(tmp2), db, csum, item, TRUE);
r = apk_url_download(tmp, tmp2);
if (r < 0)
return r;
if (verify != APK_SIGN_NONE) {
struct apk_istream *is;
struct apk_sign_ctx sctx;
int ok;
apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL);
is = apk_bstream_gunzip_mpart(apk_bstream_from_file(tmp2),
apk_sign_ctx_mpart_cb, &sctx);
r = apk_tar_parse(is, apk_sign_ctx_verify_tar, &sctx);
is->close(is);
ok = (r == 0) && sctx.control_verified && sctx.data_verified;
apk_sign_ctx_free(&sctx);
if (!ok) {
unlink(tmp2);
return -10;
}
}
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, struct apk_checksum *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;
}
static int apk_cache_delete(struct apk_database *db, struct apk_checksum *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 unlink(tmp);
}
int apk_repository_update(struct apk_database *db, struct apk_repository *repo)
{
r = apk_cache_download(db, &repo->csum, repo->url, apkindex_tar_gz,
APK_SIGN_VERIFY);
if (r == 0 || r == -10) {
apk_error("%s: untrusted or bad signature!", repo->url);
apk_cache_delete(db, &repo->csum, apk_index_gz);
return r;
}
r = apk_cache_download(db, &repo->csum, repo->url, apk_index_gz,
APK_SIGN_NONE);
if (r != 0)
apk_error("Failed to update %s: download failed");
return r;
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
}
struct apkindex_ctx {
struct apk_database *db;
struct apk_sign_ctx sctx;
int repo;
};
static int load_apkindex(void *sctx, const struct apk_file_info *fi,
struct apk_istream *is)
{
struct apkindex_ctx *ctx = (struct apkindex_ctx *) sctx;
struct apk_bstream *bs;
if (apk_sign_ctx_process_file(&ctx->sctx, fi, is) == 0)
return 0;
if (strcmp(fi->name, "APKINDEX") != 0)
return 0;
bs = apk_bstream_from_istream(is);
apk_db_index_read(ctx->db, bs, ctx->repo);
bs->close(bs, NULL);
return 0;
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
static int load_index(struct apk_database *db, struct apk_bstream *bs,
int targz, int repo)
{
if (targz) {
struct apk_istream *is;
struct apkindex_ctx ctx;
ctx.db = db;
ctx.repo = repo;
apk_sign_ctx_init(&ctx.sctx, APK_SIGN_VERIFY, NULL);
is = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, &ctx.sctx);
apk_tar_parse(is, load_apkindex, &ctx);
is->close(is);
apk_sign_ctx_free(&ctx.sctx);
if (!ctx.sctx.data_verified)
return -1;
} else {
bs = apk_bstream_from_istream(apk_bstream_gunzip(bs));
apk_db_index_read(db, bs, repo);
bs->close(bs, NULL);
}
return 0;
}
int apk_db_index_read_file(struct apk_database *db, const char *file, int repo)
{
int targz = 1;
if (strstr(file, ".tar.gz") == NULL && strstr(file, ".gz") != NULL)
targz = 0;
return load_index(db, apk_bstream_from_file(file), targz, repo);
}
int apk_db_add_repository(apk_database_t _db, apk_blob_t repository)
{
struct apk_database *db = _db.db;
struct apk_bstream *bs = NULL;
struct apk_repository *repo;
if (repository.ptr == NULL || *repository.ptr == '\0'
|| *repository.ptr == '#')
return 0;
if (db->num_repos >= APK_MAX_REPOS)
return -1;
r = db->num_repos++;
repo = &db->repos[r];
*repo = (struct apk_repository) {
};
apk_blob_checksum(repository, apk_default_checksum(), &repo->csum);
bs = apk_db_cache_open(db, &repo->csum, apkindex_tar_gz);
bs = apk_db_cache_open(db, &repo->csum, apk_index_gz);
bs = apk_repository_file_open(repo, apkindex_tar_gz);
if (bs == NULL) {
bs = apk_repository_file_open(repo, apk_index_gz);
targz = 0;
}
apk_warning("Failed to open index for %s", repo->url);
return -1;
}
r = load_index(db, bs, targz, r);
if (r != 0)
apk_error("%s: Bad repository signature", repo->url);
return r;
}
static void extract_cb(void *_ctx, size_t progress)
{
struct install_ctx *ctx = (struct install_ctx *) _ctx;
if (ctx->cb) {
size_t size = ctx->installed_size;
size += muldiv(progress, ctx->current_file_size, APK_PROGRESS_SCALE);
if (size > ctx->pkg->installed_size)
size = ctx->pkg->installed_size;
ctx->cb(ctx->cb_ctx, muldiv(APK_PROGRESS_SCALE, size, ctx->pkg->installed_size));
}
}
static int apk_db_run_pending_script(struct install_ctx *ctx)
{
int r;
if (!ctx->script_pending)
return 0;
if (!ctx->sctx.control_verified)
return 0;
ctx->script_pending = FALSE;
r = apk_pkg_run_script(ctx->pkg, ctx->db->root_fd, ctx->script);
if (r != 0)
apk_error("%s-%s: Failed to execute "
"pre-install/upgrade script",
ctx->pkg->name->name, ctx->pkg->version);
return r;
}
static int apk_db_install_archive_entry(void *_ctx,
const struct apk_file_info *ae,
{
struct install_ctx *ctx = (struct install_ctx *) _ctx;
struct apk_database *db = ctx->db;
struct apk_package *pkg = ctx->pkg, *opkg;
apk_blob_t name = APK_BLOB_STR(ae->name), bdir, bfile;
struct apk_db_dir_instance *diri = ctx->diri;
struct apk_db_file *file;
char alt_name[PATH_MAX];
const char *p;
apk_sign_ctx_verify_tar(&ctx->sctx, ae, is);
/* Package metainfo and script processing */
if (ae->name[0] == '.') {
/* APK 2.0 format */
type = apk_script_type(&ae->name[1]);
if (type == APK_SCRIPT_INVALID)
return 0;
} else if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
/* APK 1.0 format */
p = &ae->name[11];
if (strncmp(p, pkg->name->name, strlen(pkg->name->name)) != 0)
return 0;
p += strlen(pkg->name->name) + 1;
if (strncmp(p, pkg->version, strlen(pkg->version)) != 0)
return 0;
p += strlen(pkg->version) + 1;
type = apk_script_type(p);
return 0;
/* Handle script */
if (type != APK_SCRIPT_INVALID) {
apk_pkg_add_script(pkg, is, type, ae->size);
if (type == ctx->script)
ctx->script_pending = TRUE;
return apk_db_run_pending_script(ctx);
}
r = apk_db_run_pending_script(ctx);
if (r != 0)
return r;
/* Show progress */
if (ctx->cb) {
size_t size = ctx->installed_size;
if (size > pkg->installed_size)
size = pkg->installed_size;
ctx->cb(ctx->cb_ctx, muldiv(APK_PROGRESS_SCALE, size, pkg->installed_size));
}
ctx->current_file_size = apk_calc_installed_size(ae->size);
if (!S_ISDIR(ae->mode)) {
if (!apk_blob_rsplit(name, '/', &bdir, &bfile))
return 0;
if (bfile.len > 6 && memcmp(bfile.ptr, ".keep_", 6) == 0)
return 0;
/* Make sure the file is part of the cached directory tree */
if (diri == NULL ||
apk_blob_compare(APK_BLOB_PTR_LEN(diri->dir->name,
diri->dir->namelen),
bdir) != 0) {
hlist_for_each_entry(diri, n, &pkg->owned_dirs, pkg_dirs_list) {
if (apk_blob_compare(APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen), bdir) == 0)
break;
}
if (diri == NULL) {
apk_error("%s: File '%*s' entry without directory entry.\n",
pkg->name->name, name.len, name.ptr);
return -1;
}
ctx->diri = diri;
ctx->file_diri_node = hlist_tail_ptr(&diri->owned_files);
file = apk_db_file_query(db, bdir, bfile);
if (file != NULL) {
if (opkg->name != pkg->name) {
if (!(apk_flags & APK_FORCE)) {
pkg->name->name, ae->name,
opkg->name->name);
return -1;
}
apk_warning("%s: Overwriting %s owned by %s.",
pkg->name->name, ae->name,
opkg->name->name);
/* Create the file entry without adding it to hash */
file = apk_db_file_new(diri, bfile, &ctx->file_diri_node);
if (apk_verbosity > 1)
printf("%s\n", ae->name);
/* Extract the file as name.apk-new */
snprintf(alt_name, sizeof(alt_name), "%s/%s.apk-new",
diri->dir->name, file->name);
r = apk_archive_entry_extract(ae, is, alt_name,
extract_cb, ctx);
} else {
if (apk_verbosity > 1)
printf("%s\n", ae->name);
if (name.ptr[name.len-1] == '/')
name.len--;
if (ctx->diri_node == NULL)
ctx->diri_node = hlist_tail_ptr(&pkg->owned_dirs);
ctx->diri = diri = apk_db_diri_new(db, pkg, name,
&ctx->diri_node);
ctx->file_diri_node = hlist_tail_ptr(&diri->owned_files);
apk_db_diri_set(diri, ae->mode & 0777, ae->uid, ae->gid);
}
ctx->installed_size += ctx->current_file_size;
return r;
}
static void apk_db_purge_pkg(struct apk_database *db, struct apk_package *pkg,
const char *exten)
{
struct apk_db_file *file;
struct apk_db_file_hash_key key;
struct hlist_node *dc, *dn, *fc, *fn;
char name[1024];
hlist_for_each_entry_safe(diri, dc, dn, &pkg->owned_dirs, pkg_dirs_list) {
hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) {
snprintf(name, sizeof(name), "%s/%s%s",
diri->dir->name, file->name, exten ?: "");
key = (struct apk_db_file_hash_key) {
.dirname = APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen),
.filename = APK_BLOB_PTR_LEN(file->name, file->namelen),
hash = apk_blob_hash_seed(key.filename, diri->dir->hash);
if (apk_verbosity > 1)
printf("%s\n", name);
__hlist_del(fc, &diri->owned_files.first);
if (exten == NULL) {
apk_hash_delete_hashed(&db->installed.files, APK_BLOB_BUF(&key), hash);
db->installed.stats.files--;
}
__hlist_del(dc, &pkg->owned_dirs.first);
}
apk_pkg_set_state(db, pkg, APK_PKG_NOT_INSTALLED);
}
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
static void apk_db_migrate_files(struct apk_database *db,
struct apk_package *pkg)
{
struct apk_db_dir_instance *diri;
struct apk_db_dir *dir;
struct apk_db_file *file, *ofile;
struct apk_db_file_hash_key key;
struct apk_file_info fi;
struct hlist_node *dc, *dn, *fc, *fn;
unsigned long hash;
char name[1024], tmpname[1024];
hlist_for_each_entry_safe(diri, dc, dn, &pkg->owned_dirs, pkg_dirs_list) {
dir = diri->dir;
hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) {
snprintf(name, sizeof(name), "%s/%s",
diri->dir->name, file->name);
snprintf(tmpname, sizeof(tmpname), "%s/%s.apk-new",
diri->dir->name, file->name);
key = (struct apk_db_file_hash_key) {
.dirname = APK_BLOB_PTR_LEN(dir->name, dir->namelen),
.filename = APK_BLOB_PTR_LEN(file->name, file->namelen),
};
hash = apk_blob_hash_seed(key.filename, dir->hash);
/* check for existing file */
ofile = (struct apk_db_file *) apk_hash_get_hashed(
&db->installed.files, APK_BLOB_BUF(&key), hash);
r = apk_file_get_info(name,
ofile ? ofile->csum.type : APK_CHECKSUM_NONE,
&fi);
if ((diri->dir->flags & APK_DBDIRF_PROTECTED) &&
(r == 0) &&
(ofile == NULL ||
apk_checksum_compare(&ofile->csum, &fi.csum) == 0)) {
/* Protected directory, with file without
* db entry, or local modifications.
*
* Delete the apk-new if it's identical with the
* existing file */
if (ofile == NULL ||
ofile->csum.type != file->csum.type)
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
apk_file_get_info(name, file->csum.type, &fi);
if (apk_checksum_compare(&file->csum, &fi.csum) == 0)
unlink(tmpname);
} else {
/* Overwrite the old file */
rename(tmpname, name);
}
/* Claim ownership of the file in db */
if (ofile != NULL) {
hlist_del(&ofile->diri_files_list,
&diri->owned_files);
apk_hash_delete_hashed(&db->installed.files,
APK_BLOB_BUF(&key), hash);
} else
db->installed.stats.files++;
apk_hash_insert_hashed(&db->installed.files, file, hash);
}
}
}
static int apk_db_unpack_pkg(struct apk_database *db,
struct apk_package *newpkg,
int upgrade, apk_progress_cb cb, void *cb_ctx)
{
struct install_ctx ctx;
int r, i, need_copy = FALSE;
snprintf(pkgname, sizeof(pkgname), "%s-%s.apk",
newpkg->name->name, newpkg->version);
if (newpkg->filename == NULL) {
for (i = 0; i < APK_MAX_REPOS; i++)
if (newpkg->repos & BIT(i))
break;
if (i >= APK_MAX_REPOS) {
apk_error("%s-%s: not present in any repository",
newpkg->name->name, newpkg->version);
return -1;
}
if (apk_db_cache_active(db) &&
repo->csum.type != APK_CHECKSUM_NONE)
bs = apk_db_cache_open(db, &newpkg->csum, pkgname);
bs = apk_bstream_from_file(newpkg->filename);
if (!apk_db_cache_active(db))
need_copy = FALSE;
apk_db_cache_get_name(file, sizeof(file), db, &newpkg->csum,
pkgname, TRUE);
bs = apk_bstream_tee(bs, file);
}
apk_error("%s: %s", file, strerror(errno));
return errno;
ctx = (struct install_ctx) {
.db = db,
.pkg = newpkg,
APK_SCRIPT_PRE_UPGRADE : APK_SCRIPT_PRE_INSTALL,
.cb = cb,
.cb_ctx = cb_ctx,
};
apk_sign_ctx_init(&ctx.sctx, APK_SIGN_VERIFY_IDENTITY, &newpkg->csum);
tar = apk_bstream_gunzip_mpart(bs, apk_sign_ctx_mpart_cb, &ctx.sctx);
r = apk_tar_parse(tar, apk_db_install_archive_entry, &ctx);
apk_sign_ctx_free(&ctx.sctx);
if (r != 0) {
apk_error("%s-%s: package integrity check failed",
newpkg->name->name, newpkg->version);
r = apk_db_run_pending_script(&ctx);
if (r != 0)
goto err;
apk_db_migrate_files(db, newpkg);
char file2[256];
apk_db_cache_get_name(file2, sizeof(file2), db,
&newpkg->csum, pkgname, FALSE);
rename(file, file2);
err:
apk_db_purge_pkg(db, newpkg, ".apk-new");
return r;
}
int apk_db_install_pkg(struct apk_database *db,
struct apk_package *oldpkg,
struct apk_package *newpkg,
apk_progress_cb cb, void *cb_ctx)
{
int r;
if (fchdir(db->root_fd) < 0)
return errno;
/* Just purging? */
if (oldpkg != NULL && newpkg == NULL) {
r = apk_pkg_run_script(oldpkg, db->root_fd,
APK_SCRIPT_PRE_DEINSTALL);
if (r != 0)
return r;
r = apk_pkg_run_script(oldpkg, db->root_fd,
APK_SCRIPT_POST_DEINSTALL);
return r;
}
/* Install the new stuff */
if (!(newpkg->name->flags & APK_NAME_VIRTUAL)) {
r = apk_db_unpack_pkg(db, newpkg, (oldpkg != NULL), cb, cb_ctx);
if (r != 0)
return r;
}
apk_pkg_set_state(db, newpkg, APK_PKG_INSTALLED);
if (oldpkg != NULL)
r = apk_pkg_run_script(newpkg, db->root_fd,
(oldpkg == NULL) ?
APK_SCRIPT_POST_INSTALL : APK_SCRIPT_POST_UPGRADE);
apk_error("%s-%s: Failed to execute post-install/upgrade script",
newpkg->name->name, newpkg->version);
return r;
}