diff --git a/bin/xbps-rindex/defs.h b/bin/xbps-rindex/defs.h index 0f3063bc..8ca90eac 100644 --- a/bin/xbps-rindex/defs.h +++ b/bin/xbps-rindex/defs.h @@ -31,7 +31,7 @@ #define _XBPS_RINDEX "xbps-rindex" /* From index-add.c */ -int index_add(struct xbps_handle *, int, int, char **, bool, const char *); +int index_add(struct xbps_handle *xhp, int argc, char **argv, bool force, const char *compression, xbps_array_t repos); /* From index-clean.c */ int index_clean(struct xbps_handle *, const char *, bool, const char *); @@ -45,8 +45,13 @@ int sign_repo(struct xbps_handle *, const char *, const char *, int sign_pkgs(struct xbps_handle *, int, int, char **, const char *, bool); /* From repoflush.c */ -int repodata_flush(const char *repodir, const char *arch, - xbps_dictionary_t index, xbps_dictionary_t stage, xbps_dictionary_t meta, - const char *compression); +int repodata_write_file(const char *repodir, const char *arch, + xbps_dictionary_t index, xbps_dictionary_t stage, xbps_dictionary_t meta, + const char *compression); +int repodata_write_tmpfile(char *tmp, size_t tmpsz, char *path, size_t pathsz, + const char *repodir, const char *arch, xbps_dictionary_t index, + xbps_dictionary_t stage, xbps_dictionary_t meta, const char *compression); +int repodata_write_fd(int fd, xbps_dictionary_t index, xbps_dictionary_t stage, xbps_dictionary_t meta, + const char *compression); #endif /* !_XBPS_RINDEX_DEFS_H_ */ diff --git a/bin/xbps-rindex/index-add.c b/bin/xbps-rindex/index-add.c index 727053b5..44530c65 100644 --- a/bin/xbps-rindex/index-add.c +++ b/bin/xbps-rindex/index-add.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2012-2015 Juan Romero Pardines. + * Copyright (c) 2025 Duncan Overbruck . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -39,166 +41,217 @@ #include #include "defs.h" +static void __attribute__((__noreturn__)) +unreachable_log_abort(const char *file, size_t line) +{ + xbps_error_printf("%s:%zu: code should not be reached\n", file, line); + abort(); +} + +#define unreachable() unreachable_log_abort(__FILE__, __LINE__) + +struct repo { + char path[PATH_MAX]; + char tmp[PATH_MAX]; + int lockfd; + bool changed; + const char *repodir; + const char *arch; + xbps_dictionary_t index; + xbps_dictionary_t stage; + xbps_dictionary_t meta; + xbps_array_t added; +}; + +struct shared_state { + struct repo *repos; + unsigned int nrepos; + + xbps_dictionary_t old_shlibs; + xbps_dictionary_t used_shlibs; +}; + static int -repodata_commit(const char *repodir, const char *repoarch, - xbps_dictionary_t index, xbps_dictionary_t stage, xbps_dictionary_t meta, - const char *compression) +add_old_shlibs(struct shared_state *state, struct repo *repo) { xbps_object_iterator_t iter; xbps_object_t keysym; - int r; - xbps_dictionary_t oldshlibs, usedshlibs; - if (xbps_dictionary_count(stage) == 0) - return 0; + iter = xbps_dictionary_iterator(repo->stage); + if (!iter) + return xbps_error_oom(); - /* - * Find old shlibs-provides - */ - oldshlibs = xbps_dictionary_create(); - usedshlibs = xbps_dictionary_create(); - - iter = xbps_dictionary_iterator(stage); + // record all shlibs from the old version of the staged package while ((keysym = xbps_object_iterator_next(iter))) { const char *pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); - xbps_dictionary_t pkg = xbps_dictionary_get(index, pkgname); - xbps_array_t pkgshlibs; + xbps_dictionary_t oldpkg; + xbps_array_t oldshlibs; - pkgshlibs = xbps_dictionary_get(pkg, "shlib-provides"); - for (unsigned int i = 0; i < xbps_array_count(pkgshlibs); i++) { + oldpkg = xbps_dictionary_get(repo->index, pkgname); + if (!oldpkg) + continue; + + oldshlibs = xbps_dictionary_get(oldpkg, "shlib-provides"); + for (unsigned int i = 0; i < xbps_array_count(oldshlibs); i++) { const char *shlib = NULL; - xbps_array_get_cstring_nocopy(pkgshlibs, i, &shlib); - xbps_dictionary_set_cstring(oldshlibs, shlib, pkgname); + if (!xbps_array_get_cstring_nocopy(oldshlibs, i, &shlib)) + unreachable(); + if (!xbps_dictionary_set_cstring(state->old_shlibs, shlib, pkgname)) { + xbps_object_iterator_release(iter); + return xbps_error_oom(); + } } } + xbps_object_iterator_reset(iter); + + // remove all shlibs that are still provided by a staged package + while ((keysym = xbps_object_iterator_next(iter))) { + // const char *pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); + xbps_dictionary_t newpkg; + xbps_array_t newshlibs; + + newpkg = xbps_dictionary_get_keysym(repo->stage, keysym); + if (!newpkg) + unreachable(); + + newshlibs = xbps_dictionary_get(newpkg, "shlib-provides"); + for (unsigned int i = 0; i < xbps_array_count(newshlibs); i++) { + const char *shlib = NULL; + if (!xbps_array_get_cstring_nocopy(newshlibs, i, &shlib)) + unreachable(); + xbps_dictionary_remove(state->old_shlibs, shlib); + } + } + xbps_object_iterator_release(iter); + return 0; +} + +static int +dictionary_array_add_cstring(xbps_dictionary_t dict, const char *key, const char *value) +{ + xbps_array_t array; + bool alloc = false; + int r = 0; + + assert(dict); + + array = xbps_dictionary_get(dict, key); + if (!array) { + array = xbps_array_create(); + if (!array) + return xbps_error_oom(); + if (!xbps_dictionary_set(dict, key, array)) { + xbps_object_release(array); + return xbps_error_oom(); + } + alloc = true; + } + if (!xbps_array_add_cstring(array, value)) + r = xbps_error_oom(); + if (alloc) + xbps_object_release(array); + return r; +} + +static int +find_used_staged_shlibs(struct shared_state *state, struct repo *repo) +{ + xbps_object_iterator_t iter; + xbps_object_t keysym; + int r; + + iter = xbps_dictionary_iterator(repo->index); + if (!iter) + return xbps_error_oom(); - /* - * throw away all unused shlibs - */ - iter = xbps_dictionary_iterator(index); while ((keysym = xbps_object_iterator_next(iter))) { const char *pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); - xbps_dictionary_t pkg = xbps_dictionary_get(stage, pkgname); - xbps_array_t pkgshlibs; + xbps_dictionary_t pkg; + xbps_array_t shlibreqs; + + pkg = xbps_dictionary_get(repo->stage, pkgname); if (!pkg) - pkg = xbps_dictionary_get_keysym(index, keysym); - pkgshlibs = xbps_dictionary_get(pkg, "shlib-requires"); + pkg = xbps_dictionary_get_keysym(repo->index, keysym); - for (unsigned int i = 0; i < xbps_array_count(pkgshlibs); i++) { + shlibreqs = xbps_dictionary_get(pkg, "shlib-requires"); + for (unsigned int i = 0; i < xbps_array_count(shlibreqs); i++) { const char *shlib = NULL; - bool alloc = false; - xbps_array_t users; - xbps_array_get_cstring_nocopy(pkgshlibs, i, &shlib); - if (!xbps_dictionary_get(oldshlibs, shlib)) + if (!xbps_array_get_cstring_nocopy(shlibreqs, i, &shlib)) + unreachable(); + if (!xbps_dictionary_get(state->old_shlibs, shlib)) continue; - users = xbps_dictionary_get(usedshlibs, shlib); - if (!users) { - users = xbps_array_create(); - xbps_dictionary_set(usedshlibs, shlib, users); - alloc = true; + r = dictionary_array_add_cstring(state->used_shlibs, shlib, pkgname); + if (r < 0) { + xbps_object_iterator_release(iter); + return r; } - xbps_array_add_cstring(users, pkgname); - if (alloc) - xbps_object_release(users); } } xbps_object_iterator_release(iter); + return 0; +} + +static int UNUSED +purge_satisfied_by_index(struct shared_state *state, struct repo *repo) +{ + xbps_object_iterator_t iter; + xbps_object_t keysym; + + iter = xbps_dictionary_iterator(repo->index); + if (!iter) + return xbps_error_oom(); - /* - * purge all packages that are fullfilled by the index and - * not in the stage. - */ - iter = xbps_dictionary_iterator(index); while ((keysym = xbps_object_iterator_next(iter))) { - xbps_dictionary_t pkg = xbps_dictionary_get_keysym(index, keysym); + xbps_dictionary_t pkg = xbps_dictionary_get_keysym(repo->index, keysym); xbps_array_t pkgshlibs; + const char *pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); - - if (xbps_dictionary_get(stage, - xbps_dictionary_keysym_cstring_nocopy(keysym))) { + if (xbps_dictionary_get(repo->stage, pkgname)) continue; - } pkgshlibs = xbps_dictionary_get(pkg, "shlib-provides"); for (unsigned int i = 0; i < xbps_array_count(pkgshlibs); i++) { const char *shlib = NULL; - xbps_array_get_cstring_nocopy(pkgshlibs, i, &shlib); - xbps_dictionary_remove(usedshlibs, shlib); + if (!xbps_array_get_cstring_nocopy(pkgshlibs, i, &shlib)) + unreachable(); + xbps_dictionary_remove(state->used_shlibs, shlib); } } + xbps_object_iterator_release(iter); + return 0; +} + +static int UNUSED +purge_satisfied_by_stage(struct shared_state *state, struct repo *repo) +{ + xbps_object_iterator_t iter; + xbps_object_t keysym; + + iter = xbps_dictionary_iterator(repo->stage); + if (!iter) + return xbps_error_oom(); - /* - * purge all packages that are fullfilled by the stage - */ - iter = xbps_dictionary_iterator(stage); while ((keysym = xbps_object_iterator_next(iter))) { - xbps_dictionary_t pkg = xbps_dictionary_get_keysym(stage, keysym); + xbps_dictionary_t pkg = xbps_dictionary_get_keysym(repo->stage, keysym); xbps_array_t pkgshlibs; pkgshlibs = xbps_dictionary_get(pkg, "shlib-provides"); for (unsigned int i = 0; i < xbps_array_count(pkgshlibs); i++) { const char *shlib = NULL; - xbps_array_get_cstring_nocopy(pkgshlibs, i, &shlib); - xbps_dictionary_remove(usedshlibs, shlib); - } - } - xbps_object_iterator_release(iter); - - if (xbps_dictionary_count(usedshlibs) != 0) { - printf("Inconsistent shlibs:\n"); - iter = xbps_dictionary_iterator(usedshlibs); - while ((keysym = xbps_object_iterator_next(iter))) { - const char *shlib = xbps_dictionary_keysym_cstring_nocopy(keysym), - *provider = NULL, *pre; - xbps_array_t users = xbps_dictionary_get(usedshlibs, shlib); - xbps_dictionary_get_cstring_nocopy(oldshlibs, shlib, &provider); - - printf(" %s (provided by: %s; used by: ", shlib, provider); - pre = ""; - for (unsigned int i = 0; i < xbps_array_count(users); i++) { - const char *user = NULL; - xbps_array_get_cstring_nocopy(users, i, &user); - printf("%s%s", pre, user); - pre = ", "; - } - printf(")\n"); - } - xbps_object_iterator_release(iter); - iter = xbps_dictionary_iterator(stage); - while ((keysym = xbps_object_iterator_next(iter))) { - xbps_dictionary_t pkg = xbps_dictionary_get_keysym(stage, keysym); - const char *pkgver = NULL, *arch = NULL; - xbps_dictionary_get_cstring_nocopy(pkg, "pkgver", &pkgver); - xbps_dictionary_get_cstring_nocopy(pkg, "architecture", &arch); - printf("stage: added `%s' (%s)\n", pkgver, arch); - } - xbps_object_iterator_release(iter); - } else { - iter = xbps_dictionary_iterator(stage); - while ((keysym = xbps_object_iterator_next(iter))) { - const char *pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); - xbps_dictionary_t pkg = xbps_dictionary_get_keysym(stage, keysym); - const char *pkgver = NULL, *arch = NULL; - xbps_dictionary_get_cstring_nocopy(pkg, "pkgver", &pkgver); - xbps_dictionary_get_cstring_nocopy(pkg, "architecture", &arch); - printf("index: added `%s' (%s).\n", pkgver, arch); - xbps_dictionary_set(index, pkgname, pkg); + if (!xbps_array_get_cstring_nocopy(pkgshlibs, i, &shlib)) + unreachable(); + xbps_dictionary_remove(state->used_shlibs, shlib); } - xbps_object_iterator_release(iter); - stage = NULL; } - r = repodata_flush(repodir, repoarch, index, stage, meta, compression); - xbps_object_release(usedshlibs); - xbps_object_release(oldshlibs); - return r; + xbps_object_iterator_release(iter); + return 0; } static int -index_add_pkg(struct xbps_handle *xhp, xbps_dictionary_t index, xbps_dictionary_t stage, - const char *file, bool force) +repo_add_pkg(struct repo *repo, const char *file, bool force) { char sha256[XBPS_SHA256_SIZE]; char pkgname[XBPS_NAME_SIZE]; @@ -217,15 +270,33 @@ index_add_pkg(struct xbps_handle *xhp, xbps_dictionary_t index, xbps_dictionary_ "`%s', skipping!\n", XBPS_PKGPROPS, file); return 0; } - xbps_dictionary_get_cstring_nocopy(binpkgd, "architecture", &arch); - xbps_dictionary_get_cstring_nocopy(binpkgd, "pkgver", &pkgver); - if (!xbps_pkg_arch_match(xhp, arch, NULL)) { - fprintf(stderr, "index: ignoring %s, unmatched arch (%s)\n", pkgver, arch); - goto out; + + if (!xbps_dictionary_get_cstring_nocopy(binpkgd, "architecture", &arch)) { + xbps_error_printf( + "invalid binary packages: %s: missing property: " + "architecture\n", + file); + return -EINVAL; + } + if (!xbps_dictionary_get_cstring_nocopy(binpkgd, "pkgver", &pkgver)) { + xbps_error_printf( + "invalid binary packages: %s: missing property: pkgver\n", + file); + return -EINVAL; } + + if (strcmp(repo->arch, arch) != 0 && strcmp("noarch", arch) != 0) { + xbps_warn_printf("ignoring %s, unmatched arch (%s)\n", pkgver, arch); + xbps_object_release(binpkgd); + return 0; + } + if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) { - r = -EINVAL; - goto err; + xbps_error_printf( + "invalid binary package: %s: invalid property: pkgver: " + "%s\n", file, pkgver); + xbps_object_release(binpkgd); + return -EINVAL; } /* @@ -234,16 +305,18 @@ index_add_pkg(struct xbps_handle *xhp, xbps_dictionary_t index, xbps_dictionary_ * than current registered package, update the index; otherwise * pass to the next one. */ - curpkgd = xbps_dictionary_get(stage, pkgname); + curpkgd = xbps_dictionary_get(repo->stage, pkgname); if (!curpkgd) - curpkgd = xbps_dictionary_get(index, pkgname); + curpkgd = xbps_dictionary_get(repo->index, pkgname); if (curpkgd && !force) { const char *opkgver = NULL, *oarch = NULL; int cmp; - xbps_dictionary_get_cstring_nocopy(curpkgd, "pkgver", &opkgver); - xbps_dictionary_get_cstring_nocopy(curpkgd, "architecture", &oarch); + if (!xbps_dictionary_get_cstring_nocopy(curpkgd, "pkgver", &opkgver)) + unreachable(); + if (!xbps_dictionary_get_cstring_nocopy(curpkgd, "architecture", &oarch)) + unreachable(); cmp = xbps_cmpver(pkgver, opkgver); if (cmp < 0 && xbps_pkg_reverts(binpkgd, opkgver)) { @@ -260,19 +333,32 @@ index_add_pkg(struct xbps_handle *xhp, xbps_dictionary_t index, xbps_dictionary_ cmp = -1; } if (cmp <= 0) { - fprintf(stderr, "index: skipping `%s' (%s), already registered.\n", pkgver, arch); - goto out; + xbps_warn_printf( + "%s: skipping `%s' (%s), already registered.\n", + repo->repodir, pkgver, arch); + xbps_object_release(binpkgd); + return 0; } } - if (!xbps_file_sha256(sha256, sizeof(sha256), file)) - goto err_errno; - if (!xbps_dictionary_set_cstring(binpkgd, "filename-sha256", sha256)) - goto err_errno; - if (stat(file, &st) == -1) - goto err_errno; - if (!xbps_dictionary_set_uint64(binpkgd, "filename-size", (uint64_t)st.st_size)) - goto err_errno; + if (!xbps_file_sha256(sha256, sizeof(sha256), file)) { + r = -errno; + xbps_error_printf("failed to checksum binary package: %s: %s\n", file, strerror(-r)); + goto err; + } + if (!xbps_dictionary_set_cstring(binpkgd, "filename-sha256", sha256)) { + r = xbps_error_oom(); + goto err; + } + if (stat(file, &st) == -1) { + r = -errno; + xbps_error_printf("failed to stat binary package: %s: %s\n", file, strerror(-r)); + goto err; + } + if (!xbps_dictionary_set_uint64(binpkgd, "filename-size", (uint64_t)st.st_size)) { + r = xbps_error_oom(); + goto err; + } xbps_dictionary_remove(binpkgd, "pkgname"); xbps_dictionary_remove(binpkgd, "version"); @@ -281,86 +367,425 @@ index_add_pkg(struct xbps_handle *xhp, xbps_dictionary_t index, xbps_dictionary_ /* * Add new pkg dictionary into the stage index */ - if (!xbps_dictionary_set(stage, pkgname, binpkgd)) - goto err_errno; + if (!xbps_dictionary_set(repo->stage, pkgname, binpkgd)) { + r = xbps_error_oom(); + goto err; + } -out: + repo->changed = true; + if (!xbps_array_add_cstring(repo->added, pkgname)) { + r = xbps_error_oom(); + goto err; + } + + r = 0; +err: xbps_object_release(binpkgd); + return r; +} + +static const char * +safe_dirname(char *dst, size_t dstsz, const char *path) +{ + if (strlcpy(dst, path, dstsz) >= dstsz) { + errno = ENOBUFS; + return NULL; + } + return dirname(dst); +} + +static int +find_repo(struct repo *repos, unsigned int nrepos, const char *file, struct repo **outp) +{ + char tmp[PATH_MAX]; + const char *dir; + int r; + + dir = safe_dirname(tmp, sizeof(tmp), file); + if (!dir) { + r = -errno; + xbps_error_printf("failed to get directory from path: %s: %s\n", + file, strerror(-r)); + return r; + } + + for (unsigned i = 0; i < nrepos; i++) { + if (strcmp(repos[i].repodir, dir) == 0) { + *outp = &repos[i]; + return 1; + } + } + + *outp = NULL; + xbps_error_printf("repository not found: %s\n", dir); + return -ENOENT; +} + +static int +repo_open(struct xbps_handle *xhp, struct repo *state, const char *repodir, const char *arch) +{ + struct xbps_repo *src; + int r; + + state->changed = false; + state->repodir = repodir; + state->arch = arch; + state->lockfd = xbps_repo_lock(state->repodir, arch); + if (state->lockfd < 0) { + xbps_error_printf( + "failed to lock repository: %s: %s\n", + state->repodir, strerror(-state->lockfd)); + return -state->lockfd; + } + + state->added = xbps_array_create(); + if (!state->added) + return xbps_error_oom(); + + src = xbps_repo_open(xhp, repodir); + if (src) { + state->index = xbps_dictionary_copy_mutable(src->index); + if (!state->index) { + r = xbps_error_oom(); + goto err; + } + state->stage = xbps_dictionary_copy_mutable(src->stage); + if (!state->stage) { + r = xbps_error_oom(); + goto err; + } + if (src->idxmeta) { + state->meta = xbps_dictionary_copy_mutable(src->idxmeta); + if (!state->meta) { + r = xbps_error_oom(); + goto err; + } + } + xbps_repo_release(src); + } else { + if (errno != ENOENT) + return -errno; + state->index = xbps_dictionary_create(); + if (!state->index) + return xbps_error_oom(); + state->stage = xbps_dictionary_create(); + if (!state->stage) + return xbps_error_oom(); + state->meta = NULL; + } return 0; -err_errno: - r = -errno; err: - xbps_object_release(binpkgd); + xbps_repo_release(src); return r; } +static void +repo_state_release(struct repo *repo) +{ + if (repo->index) + xbps_object_release(repo->index); + if (repo->stage) + xbps_object_release(repo->stage); + if (repo->meta) + xbps_object_release(repo->meta); + if (repo->added) + xbps_object_release(repo->added); + xbps_repo_unlock(repo->repodir, repo->arch, repo->lockfd); +} + +static int +print_inconsistent_shlibs(struct shared_state *state) +{ + xbps_object_iterator_t iter; + xbps_object_t keysym; + + iter = xbps_dictionary_iterator(state->used_shlibs); + if (!iter) + return xbps_error_oom(); + + printf("Inconsistent shlibs:\n"); + while ((keysym = xbps_object_iterator_next(iter))) { + const char *shlib = xbps_dictionary_keysym_cstring_nocopy(keysym); + const char *provider = NULL; + xbps_array_t users; + + if (!xbps_dictionary_get_cstring_nocopy(state->old_shlibs, shlib, &provider)) + unreachable(); + + users = xbps_dictionary_get(state->used_shlibs, shlib); + printf(" %s (provided by: %s; used by: ", shlib, provider); + for (unsigned int i = 0; i < xbps_array_count(users); i++) { + const char *user = NULL; + if (!xbps_array_get_cstring_nocopy(users, i, &user)) + unreachable(); + printf("%s%s", i > 0 ? ", " : "", user); + } + printf(")\n"); + } + + xbps_object_iterator_release(iter); + return 0; +} + +static int +print_staged_packages(struct repo *repos, unsigned nrepos) +{ + for (unsigned int i = 0; i < nrepos; i++) { + const struct repo *state = &repos[i]; + + for (unsigned int j = 0; j < xbps_array_count(state->added); j++) { + xbps_dictionary_t pkg; + const char *pkgname = NULL, *pkgver = NULL, *arch = NULL; + if (!xbps_array_get_cstring_nocopy(state->added, j, &pkgname)) + unreachable(); + if (!xbps_dictionary_get_dict(state->stage, pkgname, &pkg)) + unreachable(); + if (!xbps_dictionary_get_cstring_nocopy(pkg, "pkgver", &pkgver)) + unreachable(); + if (!xbps_dictionary_get_cstring_nocopy(pkg, "architecture", &arch)) + unreachable(); + printf("%s: stage: added `%s' (%s).\n", state->repodir, pkgver, arch); + } + } + + return 0; +} + +static int +repo_unstage(struct repo *state) +{ + xbps_object_iterator_t iter; + xbps_object_t keysym; + + if (xbps_dictionary_count(state->stage) == 0) + return 0; + + iter = xbps_dictionary_iterator(state->stage); + if (!iter) + return xbps_error_oom(); + + while ((keysym = xbps_object_iterator_next(iter))) { + const char *pkgver = NULL, *arch = NULL; + const char *pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); + xbps_dictionary_t pkg = xbps_dictionary_get_keysym(state->stage, keysym); + + if (!xbps_dictionary_get_cstring_nocopy(pkg, "pkgver", &pkgver)) + unreachable(); + if (!xbps_dictionary_get_cstring_nocopy(pkg, "architecture", &arch)) + unreachable(); + + printf("%s: index: added `%s' (%s).\n", state->repodir, + pkgver, arch); + + if (!xbps_dictionary_set(state->index, pkgname, pkg)) { + xbps_object_iterator_release(iter); + return xbps_error_oom(); + } + } + xbps_object_iterator_release(iter); + + xbps_object_release(state->stage); + state->stage = NULL; + state->changed = true; + + return 0; +} + +static int +repos_check_stage(struct repo *repos, unsigned nrepos) +{ + struct shared_state state = { + .repos = repos, + .nrepos = nrepos, + }; + int r; + + state.old_shlibs = xbps_dictionary_create(); + if (!state.old_shlibs) + return xbps_error_oom(); + + state.used_shlibs = xbps_dictionary_create(); + if (!state.used_shlibs) { + xbps_object_release(state.old_shlibs); + return xbps_error_oom(); + } + + // collect all the used shared libraries in the stage + for (unsigned i = 0; i < nrepos; i++) { + r = add_old_shlibs(&state, &repos[i]); + if (r < 0) + goto err; + } + for (unsigned i = 0; i < nrepos; i++) { + r = find_used_staged_shlibs(&state, &repos[i]); + if (r < 0) + goto err; + } + + // throw out shared libraries that are already satisfied + for (unsigned i = 0; i < nrepos; i++) { + r = purge_satisfied_by_index(&state, &repos[i]); + if (r < 0) + goto err; + r = purge_satisfied_by_stage(&state, &repos[i]); + if (r < 0) + goto err; + } + + // ... now if there are libraries left, there is an inconsistency + if (xbps_dictionary_count(state.used_shlibs) != 0) { + r = print_inconsistent_shlibs(&state); + if (r < 0) + goto err; + r = print_staged_packages(repos, nrepos); + if (r < 0) + goto err; + } else { + for (unsigned i = 0; i < nrepos; i++) { + r = repo_unstage(&repos[i]); + if (r < 0) + goto err; + } + } + + for (unsigned i = 0; i < nrepos; i++) { + struct repo *repo = &repos[i]; + if (!repo->changed) + continue; + printf("%s: index: %u packages registered.\n", + repo->repodir, xbps_dictionary_count(repo->index)); + if (xbps_dictionary_count(repo->stage) != 0) { + printf("%s: stage: %u packages registered.\n", + repo->repodir, xbps_dictionary_count(repo->stage)); + } + } + + r = 0; +err: + xbps_object_release(state.old_shlibs); + xbps_object_release(state.used_shlibs); + return r; +} + +static xbps_array_t +repos_from_argv(int argc, char **argv) +{ + char tmp[PATH_MAX]; + xbps_array_t res; + + res = xbps_array_create(); + if (!res) { + errno = -xbps_error_oom(); + return NULL; + } + + for (int i = 0; i < argc; i++) { + const char *dir; + int r; + dir = safe_dirname(tmp, sizeof(tmp), argv[0]); + if (!dir) { + r = -errno; + xbps_error_printf( + "failed to get dirname: %s: %s\n", argv[0], strerror(-r)); + xbps_object_release(res); + errno = -r; + return NULL; + } + if (xbps_match_string_in_array(res, dir)) + continue; + if (!xbps_array_add_cstring(res, dir)) { + xbps_object_release(res); + errno = -xbps_error_oom(); + return NULL; + } + } + + return res; +} + int -index_add(struct xbps_handle *xhp, int args, int argc, char **argv, bool force, const char *compression) +index_add(struct xbps_handle *xhp, int argc, char **argv, bool force, const char *compression, xbps_array_t repo_args) { - xbps_dictionary_t index, stage, meta; - struct xbps_repo *repo; - char *tmprepodir = NULL, *repodir = NULL; - int lockfd; + const char *arch = xhp->target_arch ? xhp->target_arch : xhp->native_arch; + struct repo *repos; + unsigned nrepos; int r; - const char *repoarch = xhp->target_arch ? xhp->target_arch : xhp->native_arch; - if ((tmprepodir = strdup(argv[args])) == NULL) - return EXIT_FAILURE; - repodir = dirname(tmprepodir); + // Backwards compatibiltiy if no repo args are given, in this case + // add the repos based on the supplied packages... + if (!repo_args) { + repo_args = repos_from_argv(argc, argv); + if (!repo_args) + return EXIT_FAILURE; + } - lockfd = xbps_repo_lock(repodir, repoarch); - if (lockfd < 0) { - xbps_error_printf("xbps-rindex: cannot lock repository " - "%s: %s\n", repodir, strerror(-lockfd)); - free(tmprepodir); + nrepos = xbps_array_count(repo_args); + repos = calloc(nrepos, sizeof(*repos)); + if (!repos) { + xbps_error_oom(); return EXIT_FAILURE; } - repo = xbps_repo_open(xhp, repodir); - if (!repo && errno != ENOENT) { - free(tmprepodir); - return EXIT_FAILURE; + for (unsigned i = 0; i < nrepos; i++) { + const char *repodir = NULL; + if (!xbps_array_get_cstring_nocopy(repo_args, i, &repodir)) + unreachable(); + r = repo_open(xhp, &repos[i], repodir, arch); + if (r < 0) + goto err; } - if (repo) { - index = xbps_dictionary_copy_mutable(repo->index); - stage = xbps_dictionary_copy_mutable(repo->stage); - meta = xbps_dictionary_copy_mutable(repo->idxmeta); - } else { - index = xbps_dictionary_create(); - stage = xbps_dictionary_create(); - meta = NULL; + for (int i = 0; i < argc; i++) { + struct repo *repo = NULL; + r = find_repo(repos, nrepos, argv[i], &repo); + if (r < 0) + goto err; + r = repo_add_pkg(repo, argv[i], force); + if (r < 0) + goto err; } - for (int i = args; i < argc; i++) { - r = index_add_pkg(xhp, index, stage, argv[i], force); + r = repos_check_stage(repos, nrepos); + if (r < 0) + goto err; + + // write all changed repodata's to tempfiles + for (unsigned i = 0; i < nrepos; i++) { + struct repo *repo = &repos[i]; + if (!repo->changed) + continue; + r = repodata_write_tmpfile(repo->path, sizeof(repo->path), + repo->tmp, sizeof(repo->tmp), repo->repodir, repo->arch, + repo->index, repo->stage, repo->meta, compression); if (r < 0) - goto err2; + goto err; } - r = repodata_commit(repodir, repoarch, index, stage, meta, compression); - if (r < 0) { - xbps_error_printf("failed to write repodata: %s\n", strerror(-r)); - goto err2; + // rename all changed repodata's tempfiles + for (unsigned i = 0; i < nrepos; i++) { + struct repo *repo = &repos[i]; + if (!repo->changed) + continue; + if (rename(repo->tmp, repo->path) == -2) { + xbps_error_printf("failed to rename tempfile: %s: %s: %s\n", + repo->tmp, repo->path, strerror(-errno)); + // XXX: maybe better to abort here, but either way + // we'll end up with inconsistent staging... + } + } - printf("index: %u packages registered.\n", xbps_dictionary_count(index)); - xbps_object_release(index); - xbps_object_release(stage); - if (meta) - xbps_object_release(meta); - xbps_repo_release(repo); - xbps_repo_unlock(repodir, repoarch, lockfd); - free(tmprepodir); + for (unsigned i = 0; i < nrepos; i++) + repo_state_release(&repos[i]); + free(repos); + return EXIT_SUCCESS; -err2: - xbps_object_release(index); - xbps_object_release(stage); - if (meta) - xbps_object_release(meta); - xbps_repo_release(repo); - xbps_repo_unlock(repodir, repoarch, lockfd); - free(tmprepodir); +err: + for (unsigned i = 0; i < nrepos; i++) { + if (repos[i].tmp[0] != 0) + unlink(repos[i].tmp); + repo_state_release(&repos[i]); + } return EXIT_FAILURE; } diff --git a/bin/xbps-rindex/index-clean.c b/bin/xbps-rindex/index-clean.c index 27a105aa..ed0992d4 100644 --- a/bin/xbps-rindex/index-clean.c +++ b/bin/xbps-rindex/index-clean.c @@ -134,7 +134,7 @@ cleanup_repo(struct xbps_handle *xhp, const char *repodir, struct xbps_repo *rep return 0; } - r = repodata_flush(repodir, repoarch, index, stage, repo->idxmeta, compression); + r = repodata_write_file(repodir, repoarch, index, stage, repo->idxmeta, compression); if (r < 0) { xbps_error_printf("failed to write repodata: %s\n", strerror(-r)); xbps_object_release(index); diff --git a/bin/xbps-rindex/main.c b/bin/xbps-rindex/main.c index 0f9ed316..7f945c72 100644 --- a/bin/xbps-rindex/main.c +++ b/bin/xbps-rindex/main.c @@ -30,9 +30,10 @@ #include #include "defs.h" +#include "xbps/xbps_array.h" static void __attribute__((noreturn)) -usage(bool fail) +usage(int status) { fprintf(stdout, "Usage: xbps-rindex [OPTIONS] MODE ARGUMENTS\n\n" @@ -46,19 +47,28 @@ usage(bool fail) " --compression Compression format: none, gzip, bzip2, lz4, xz, zstd (default)\n" " --privkey Path to the private key for signing\n" " --signedby Signature details, i.e \"name \"\n\n" + " -R, --repository Add a local repository\n" "MODE\n" " -a, --add ... Add package(s) to repository index\n" " -c, --clean Clean repository index\n" " -r, --remove-obsoletes Removes obsolete packages from repository\n" " -s, --sign Initialize repository metadata signature\n" " -S, --sign-pkg ... Sign binary package archive\n"); - exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); + exit(status); +} + +static void __attribute__((noreturn)) +multiple_mode_error(void) +{ + xbps_error_printf("only one mode can be specified: add, clean, " + "remove-obsoletes, sign or sign-pkg.\n"); + exit(EXIT_FAILURE); } int main(int argc, char **argv) { - const char *shortopts = "acdfhrsCSVv"; + const char *shortopts = "acdfhrR:sCSVv"; struct option longopts[] = { { "add", no_argument, NULL, 'a' }, { "clean", no_argument, NULL, 'c' }, @@ -74,17 +84,22 @@ main(int argc, char **argv) { "sign-pkg", no_argument, NULL, 'S'}, { "hashcheck", no_argument, NULL, 'C' }, { "compression", required_argument, NULL, 2}, + { "repository", required_argument, NULL, 'R'}, { NULL, 0, NULL, 0 } }; - struct xbps_handle xh; + struct xbps_handle xh = {0}; const char *compression = NULL; const char *privkey = NULL, *signedby = NULL; - int rv, c, flags = 0; - bool add_mode, clean_mode, rm_mode, sign_mode, sign_pkg_mode, force, - hashcheck; - - add_mode = clean_mode = rm_mode = sign_mode = sign_pkg_mode = force = - hashcheck = false; + int rv, c; + xbps_array_t repos = NULL; + enum { + INDEX_ADD = 1, + CLEAN_INDEX, + REMOVE_OBSOLETES, + SIGN_REPO, + SIGN_PACKAGE, + } mode = 0; + bool force = false, hashcheck = false; while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { switch (c) { @@ -98,77 +113,93 @@ main(int argc, char **argv) compression = optarg; break; case 'a': - add_mode = true; + if (mode != 0) + multiple_mode_error(); + mode = INDEX_ADD; break; case 'c': - clean_mode = true; + if (mode != 0) + multiple_mode_error(); + mode = CLEAN_INDEX; break; case 'd': - flags |= XBPS_FLAG_DEBUG; + xh.flags |= XBPS_FLAG_DEBUG; break; case 'f': force = true; break; case 'h': - usage(false); + usage(EXIT_SUCCESS); /* NOTREACHED */ case 'r': - rm_mode = true; + if (mode != 0) + multiple_mode_error(); + mode = REMOVE_OBSOLETES; break; case 's': - sign_mode = true; + if (mode != 0) + multiple_mode_error(); + mode = SIGN_REPO; break; case 'C': hashcheck = true; break; + case 'R': + if (!repos) + repos = xbps_array_create(); + if (!repos || !xbps_array_add_cstring(repos, optarg)) { + xbps_error_oom(); + exit(1); + } + break; case 'S': - sign_pkg_mode = true; + if (mode != 0) + multiple_mode_error(); + mode = SIGN_PACKAGE; break; case 'v': - flags |= XBPS_FLAG_VERBOSE; + xh.flags |= XBPS_FLAG_VERBOSE; break; case 'V': printf("%s\n", XBPS_RELVER); exit(EXIT_SUCCESS); case '?': default: - usage(true); + usage(EXIT_FAILURE); /* NOTREACHED */ } } - if ((argc == optind) || - (!add_mode && !clean_mode && !rm_mode && !sign_mode && !sign_pkg_mode)) { - usage(true); + + if ((argc == optind) || mode == 0) { + usage(EXIT_FAILURE); /* NOTREACHED */ - } else if ((add_mode && (clean_mode || rm_mode || sign_mode || sign_pkg_mode)) || - (clean_mode && (add_mode || rm_mode || sign_mode || sign_pkg_mode)) || - (rm_mode && (add_mode || clean_mode || sign_mode || sign_pkg_mode)) || - (sign_mode && (add_mode || clean_mode || rm_mode || sign_pkg_mode)) || - (sign_pkg_mode && (add_mode || clean_mode || rm_mode || sign_mode))) { - xbps_error_printf("Only one mode can be specified: add, clean, " - "remove-obsoletes, sign or sign-pkg.\n"); - exit(EXIT_FAILURE); } /* initialize libxbps */ - memset(&xh, 0, sizeof(xh)); - xh.flags = flags; if ((rv = xbps_init(&xh)) != 0) { xbps_error_printf("failed to initialize libxbps: %s\n", strerror(rv)); exit(EXIT_FAILURE); } - if (add_mode) - rv = index_add(&xh, optind, argc, argv, force, compression); - else if (clean_mode) + switch (mode) { + case INDEX_ADD: + rv = index_add(&xh, argc - optind, argv + optind, force, + compression, repos); + break; + case CLEAN_INDEX: rv = index_clean(&xh, argv[optind], hashcheck, compression); - else if (rm_mode) + break; + case REMOVE_OBSOLETES: rv = remove_obsoletes(&xh, argv[optind]); - else if (sign_mode) + break; + case SIGN_REPO: rv = sign_repo(&xh, argv[optind], privkey, signedby, compression); - else if (sign_pkg_mode) + break; + case SIGN_PACKAGE: rv = sign_pkgs(&xh, optind, argc, argv, privkey, force); + break; + } exit(rv); } diff --git a/bin/xbps-rindex/repoflush.c b/bin/xbps-rindex/repoflush.c index 3bdd474c..a1d3ba90 100644 --- a/bin/xbps-rindex/repoflush.c +++ b/bin/xbps-rindex/repoflush.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2013-2019 Juan Romero Pardines. - * Copyright (c) 2023 Duncan Overbruck . + * Copyright (c) 2023-2025 Duncan Overbruck . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -131,48 +131,17 @@ archive_dict(struct archive *ar, const char *filename, xbps_dictionary_t dict) } int -repodata_flush(const char *repodir, - const char *arch, - xbps_dictionary_t index, - xbps_dictionary_t stage, - xbps_dictionary_t meta, - const char *compression) +repodata_write_fd(int fd, xbps_dictionary_t index, xbps_dictionary_t stage, + xbps_dictionary_t meta, const char *compression) { - char path[PATH_MAX]; - char tmp[PATH_MAX]; - struct archive *ar = NULL; - mode_t prevumask; + struct archive *ar; int r; - int fd; - - r = snprintf(path, sizeof(path), "%s/%s-repodata", repodir, arch); - if (r < 0 || (size_t)r >= sizeof(tmp)) { - xbps_error_printf("repodata path too long: %s: %s\n", path, - strerror(ENAMETOOLONG)); - return -ENAMETOOLONG; - } - - r = snprintf(tmp, sizeof(tmp), "%s.XXXXXXX", path); - if (r < 0 || (size_t)r >= sizeof(tmp)) { - xbps_error_printf("repodata tmp path too long: %s: %s\n", path, - strerror(ENAMETOOLONG)); - return -ENAMETOOLONG; - } - - prevumask = umask(S_IXUSR|S_IRWXG|S_IRWXO); - fd = mkstemp(tmp); - if (fd == -1) { - r = -errno; - xbps_error_printf("failed to open temp file: %s: %s", tmp, strerror(-r)); - umask(prevumask); - goto err; - } - umask(prevumask); ar = open_archive(fd, compression); if (!ar) { r = -errno; - goto err; + xbps_error_printf("failed to open archive: %s\n", strerror(-r)); + return r; } r = archive_dict(ar, XBPS_REPODATA_INDEX, index); @@ -185,18 +154,19 @@ repodata_flush(const char *repodir, if (r < 0) goto err; - /* Write data to tempfile and rename */ if (archive_write_close(ar) == ARCHIVE_FATAL) { r = -archive_errno(ar); - if (r == 1) - r = -EINVAL; - xbps_error_printf("failed to close archive: %s\n", archive_error_string(ar)); - goto err; + xbps_error_printf( + "failed to close archive: %s\n", archive_error_string(ar)); + archive_write_free(ar); + return r; } if (archive_write_free(ar) == ARCHIVE_FATAL) { r = -errno; - xbps_error_printf("failed to free archive: %s\n", strerror(-r)); - goto err; + xbps_error_printf( + "failed to free archive: %s\n", strerror(-r)); + archive_write_free(ar); + return r; } #ifdef HAVE_FDATASYNC @@ -204,6 +174,51 @@ repodata_flush(const char *repodir, #else fsync(fd); #endif + return 0; + +err: + archive_write_free(ar); + return r; +} + +int +repodata_write_tmpfile(char *path, size_t pathsz, char *tmp, size_t tmpsz, + const char *repodir, const char *arch, xbps_dictionary_t index, + xbps_dictionary_t stage, xbps_dictionary_t meta, const char *compression) +{ + mode_t prevumask; + int fd; + int r; + + r = snprintf(path, pathsz, "%s/%s-repodata", repodir, arch); + if (r < 0 || (size_t)r >= pathsz) { + xbps_error_printf("repodata path too long: %s: %s\n", path, + strerror(ENAMETOOLONG)); + return -ENAMETOOLONG; + } + + r = snprintf(tmp, tmpsz, "%s.XXXXXXX", path); + if (r < 0 || (size_t)r >= tmpsz) { + xbps_error_printf("repodata tmp path too long: %s: %s\n", path, + strerror(ENAMETOOLONG)); + return -ENAMETOOLONG; + } + + prevumask = umask(S_IXUSR|S_IRWXG|S_IRWXO); + + fd = mkstemp(tmp); + if (fd == -1) { + r = -errno; + xbps_error_printf("failed to open temp file: %s: %s", tmp, strerror(-r)); + umask(prevumask); + goto err; + } + + umask(prevumask); + + r = repodata_write_fd(fd, index, stage, meta, compression); + if (r < 0) + goto err; if (fchmod(fd, 0664) == -1) { errno = -r; @@ -215,6 +230,28 @@ repodata_flush(const char *repodir, } close(fd); + return 0; +err: + if (fd != -1) + close(fd); + unlink(tmp); + return r; +} + +int +repodata_write_file(const char *repodir, const char *arch, + xbps_dictionary_t index, xbps_dictionary_t stage, xbps_dictionary_t meta, + const char *compression) +{ + char path[PATH_MAX]; + char tmp[PATH_MAX]; + int r; + + r = repodata_write_tmpfile(path, sizeof(path), tmp, sizeof(tmp), repodir, + arch, index, stage, meta, compression); + if (r < 0) + return r; + if (rename(tmp, path) == -1) { r = -errno; xbps_error_printf("failed to rename repodata: %s: %s: %s\n", @@ -222,15 +259,6 @@ repodata_flush(const char *repodir, unlink(tmp); return r; } - return 0; -err: - if (ar) { - archive_write_close(ar); - archive_write_free(ar); - } - if (fd != -1) - close(fd); - unlink(tmp); - return r; + return 0; } diff --git a/bin/xbps-rindex/sign.c b/bin/xbps-rindex/sign.c index 4acb203b..35797e42 100644 --- a/bin/xbps-rindex/sign.c +++ b/bin/xbps-rindex/sign.c @@ -230,7 +230,7 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, xbps_error_printf("cannot lock repository: %s\n", strerror(errno)); goto out; } - r = repodata_flush(repodir, repoarch, repo->index, repo->stage, meta, compression); + r = repodata_write_file(repodir, repoarch, repo->index, repo->stage, meta, compression); xbps_repo_unlock(repodir, repoarch, lockfd); if (r < 0) { xbps_error_printf("failed to write repodata: %s\n", strerror(errno)); diff --git a/tests/xbps/xbps-rindex/add_test.sh b/tests/xbps/xbps-rindex/add_test.sh index 6fa99b5e..2307fb89 100644 --- a/tests/xbps/xbps-rindex/add_test.sh +++ b/tests/xbps/xbps-rindex/add_test.sh @@ -60,39 +60,38 @@ stage_head() { stage_body() { mkdir -p some_repo pkg_A pkg_B touch pkg_A/file00 pkg_B/file01 + cd some_repo - xbps-create -A noarch -n foo-1.0_1 -s "foo pkg" --shlib-provides "libfoo.so.1" ../pkg_A - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -- xbps-create -A noarch -n foo-1.0_1 -s "foo pkg" --shlib-provides "libfoo.so.1" ../pkg_A + + atf_check -e ignore -o match:"index: added \`foo-1.0_1'" -- \ + xbps-rindex -d -a $PWD/*.xbps + atf_check -o inline:" 1 $PWD (RSA unsigned)\n" -- \ xbps-query -r ../root -i --repository=$PWD -L - xbps-create -A noarch -n foo-1.1_1 -s "foo pkg" --shlib-provides "libfoo.so.2" ../pkg_A - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -- xbps-create -A noarch -n foo-1.1_1 -s "foo pkg" --shlib-provides "libfoo.so.2" ../pkg_A + atf_check -e ignore -o match:"index: added \`foo-1.1_1'" -- xbps-rindex -d -a $PWD/*.xbps atf_check -o inline:" 1 $PWD (RSA unsigned)\n" -- \ xbps-query -r ../root -i --repository=$PWD -L - xbps-create -A noarch -n bar-1.0_1 -s "foo pkg" --shlib-requires "libfoo.so.2" ../pkg_B - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -- xbps-create -A noarch -n bar-1.0_1 -s "foo pkg" --shlib-requires "libfoo.so.2" ../pkg_B + atf_check -e ignore -o match:"index: added \`bar-1.0_1'" -- \ + xbps-rindex -d -a $PWD/*.xbps atf_check -o inline:" 2 $PWD (RSA unsigned)\n" -- \ xbps-query -r ../root -i --repository=$PWD -L - xbps-create -A noarch -n foo-1.2_1 -s "foo pkg" --shlib-provides "libfoo.so.3" ../pkg_A - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -- xbps-create -A noarch -n foo-1.2_1 -s "foo pkg" --shlib-provides "libfoo.so.3" ../pkg_A + atf_check -e ignore -o not-match:"index: added \`foo-1.2_1'" -- \ + xbps-rindex -d -a $PWD/*.xbps atf_check -o inline:" 2 $PWD (Staged) (RSA unsigned)\n" -- \ xbps-query -r ../root -i --repository=$PWD -L - xbps-create -A noarch -n bar-1.1_1 -s "foo pkg" --shlib-requires "libfoo.so.3" ../pkg_A - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -- xbps-create -A noarch -n bar-1.1_1 -s "foo pkg" --shlib-requires "libfoo.so.3" ../pkg_A + atf_check -e ignore \ + -o match:"added \`bar-1.1_1'" \ + -o match:"added \`foo-1.2_1'" \ + -- xbps-rindex -d -a $PWD/*.xbps atf_check -o inline:" 2 $PWD (RSA unsigned)\n" -- \ xbps-query -r ../root -i --repository=$PWD -L } @@ -111,47 +110,52 @@ stage_resolve_bug_body() { cd some_repo # first add the provider and the requirer to the repo - xbps-create -A noarch -n provider-1.0_1 -s "foo pkg" --shlib-provides "libfoo.so.1 libbar.so.1" ../provider - atf_check_equal $? 0 - xbps-create -A noarch -n require-1.0_1 -s "foo pkg" --shlib-requires "libfoo.so.1" ../requirer - atf_check_equal $? 0 - xbps-create -A noarch -n stage-trigger-1.0_1 -s "foo pkg" --shlib-requires "libbar.so.1" ../stage-trigger - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -- xbps-create -A noarch -n provider-1.0_1 -s "foo pkg" --shlib-provides "libfoo.so.1 libbar.so.1" ../provider + atf_check -o ignore -- xbps-create -A noarch -n require-1.0_1 -s "foo pkg" --shlib-requires "libfoo.so.1" ../requirer + atf_check -o ignore -- xbps-create -A noarch -n stage-trigger-1.0_1 -s "foo pkg" --shlib-requires "libbar.so.1" ../stage-trigger + atf_check \ + -e ignore \ + -o match:"some_repo: index: added \`provider-1\.0_1' \(noarch\)\." \ + -o match:"some_repo: index: added \`require-1\.0_1' \(noarch\)\." \ + -o match:"some_repo: index: added \`stage-trigger-1\.0_1' \(noarch\)\." \ + -- xbps-rindex -a $PWD/*.xbps # then add libprovider that also provides the library - xbps-create -A noarch -n libprovider-1.0_2 -s "foo pkg" --shlib-provides "libfoo.so.1" ../libprovider - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -- xbps-create -A noarch -n libprovider-1.0_2 -s "foo pkg" --shlib-provides "libfoo.so.1" ../libprovider + atf_check \ + -e ignore \ + -o match:"some_repo: index: added \`libprovider-1\.0_2' \(noarch\)\." \ + -- xbps-rindex -a $PWD/*.xbps atf_check -o inline:" 4 $PWD (RSA unsigned)\n" -- \ xbps-query -r ../root -i --repository=$PWD -L # trigger staging - xbps-create -A noarch -n provider-1.0_2 -s "foo pkg" --shlib-provides "libfoo.so.1" ../provider - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -- xbps-create -A noarch -n provider-1.0_2 -s "foo pkg" --shlib-provides "libfoo.so.1" ../provider + atf_check \ + -e ignore \ + -o match:"some_repo: stage: added \`provider-1\.0_2' \(noarch\)\." \ + -- xbps-rindex -a $PWD/*.xbps atf_check -o inline:" 4 $PWD (Staged) (RSA unsigned)\n" -- \ xbps-query -r ../root -i --repository=$PWD -L # then add a new provider not containing the provides field. This resulted in # a stage state despites the library is resolved through libprovides - xbps-create -A noarch -n provider-1.0_3 -s "foo pkg" ../provider - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -- xbps-create -A noarch -n provider-1.0_3 -s "foo pkg" ../provider + atf_check \ + -e ignore \ + -o match:"some_repo: stage: added \`provider-1\.0_3' \(noarch\)\." \ + -- xbps-rindex -a $PWD/*.xbps atf_check -o inline:" 4 $PWD (Staged) (RSA unsigned)\n" -- \ xbps-query -r ../root -i --repository=$PWD -L # resolve staging # the actual bug appeared here: libfoo.so.1 is still provided by libprovider, but # xbps-rindex fails to register that. - xbps-create -A noarch -n stage-trigger-1.0_2 -s "foo pkg" ../stage-trigger - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -- xbps-create -A noarch -n stage-trigger-1.0_2 -s "foo pkg" ../stage-trigger + atf_check \ + -e ignore \ + -o match:"some_repo: index: added \`stage-trigger-1\.0_2' \(noarch\)\." \ + -- xbps-rindex -a $PWD/*.xbps atf_check -o inline:" 4 $PWD (RSA unsigned)\n" -- \ xbps-query -r ../root -i --repository=$PWD -L } @@ -194,10 +198,163 @@ stage_stacked_body() { } +atf_test_case stage_multi_repos + +stage_multi_repos_head() { + atf_set "descr" "xbps-rindex(1) -a: staging multiple repositories" +} + +stage_multi_repos_body() { + mkdir -p repo1 repo2 root pkg + + cd repo1 + atf_check -o ignore -- xbps-create -A noarch -n flac-1.4.3_1 -s "flac pkg" --shlib-provides "libFLAC.so.12" ../pkg + atf_check -o ignore -- xbps-create -A noarch -n ruby-3.3.8_1 -s "ruby pkg" --shlib-provides "libruby.so.3.3" ../pkg + atf_check -o ignore -- xbps-create -A noarch -n A-1.0_1 -s "A pkg" --shlib-requires "libFLAC.so.12" ../pkg + atf_check -o ignore -- xbps-create -A noarch -n B-1.0_1 -s "B pkg" --shlib-requires "libruby.so.3.3" ../pkg + atf_check -o ignore -- xbps-create -A noarch -n unaffected-1.0_1 -s "unaffected pkg" --shlib-provides "libfoo.so.1" ../pkg + cd .. + + cd repo2 + atf_check -o ignore -- xbps-create -A noarch -n AA-1.0_1 -s "AA pkg" --shlib-requires "libFLAC.so.12" ../pkg + atf_check -o ignore -- xbps-create -A noarch -n BB-1.0_1 -s "BB pkg" --shlib-requires "libruby.so.3.3" ../pkg + atf_check -o ignore -- xbps-create -A noarch -n other-unaffected-1.0_1 -s "other unaffected pkg" --shlib-requires "libfoo.so.1" ../pkg + cd .. + + atf_check -e ignore \ + -o match:"index: added \`A-1.0_1'" \ + -o match:"index: added \`B-1.0_1'" \ + -o match:"index: added \`flac-1.4.3_1'" \ + -o match:"index: added \`ruby-3.3.8_1'" \ + -- xbps-rindex -v -R repo1 -R repo2 -a \ + repo1/flac-1.4.3_1.noarch.xbps \ + repo1/ruby-3.3.8_1.noarch.xbps \ + repo1/A-1.0_1.noarch.xbps \ + repo1/B-1.0_1.noarch.xbps \ + repo1/unaffected-1.0_1.noarch.xbps + atf_check -e ignore \ + -o match:"index: added \`AA-1.0_1'" \ + -o match:"index: added \`BB-1.0_1'" \ + -- xbps-rindex -v -R repo1 -R repo2 -a \ + repo2/AA-1.0_1.noarch.xbps \ + repo2/BB-1.0_1.noarch.xbps \ + repo2/other-unaffected-1.0_1.noarch.xbps + + cd repo1 + atf_check -o ignore -- xbps-create -A noarch -n flac-1.5.0_1 -s "flac pkg" --shlib-provides "libFLAC.so.14" ../pkg + cd .. + + atf_check -e ignore -o match:"repo1: stage: added \`flac-1.5.0_1'" \ + -- xbps-rindex -v -R repo1 -R repo2 -a repo1/flac-1.5.0_1.noarch.xbps + + atf_check \ + -o match:"repo1 \(Staged\)" \ + -o not-match:"repo2 \(Staged\)" \ + -- xbps-query -r ../root -i --repository=repo1 --repository=repo2 -L + + cd repo2 + atf_check -o ignore -- xbps-create -A noarch -n AA-1.0_2 -s "AA pkg" --shlib-requires "libFLAC.so.14" ../pkg + cd .. + + atf_check -o match:"repo2: stage: added \`AA-1.0_2'" \ + -- xbps-rindex -v -R repo1 -R repo2 -a repo2/AA-1.0_2.noarch.xbps + + atf_check \ + -o match:"repo1 \(Staged\)" \ + -o match:"repo2 \(Staged\)" \ + -- xbps-query -r ../root -i --repository=repo1 --repository=repo2 -L + + cd repo1 + atf_check -o ignore -- xbps-create -A noarch -n ruby-3.4.5_1 -s "ruby pkg" --shlib-provides "libruby.so.3.4" ../pkg + cd .. + atf_check -o match:"repo1: stage: added \`ruby-3.4.5_1'" \ + -- xbps-rindex -v -R repo1 -R repo2 -a repo1/ruby-3.4.5_1.noarch.xbps + + atf_check \ + -o match:"repo1 \(Staged\)" \ + -o match:"repo2 \(Staged\)" \ + -- xbps-query -r ../root -i --repository=repo1 --repository=repo2 -L + + cd repo2 + atf_check -o ignore -- xbps-create -A noarch -n BB-1.0_2 -s "BB pkg" --shlib-requires "libruby.so.3.4" ../pkg + cd .. + atf_check -o match:"repo2: stage: added \`BB-1.0_2'" \ + -- xbps-rindex -v -R repo1 -R repo2 -a repo2/BB-1.0_2.noarch.xbps + + cd repo1 + atf_check -o ignore -- xbps-create -A noarch -n A-1.0_2 -s "A pkg" --shlib-requires "libFLAC.so.14" ../pkg + atf_check -o ignore -- xbps-create -A noarch -n B-1.0_2 -s "B pkg" --shlib-requires "libruby.so.3.4" ../pkg + cd .. + atf_check \ + -o match:"repo1: stage: added \`A-1.0_2'" \ + -- xbps-rindex -v -R repo1 -R repo2 -a repo1/A-1.0_2.noarch.xbps + + atf_check \ + -o match:"repo1 \(Staged\)" \ + -o match:"repo2 \(Staged\)" \ + -- xbps-query -r ../root -i --repository=repo1 --repository=repo2 -L + + atf_check \ + -o match:"repo1: index: added \`A-1\.0_2'" \ + -o match:"repo1: index: added \`B-1\.0_2'" \ + -o match:"repo1: index: added \`flac-1\.5\.0_1'" \ + -o match:"repo1: index: added \`ruby-3\.4\.5_1'" \ + -o match:"repo2: index: added \`AA-1\.0_2'" \ + -o match:"repo2: index: added \`BB-1\.0_2'" \ + -- xbps-rindex -v -R repo1 -R repo2 -a repo1/B-1.0_2.noarch.xbps + + atf_check \ + -o not-match:"repo1 \(Staged\)" \ + -o not-match:"repo2 \(Staged\)" \ + -- xbps-query -r ../root -i --repository=repo1 --repository=repo2 -L +} + +atf_test_case stage_dependency + +stage_dependency_head() { + atf_set "descr" "xbps-rindex(1) -a: stage repository due to broken dependency" +} + +stage_dependency_body() { + atf_expect_fail "not implemented" + mkdir -p repo1 repo2 repo3 repo4 root pkg + + cd repo1 + atf_check -o ignore -- xbps-create -A noarch -n A-1.0_1 -s "A pkg" --dependencies "B<2.0_1" ../pkg + atf_check -o ignore -- xbps-create -A noarch -n B-1.0_1 -s "B pkg" ../pkg + atf_check -o ignore -- xbps-create -A noarch -n unaffected-1.0_1 -s "unaffected pkg" --dependencies "B>=1.0_1" ../pkg + cd .. + + atf_check \ + -e ignore \ + -o match:"index: added \`A-1\.0_1'" \ + -o match:"index: added \`B-1\.0_1'" \ + -o match:"index: added \`unaffected-1\.0_1'" \ + -- xbps-rindex -v -R repo1 -a \ + repo1/A-1.0_1.noarch.xbps \ + repo1/B-1.0_1.noarch.xbps \ + repo1/unaffected-1.0_1.noarch.xbps + + cd repo1 + atf_check -o ignore -- xbps-create -A noarch -n B-2.0_1 -s "B pkg" ../pkg + cd .. + + atf_check \ + -e ignore \ + -o match:"repo1: stage: added \`B-2\.0_1'" \ + -- xbps-rindex -v -R repo1 -R repo2 -R repo3 -R repo4 -a repo1/B-2.0_1.noarch.xbps + + atf_check \ + -o match:"repo1 \(Staged\)" \ + -- xbps-query -r ../root -i --repository=repo1 --repository=repo2 -L +} + atf_init_test_cases() { atf_add_test_case update atf_add_test_case revert atf_add_test_case stage atf_add_test_case stage_resolve_bug atf_add_test_case stage_stacked + atf_add_test_case stage_multi_repos + atf_add_test_case stage_dependency }