summaryrefslogtreecommitdiffstats
path: root/archallflood.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--archallflood.c714
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(&current->new_filekeys);
+ strlist_move(&current->new_filekeys, &files);
+ checksumsarray_done(&current->new_origfiles);
+ checksumsarray_move(&current->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;
+}