diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:12:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:12:14 +0000 |
commit | 4b8a0f3f3dcf60dac2ce308ea08d413a535af29f (patch) | |
tree | 0f09c0ad2a4d0f535d89040a63dc3a866a6606e6 /archallflood.c | |
parent | Initial commit. (diff) | |
download | reprepro-upstream/5.4.4.tar.xz reprepro-upstream/5.4.4.zip |
Adding upstream version 5.4.4.upstream/5.4.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | archallflood.c | 714 |
1 files changed, 714 insertions, 0 deletions
diff --git a/archallflood.c b/archallflood.c new file mode 100644 index 0000000..df20247 --- /dev/null +++ b/archallflood.c @@ -0,0 +1,714 @@ +/* This file is part of "reprepro" + * Copyright (C) 2009,2016 Bernhard R. Link + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301 USA + */ +#include <config.h> + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> + +#include "error.h" +#include "strlist.h" +#include "indexfile.h" +#include "dpkgversions.h" +#include "target.h" +#include "distribution.h" +#include "tracking.h" +#include "files.h" +#include "package.h" +#include "archallflood.h" + +struct aa_source_name { + /*@null@*/struct aa_source_name *parent; + /*@null@*/struct aa_source_name *left_child; + /*@null@*/struct aa_source_name *right_child; + + char *name; + + /*@null@*/struct aa_source_version *versions; +}; + +struct aa_source_version { + /*@null@*/struct aa_source_version *next; + struct aa_source_name *name; + char *version; + + /* if true, it was already verified that there is no + * binary package of the same source version already there, + * so new architecture 'all' can be added without danger */ + bool has_no_sibling; + /* if true, then there is a binary package of this source + * package, so replacing an architecture all is only allowed + * if there is already a binary for the new one */ + bool has_sibling; +}; + +struct aa_package_data { + struct aa_package_data *next; + /* the name of the architecture all package: */ + char *name; + + /* NULL if does not exists/not yet known */ + /*@null@*/char *old_version; + /*@null@*/struct aa_source_version *old_source; + /*@null@*/char *new_version; + /*@null@*/struct aa_source_version *new_source; + bool new_has_sibling; + + struct checksumsarray new_origfiles; + struct strlist new_filekeys; + char *new_control; +}; + +struct floodlist { + /*@dependent@*/struct target *target; + struct aa_source_name *sources; + struct aa_package_data *list; + /* package the next package will most probably be after. + * (NULL=before start of list) */ + /*@null@*//*@dependent@*/struct aa_package_data *last; +}; + +static void aa_package_data_free(/*@only@*/struct aa_package_data *data){ + if (data == NULL) + return; + free(data->name); + free(data->old_version); + free(data->new_version); + free(data->new_control); + strlist_done(&data->new_filekeys); + checksumsarray_done(&data->new_origfiles); + free(data); +} + +static void floodlist_free(struct floodlist *list) { + struct aa_source_name *s; + struct aa_package_data *l; + + if (list == NULL) + return; + + l = list->list; + while (l != NULL) { + struct aa_package_data *n = l->next; + aa_package_data_free(l); + l = n; + } + s = list->sources; + while (s != NULL) { + struct aa_source_name *n; + + while (s->left_child != NULL || s->right_child != NULL) { + if (s->left_child != NULL) { + n = s->left_child; + s->left_child = NULL; + s = n; + } else { + n = s->right_child; + s->right_child = NULL; + s = n; + } + } + + while (s->versions != NULL) { + struct aa_source_version *nv; + nv = s->versions->next; + free(s->versions->version); + free(s->versions); + s->versions = nv; + } + n = s->parent; + free(s->name); + free(s); + s = n; + } + free(list); + return; +} + +static retvalue find_or_add_sourcename(struct floodlist *list, struct package *pkg, /*@out@*/struct aa_source_name **src_p) { + struct aa_source_name *parent, **p, *n; + int c; + + parent = NULL; + p = &list->sources; + + /* if this gets too slow, make it a balanced tree, + * but it seems fast enough even as simple tree */ + + while (*p != NULL) { + c = strcmp(pkg->source, (*p)->name); + if (c == 0) + break; + parent = *p; + if (c > 0) + p = &parent->right_child; + else + p = &parent->left_child; + } + if (*p == NULL) { + /* there is not even something with this name */ + n = zNEW(struct aa_source_name); + if (FAILEDTOALLOC(n)) { + return RET_ERROR_OOM; + } + n->name = strdup(pkg->source); + if (FAILEDTOALLOC(n->name)) { + free(n); + return RET_ERROR_OOM; + } + n->parent = parent; + *p = n; + *src_p = n; + return RET_OK; + } + *src_p = *p; + return RET_OK; +} + +static retvalue find_or_add_source(struct floodlist *list, struct package *pkg, /*@out@*/struct aa_source_version **src_p) { + retvalue r; + struct aa_source_name *sn; + struct aa_source_version **p, *n; + int c; + + r = find_or_add_sourcename(list, pkg, &sn); + if (RET_WAS_ERROR(r)) + return r; + + /* source name found (or created), now look for version: */ + + p = &sn->versions; + c = -1; + while (*p != NULL && (c = strcmp(pkg->sourceversion, + (*p)->version)) > 0) { + p = &(*p)->next; + } + if (c == 0) { + assert (*p != NULL); + *src_p = *p; + return RET_OK; + } + n = zNEW(struct aa_source_version); + if (FAILEDTOALLOC(n)) { + return RET_ERROR_OOM; + } + n->name = sn; + n->version = strdup(pkg->sourceversion); + if (FAILEDTOALLOC(n->version)) { + free(n); + return RET_ERROR_OOM; + } + n->next = *p; + *p = n; + *src_p = n; + return RET_OK; +} + +static struct aa_source_version *find_source(struct floodlist *list, const char *source, const char *sourceversion) { + struct aa_source_name *p; + struct aa_source_version *v; + int c = -1; + + p = list->sources; + + while (p != NULL) { + c = strcmp(source, p->name); + if (c == 0) + break; + if (c > 0) + p = p->right_child; + else + p = p->left_child; + } + if (p == NULL) + return NULL; + v = p->versions; + while (v != NULL && (c = strcmp(sourceversion, v->version)) > 0) + v = v->next; + if (c < 0) + return NULL; + else + return v; +} + +/* Before anything else is done the current state of one target is read into + * the list: list->list points to the first in the sorted list, + * list->last to the last one inserted */ +static retvalue save_package_version(struct floodlist *list, struct package *pkg) { + struct aa_source_version *src; + retvalue r; + struct aa_package_data *package; + + r = package_getarchitecture(pkg); + if (RET_WAS_ERROR(r)) + return r; + + r = package_getsource(pkg); + if (RET_WAS_ERROR(r)) + return r; + + r = find_or_add_source(list, pkg, &src); + if (RET_WAS_ERROR(r)) + return r; + + if (pkg->architecture != architecture_all) { + src->has_sibling = true; + return RET_NOTHING; + } + + r = package_getversion(pkg); + if (RET_WAS_ERROR(r)) + return r; + + package = zNEW(struct aa_package_data); + if (FAILEDTOALLOC(package)) { + return RET_ERROR_OOM; + } + + package->name = strdup(pkg->name); + if (FAILEDTOALLOC(package->name)) { + free(package); + return RET_ERROR_OOM; + } + package->old_version = package_dupversion(pkg); + if (FAILEDTOALLOC(package->old_version)) { + free(package->name); + free(package); + return RET_ERROR_OOM; + } + package->old_source = src; + + if (list->list == NULL) { + /* first chunk to add: */ + list->list = package; + list->last = package; + } else { + if (strcmp(pkg->name, list->last->name) > 0) { + list->last->next = package; + list->last = package; + } else { + /* this should only happen if the underlying + * database-method get changed, so just throwing + * out here */ + fprintf(stderr, +"INTERNAL ERROR: Package database is not sorted!!!\n"); + assert(false); + exit(EXIT_FAILURE); + } + } + return RET_OK; +} + +static retvalue floodlist_initialize(struct floodlist **fl, struct target *t) { + struct floodlist *list; + retvalue r, r2; + struct package_cursor iterator; + + list = zNEW(struct floodlist); + if (FAILEDTOALLOC(list)) + return RET_ERROR_OOM; + + list->target = t; + + /* Begin with the packages currently in the archive */ + + r = package_openiterator(t, READONLY, true, &iterator); + if (RET_WAS_ERROR(r)) { + floodlist_free(list); + return r; + } + while (package_next(&iterator)) { + r2 = save_package_version(list, &iterator.current); + RET_UPDATE(r, r2); + if (RET_WAS_ERROR(r2)) + break; + } + r2 = package_closeiterator(&iterator); + RET_UPDATE(r, r2); + + if (RET_WAS_ERROR(r)) { + floodlist_free(list); + return r; + } + list->last = NULL; + *fl = list; + return RET_OK; +} + +static retvalue floodlist_trypackage(struct floodlist *list, struct package *package) { + retvalue r; + struct aa_package_data *current, *insertafter; + + r = package_getversion(package); + if (!RET_IS_OK(r)) + return r; + r = package_getsource(package); + if (!RET_IS_OK(r)) + return r; + + /* insertafter = NULL will mean insert before list */ + insertafter = list->last; + /* the next one to test, current = NULL will mean not found */ + if (insertafter != NULL) + current = insertafter->next; + else + current = list->list; + + /* the algorithm assumes almost all packages are feed in + * alphabetically. */ + + while (true) { + int cmp; + + assert (insertafter == NULL || insertafter->next == current); + assert (insertafter != NULL || current == list->list); + + if (current == NULL) + cmp = -1; /* every package is before the end of list */ + else + cmp = strcmp(package->name, current->name); + + if (cmp == 0) + break; + + if (cmp < 0) { + int precmp; + + if (insertafter == NULL) { + /* if we are before the first + * package, add us there...*/ + current = NULL; + break; + } + precmp = strcmp(package->name, insertafter->name); + if (precmp == 0) { + current = insertafter; + break; + } else if (precmp < 0) { + /* restart at the beginning: */ + current = list->list; + insertafter = NULL; + continue; + } else { // precmp > 0 + /* insert after insertafter: */ + current = NULL; + break; + } + assert ("This is not reached" == NULL); + } + /* cmp > 0 : may come later... */ + assert (current != NULL); + insertafter = current; + current = current->next; + if (current == NULL) { + /* add behind insertafter at end of list */ + break; + } + /* otherwise repeat until place found */ + } + if (current == NULL) { + /* adding a package not yet known */ + struct aa_package_data *new; + struct aa_source_version *src; + + src = find_source(list, package->source, package->sourceversion); + new = zNEW(struct aa_package_data); + if (FAILEDTOALLOC(new)) { + return RET_ERROR_OOM; + } + new->new_source = src; + new->new_version = package_dupversion(package); + if (FAILEDTOALLOC(new->new_version)) { + aa_package_data_free(new); + return RET_ERROR_OOM; + } + new->name = strdup(package->name); + if (FAILEDTOALLOC(new->name)) { + aa_package_data_free(new); + return RET_ERROR_OOM; + } + r = list->target->getinstalldata(list->target, + package, + &new->new_control, &new->new_filekeys, + &new->new_origfiles); + if (RET_WAS_ERROR(r)) { + aa_package_data_free(new); + return r; + } + if (insertafter != NULL) { + new->next = insertafter->next; + insertafter->next = new; + } else { + new->next = list->list; + list->list = new; + } + list->last = new; + } else { + /* The package already exists: */ + char *control; + struct strlist files; + struct checksumsarray origfiles; + struct aa_source_version *src; + int versioncmp; + + list->last = current; + + if (current->new_has_sibling) { + /* it has a new and that has a binary sibling, + * which means this becomes the new version + * exactly when it is newer than the old newest */ + r = dpkgversions_cmp(package->version, + current->new_version, + &versioncmp); + if (RET_WAS_ERROR(r)) { + return r; + } + if (versioncmp <= 0) { + return RET_NOTHING; + } + } else if (current->old_version != NULL) { + /* if it is older than the old one, we will + * always discard it */ + r = dpkgversions_cmp(package->version, + current->old_version, + &versioncmp); + if (RET_WAS_ERROR(r)) { + return r; + } + if (versioncmp <= 0) { + return RET_NOTHING; + } + } + /* we need to get the source to know more */ + + src = find_source(list, package->source, package->sourceversion); + if (src == NULL || !src->has_sibling) { + /* the new one has no sibling, only allowed + * to override those that have: */ + if (current->new_version == NULL) { + if (current->old_source->has_sibling) + return RET_NOTHING; + } else if (current->new_has_sibling) { + return RET_NOTHING; + } else { + /* the new one has no sibling and the old one + * has not too, take the newer one: */ + r = dpkgversions_cmp(package->version, + current->new_version, + &versioncmp); + if (RET_WAS_ERROR(r)) { + return r; + } + if (versioncmp <= 0) { + return RET_NOTHING; + } + } + } + char *new_version = package_dupversion(package); + if (FAILEDTOALLOC(new_version)) + return RET_ERROR_OOM; + + r = list->target->getinstalldata(list->target, + package, + &control, &files, &origfiles); + if (RET_WAS_ERROR(r)) { + free(new_version); + return r; + } + free(current->new_version); + current->new_version = new_version; + current->new_source = src; + current->new_has_sibling = src != NULL && src->has_sibling; + strlist_done(¤t->new_filekeys); + strlist_move(¤t->new_filekeys, &files); + checksumsarray_done(¤t->new_origfiles); + checksumsarray_move(¤t->new_origfiles, &origfiles); + free(current->new_control); + current->new_control = control; + } + return RET_OK; +} + +static retvalue floodlist_pull(struct floodlist *list, struct target *source) { + retvalue result, r; + struct package_cursor iterator; + + list->last = NULL; + r = package_openiterator(source, READONLY, true, &iterator); + if (RET_WAS_ERROR(r)) + return r; + result = RET_NOTHING; + while (package_next(&iterator)) { + r = package_getarchitecture(&iterator.current); + if (r == RET_NOTHING) + continue; + if (!RET_IS_OK(r)) { + RET_UPDATE(result, r); + break; + } + if (iterator.current.architecture != architecture_all) + continue; + + r = floodlist_trypackage(list, &iterator.current); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + if (interrupted()) { + result = RET_ERROR_INTERRUPTED; + break; + } + } + r = package_closeiterator(&iterator); + RET_ENDUPDATE(result, r); + return result; +} + +static retvalue floodlist_install(struct floodlist *list, struct logger *logger, /*@NULL@*/struct trackingdata *td) { + struct aa_package_data *pkg; + retvalue result, r; + + if (list->list == NULL) + return RET_NOTHING; + + result = target_initpackagesdb(list->target, READWRITE); + if (RET_WAS_ERROR(result)) + return result; + result = RET_NOTHING; + for (pkg = list->list ; pkg != NULL ; pkg = pkg->next) { + if (pkg->new_version != NULL) { + r = files_expectfiles(&pkg->new_filekeys, + pkg->new_origfiles.checksums); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + continue; + if (interrupted()) { + r = RET_ERROR_INTERRUPTED; + break; + } + if (td != NULL) { + if (pkg->new_source != NULL) { + r = trackingdata_switch(td, + pkg->new_source->name->name, + pkg->new_source->version); + } else { + char *source, *sourceversion; + + r = list->target->getsourceandversion( + pkg->new_control, + pkg->name, + &source, + &sourceversion); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) { + RET_UPDATE(result, r); + break; + } + r = trackingdata_switch(td, + source, sourceversion); + free(source); + free(sourceversion); + } + if (RET_WAS_ERROR(r)) { + RET_UPDATE(result, r); + break; + } + } + r = target_addpackage(list->target, + logger, pkg->name, pkg->new_version, + pkg->new_control, &pkg->new_filekeys, + false, td, architecture_all, + NULL, NULL); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + } + } + r = target_closepackagesdb(list->target); + RET_ENDUPDATE(result, r); + return result; +} + +retvalue flood(struct distribution *d, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes, architecture_t architecture, trackingdb tracks) { + struct target *t, *s; + retvalue result = RET_NOTHING, r; + struct trackingdata trackingdata; + + if (tracks != NULL) { + r = trackingdata_new(tracks, &trackingdata); + if (RET_WAS_ERROR(r)) + return r; + } + + for (t = d->targets ; t != NULL ; t = t->next) { + struct floodlist *fl = NULL; + + if (atom_defined(architecture)) { + if (architecture != t->architecture) + continue; + } else if (limitations_missed(architectures, + t->architecture)) + continue; + if (limitations_missed(components, t->component)) + continue; + if (limitations_missed(packagetypes, t->packagetype)) + continue; + if (t->packagetype != pt_deb && t->packagetype != pt_udeb) + continue; + + r = floodlist_initialize(&fl, t); + if (RET_WAS_ERROR(r)) { + if (tracks != NULL) + trackingdata_done(&trackingdata); + return r; + } + + for (s = d->targets ; s != NULL ; s = s->next) { + if (s->component != t->component) + continue; + if (s->packagetype != t->packagetype) + continue; + /* no need to copy things from myself: */ + if (s->architecture == t->architecture) + continue; + if (limitations_missed(architectures, + s->architecture)) + continue; + r = floodlist_pull(fl, s); + RET_UPDATE(d->status, r); + if (RET_WAS_ERROR(r)) { + if (tracks != NULL) + trackingdata_done(&trackingdata); + floodlist_free(fl); + return r; + } + } + r = floodlist_install(fl, d->logger, + (tracks != NULL)?&trackingdata:NULL); + RET_UPDATE(result, r); + floodlist_free(fl); + if (RET_WAS_ERROR(r)) { + if (tracks != NULL) + trackingdata_done(&trackingdata); + return r; + } + } + if (tracks != NULL) { + r = trackingdata_finish(tracks, &trackingdata); + RET_ENDUPDATE(result, r); + } + return result; +} |