summaryrefslogtreecommitdiffstats
path: root/tracking.c
diff options
context:
space:
mode:
Diffstat (limited to 'tracking.c')
-rw-r--r--tracking.c1468
1 files changed, 1468 insertions, 0 deletions
diff --git a/tracking.c b/tracking.c
new file mode 100644
index 0000000..6fe5554
--- /dev/null
+++ b/tracking.c
@@ -0,0 +1,1468 @@
+/* This file is part of "reprepro"
+ * Copyright (C) 2005,2006,2007,2008,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 <assert.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "error.h"
+#include "names.h"
+#include "dirs.h"
+#include "names.h"
+#include "reference.h"
+#include "ignore.h"
+#include "configparser.h"
+#include "package.h"
+
+#include "database_p.h"
+#include "tracking.h"
+
+#ifndef NOPARANOIA
+#define PARANOIA
+#endif
+
+struct s_tracking {
+ char *codename;
+ struct table *table;
+ enum trackingtype type;
+ struct trackingoptions options;
+};
+
+retvalue tracking_done(trackingdb db, struct distribution *distribution) {
+ retvalue r;
+
+ if (db == NULL)
+ return RET_OK;
+
+ r = table_close(db->table);
+ free(db->codename);
+ free(db);
+ if (distribution->trackingdb == NULL) {
+ fprintf(stderr,
+"Internal Error: Tracking database was closed, but corresponding entry in the distribution structure %s is missing.\n",
+ distribution->codename);
+ } else {
+ distribution->trackingdb = NULL;
+ }
+ return r;
+}
+
+retvalue tracking_initialize(/*@out@*/trackingdb *db, struct distribution *distribution, bool readonly) {
+ struct s_tracking *t;
+ retvalue r;
+
+ t = zNEW(struct s_tracking);
+ if (FAILEDTOALLOC(t))
+ return RET_ERROR_OOM;
+ t->codename = strdup(distribution->codename);
+ if (FAILEDTOALLOC(t->codename)) {
+ free(t);
+ return RET_ERROR_OOM;
+ }
+ assert (distribution->tracking != dt_NONE || readonly);
+ t->type = distribution->tracking;
+ t->options = distribution->trackingoptions;
+ r = database_opentracking(t->codename, readonly, &t->table);
+ if (!RET_IS_OK(r)) {
+ free(t->codename);
+ free(t);
+ return r;
+ }
+ *db = t;
+ distribution->trackingdb = t;
+ return RET_OK;
+}
+
+static inline enum filetype filetypechar(enum filetype filetype) {
+ switch (filetype) {
+ case ft_LOG:
+ case ft_BUILDINFO:
+ case ft_CHANGES:
+ case ft_ALL_BINARY:
+ case ft_ARCH_BINARY:
+ case ft_SOURCE:
+ case ft_XTRA_DATA:
+ return filetype;
+ }
+ assert(false);
+ return ft_XTRA_DATA;
+}
+
+retvalue trackedpackage_addfilekey(trackingdb tracks, struct trackedpackage *pkg, enum filetype filetype, char *filekey, bool used) {
+ char *id;
+ enum filetype ft = filetypechar(filetype);
+ int i, *newrefcounts;
+ enum filetype *newfiletypes;
+ retvalue r;
+
+ if (FAILEDTOALLOC(filekey))
+ return RET_ERROR_OOM;
+
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ if (strcmp(pkg->filekeys.values[i], filekey) == 0) {
+ if (pkg->filetypes[i] != ft) {
+ /* if old file has refcount 0, just repair: */
+ if (pkg->refcounts[i] <= 0) {
+ free(filekey);
+ pkg->filetypes[i] = ft;
+ if (used)
+ pkg->refcounts[i] = 1;
+ return RET_OK;
+ }
+ fprintf(stderr,
+"Filekey '%s' already registered for '%s_%s' as type '%c' is tried to be reregistered as type '%c'!\n",
+ filekey, pkg->sourcename,
+ pkg->sourceversion,
+ pkg->filetypes[i], ft);
+ free(filekey);
+ return RET_ERROR;
+ }
+ free(filekey);
+ if (used)
+ pkg->refcounts[i]++;
+ return RET_OK;
+ }
+ }
+
+ newrefcounts = realloc(pkg->refcounts,
+ (pkg->filekeys.count + 1) * sizeof(int));
+ if (FAILEDTOALLOC(newrefcounts)) {
+ free(filekey);
+ return RET_ERROR_OOM;
+ }
+ if (used)
+ newrefcounts[pkg->filekeys.count]=1;
+ else
+ newrefcounts[pkg->filekeys.count]=0;
+ pkg->refcounts = newrefcounts;
+ newfiletypes = realloc(pkg->filetypes,
+ (pkg->filekeys.count + 1) * sizeof(enum filetype));
+ if (FAILEDTOALLOC(newfiletypes)) {
+ free(filekey);
+ return RET_ERROR_OOM;
+ }
+ newfiletypes[pkg->filekeys.count] = filetype;
+ pkg->filetypes = newfiletypes;
+
+ r = strlist_add(&pkg->filekeys, filekey);
+ if (RET_WAS_ERROR(r))
+ return r;
+
+ id = calc_trackreferee(tracks->codename,
+ pkg->sourcename, pkg->sourceversion);
+ if (FAILEDTOALLOC(id))
+ return RET_ERROR_OOM;
+ r = references_increment(filekey, id);
+ free(id);
+ return r;
+}
+
+retvalue trackedpackage_adddupfilekeys(trackingdb tracks, struct trackedpackage *pkg, enum filetype filetype, const struct strlist *filekeys, bool used) {
+ int i;
+ retvalue result, r;
+ assert (filekeys != NULL);
+
+ result = RET_OK;
+ for (i = 0 ; i < filekeys->count ; i++) {
+ char *filekey = strdup(filekeys->values[i]);
+ r = trackedpackage_addfilekey(tracks, pkg, filetype,
+ filekey, used);
+ RET_UPDATE(result, r);
+ }
+ return result;
+}
+
+static inline retvalue trackedpackage_removefilekey(trackingdb tracks, struct trackedpackage *pkg, const char *filekey) {
+ int i;
+
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ if (strcmp(pkg->filekeys.values[i], filekey) == 0) {
+ if (pkg->refcounts[i] > 0) {
+ pkg->refcounts[i]--;
+ } else
+ fprintf(stderr,
+"Warning: tracking database of %s has inconsistent refcounts of %s_%s.\n",
+ tracks->codename,
+ pkg->sourcename,
+ pkg->sourceversion);
+
+ return RET_OK;
+ }
+ }
+ fprintf(stderr,
+"Warning: tracking database of %s missed files for %s_%s.\n",
+ tracks->codename, pkg->sourcename, pkg->sourceversion);
+ return RET_OK;
+
+}
+
+retvalue trackedpackage_removefilekeys(trackingdb tracks, struct trackedpackage *pkg, const struct strlist *filekeys) {
+ int i;
+ retvalue result, r;
+ assert (filekeys != NULL);
+
+ result = RET_OK;
+ for (i = 0 ; i < filekeys->count ; i++) {
+ const char *filekey = filekeys->values[i];
+ r = trackedpackage_removefilekey(tracks, pkg, filekey);
+ RET_UPDATE(result, r);
+ }
+ return result;
+}
+
+void trackedpackage_free(struct trackedpackage *pkg) {
+ if (pkg != NULL) {
+ free(pkg->sourcename);
+ free(pkg->sourceversion);
+ strlist_done(&pkg->filekeys);
+ free(pkg->refcounts);
+ free(pkg->filetypes);
+ free(pkg);
+ }
+}
+
+static inline int parsenumber(const char **d, size_t *s) {
+ int count;
+
+ count = 0;
+ do {
+ if (**d < '0' || **d > '7')
+ return -1;
+ count = (count*8) + (**d-'0');
+ (*d)++;
+ (*s)--;
+ if (*s == 0)
+ return -1;
+ } while (**d != '\0');
+ (*d)++;
+ (*s)--;
+ return count;
+}
+
+static retvalue tracking_new(const char *sourcename, const char *version, /*@out@*/struct trackedpackage **pkg) {
+ struct trackedpackage *p;
+ assert (pkg != NULL && sourcename != NULL && version != NULL);
+
+ p = zNEW(struct trackedpackage);
+ if (FAILEDTOALLOC(p))
+ return RET_ERROR_OOM;
+ p->sourcename = strdup(sourcename);
+ p->sourceversion = strdup(version);
+ p->flags.isnew = true;
+ if (FAILEDTOALLOC(p->sourcename) || FAILEDTOALLOC(p->sourceversion)) {
+ trackedpackage_free(p);
+ return RET_ERROR_OOM;
+ }
+ *pkg = p;
+ return RET_OK;
+}
+
+static inline retvalue parse_data(const char *name, const char *version, const char *data, size_t datalen, /*@out@*/struct trackedpackage **pkg) {
+ struct trackedpackage *p;
+ int i;
+
+ p = zNEW(struct trackedpackage);
+ if (FAILEDTOALLOC(p))
+ return RET_ERROR_OOM;
+ p->sourcename = strdup(name);
+ p->sourceversion = strdup(version);
+ if (FAILEDTOALLOC(p->sourcename)
+ || FAILEDTOALLOC(p->sourceversion)
+ /* || FAILEDTOALLOC(p->sourcedir) */) {
+ trackedpackage_free(p);
+ return RET_ERROR_OOM;
+ }
+ while (datalen > 0 && *data != '\0') {
+ char *filekey;
+ const char *separator;
+ size_t filekeylen;
+ retvalue r;
+
+ if (((p->filekeys.count)&31) == 0) {
+ enum filetype *n = realloc(p->filetypes,
+ (p->filekeys.count+32)*sizeof(enum filetype));
+ if (FAILEDTOALLOC(n)) {
+ trackedpackage_free(p);
+ return RET_ERROR_OOM;
+ }
+ p->filetypes = n;
+ }
+ p->filetypes[p->filekeys.count] = *data;
+ data++; datalen--;
+ separator = memchr(data, '\0', datalen);
+ if (separator == NULL) {
+ fprintf(stderr,
+"Internal Error: Corrupt tracking data for %s %s\n",
+ name, version);
+ trackedpackage_free(p);
+ return RET_ERROR;
+ }
+ filekeylen = separator - data;
+ filekey = strndup(data, filekeylen);
+ if (FAILEDTOALLOC(filekey)) {
+ trackedpackage_free(p);
+ return RET_ERROR_OOM;
+ }
+ r = strlist_add(&p->filekeys, filekey);
+ if (RET_WAS_ERROR(r)) {
+ trackedpackage_free(p);
+ return r;
+ }
+ data += filekeylen + 1;
+ datalen -= filekeylen + 1;
+ }
+ data++; datalen--;
+ p->refcounts = nzNEW(p->filekeys.count, int);
+ if (FAILEDTOALLOC(p->refcounts)) {
+ trackedpackage_free(p);
+ return RET_ERROR_OOM;
+ }
+ for (i = 0 ; i < p->filekeys.count ; i++) {
+ if ((p->refcounts[i] = parsenumber(&data, &datalen)) < 0) {
+ fprintf(stderr,
+"Internal Error: Corrupt tracking data for %s %s\n",
+ name, version);
+ trackedpackage_free(p);
+ return RET_ERROR;
+ }
+ }
+ if (datalen > 0) {
+ fprintf(stderr,
+"Internal Error: Trailing garbage in tracking data for %s %s\n (%ld bytes)",
+ name, version, (long)datalen);
+ trackedpackage_free(p);
+ return RET_ERROR;
+ }
+ p->flags.isnew = false;
+ p->flags.deleted = false;
+ *pkg = p;
+ return RET_OK;
+}
+
+retvalue tracking_get(trackingdb t, const char *sourcename, const char *version, /*@out@*/struct trackedpackage **pkg) {
+ const char *data;
+ size_t datalen;
+ retvalue r;
+
+ assert (pkg != NULL && sourcename != NULL && version != NULL);
+
+ r = table_getpair(t->table, sourcename, version, &data, &datalen);
+ if (!RET_IS_OK(r))
+ return r;
+ return parse_data(sourcename, version, data, datalen, pkg);
+}
+
+retvalue tracking_getornew(trackingdb tracks, const char *name, const char *version, /*@out@*/struct trackedpackage **pkg) {
+ retvalue r;
+ r = tracking_get(tracks, name, version, pkg);
+ if (r == RET_NOTHING)
+ r = tracking_new(name, version, pkg);
+ return r;
+}
+
+static retvalue gen_data(struct trackedpackage *pkg, /*@out@*/char **newdata_p, /*@out@*/size_t *newdatalen_p) {
+ size_t versionsize = strlen(pkg->sourceversion)+1;
+ int i;
+ char *d, *data;
+ size_t datalen;
+
+ datalen = versionsize + 1;
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ size_t l;
+ l = strlen(pkg->filekeys.values[i]);
+ if (l > 0)
+ datalen += l+9;
+ }
+ data = malloc(datalen + 1);
+ if (FAILEDTOALLOC(data))
+ return RET_ERROR_OOM;
+ memcpy(data, pkg->sourceversion, versionsize);
+ d = data + versionsize;
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ size_t l;
+ l = strlen(pkg->filekeys.values[i]);
+ if (l > 0) {
+ *d = pkg->filetypes[i];
+ d++;
+ memcpy(d, pkg->filekeys.values[i], l + 1);
+ d+=l+1;
+ }
+ }
+ *d ='\0'; d++;
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ int j;
+#define MAXREFCOUNTOCTETS 7
+ char countstring[MAXREFCOUNTOCTETS];
+ size_t count = pkg->refcounts[i];
+
+ countstring[MAXREFCOUNTOCTETS-1] = '\0';
+ for (j = MAXREFCOUNTOCTETS-2 ; j >= 0 ; j--) {
+ countstring[j] = '0' + (count & 7);
+ count >>= 3;
+ if (count == 0)
+ break;
+ }
+#undef MAXREFCOUNTOCTETS
+ assert (count == 0);
+
+ memcpy(d, countstring+j, 7 - j);
+ d+=7-j;
+ datalen -= j;
+ }
+ *d ='\0';
+ assert ((size_t)(d-data) == datalen);
+ *newdata_p = data;
+ *newdatalen_p = datalen;
+ return RET_OK;
+}
+
+static retvalue tracking_saveatcursor(trackingdb t, struct cursor *cursor, struct trackedpackage *pkg) {
+ if (pkg->flags.deleted) {
+ /* delete if delete is requested
+ * (all unreferencing has to be done before) */
+ return cursor_delete(t->table, cursor,
+ pkg->sourcename, pkg->sourceversion);
+ } else {
+ char *newdata;
+ size_t newdatalen;
+ retvalue r;
+
+ r = gen_data(pkg, &newdata, &newdatalen);
+ if (RET_IS_OK(r)) {
+ r = cursor_replace(t->table, cursor,
+ newdata, newdatalen);
+ free(newdata);
+ }
+ return r;
+ }
+}
+
+static retvalue tracking_saveonly(trackingdb t, struct trackedpackage *pkg) {
+ retvalue r, r2;
+ char *newdata;
+ size_t newdatalen;
+
+ assert (pkg != NULL);
+
+ if (!pkg->flags.isnew) {
+ struct cursor *cursor;
+
+ r = table_newpairedcursor(t->table,
+ pkg->sourcename, pkg->sourceversion, &cursor,
+ NULL, NULL);
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (r == RET_NOTHING) {
+ fprintf(stderr,
+"Internal error: tracking_save with isnew=false called but could not find %s_%s in %s!\n",
+ pkg->sourcename, pkg->sourceversion,
+ t->codename);
+ pkg->flags.isnew = true;
+ } else {
+ r = tracking_saveatcursor(t, cursor, pkg);
+ r2 = cursor_close(t->table, cursor);
+ RET_ENDUPDATE(r, r2);
+ return r;
+ }
+ }
+
+ if (pkg->flags.deleted)
+ return RET_OK;
+
+ r = gen_data(pkg, &newdata, &newdatalen);
+ assert (r != RET_NOTHING);
+ if (!RET_IS_OK(r))
+ return r;
+
+ r = table_addrecord(t->table, pkg->sourcename, newdata, newdatalen, false);
+ free(newdata);
+ if (verbose > 18)
+ fprintf(stderr, "Adding tracked package '%s'_'%s' to '%s'\n",
+ pkg->sourcename, pkg->sourceversion,
+ t->codename);
+ return r;
+}
+
+retvalue tracking_save(trackingdb t, struct trackedpackage *pkg) {
+ retvalue r = tracking_saveonly(t, pkg);
+ trackedpackage_free(pkg);
+ return r;
+}
+
+retvalue tracking_listdistributions(struct strlist *distributions) {
+ return database_listsubtables("tracking.db", distributions);
+}
+
+retvalue tracking_drop(const char *codename) {
+ retvalue result, r;
+
+ result = database_dropsubtable("tracking.db", codename);
+ r = references_remove(codename);
+ RET_UPDATE(result, r);
+
+ return result;
+}
+
+static retvalue tracking_recreatereferences(trackingdb t) {
+ struct cursor *cursor;
+ retvalue result, r;
+ struct trackedpackage *pkg;
+ char *id;
+ int i;
+ const char *key, *value, *data;
+ size_t datalen;
+
+ r = table_newglobalcursor(t->table, true, &cursor);
+ if (!RET_IS_OK(r))
+ return r;
+
+ result = RET_NOTHING;
+
+ while (cursor_nextpair(t->table, cursor,
+ &key, &value, &data, &datalen)) {
+ r = parse_data(key, value, data, datalen, &pkg);
+ if (RET_WAS_ERROR(r)) {
+ (void)cursor_close(t->table, cursor);
+ return r;
+ }
+ id = calc_trackreferee(t->codename, pkg->sourcename,
+ pkg->sourceversion);
+ if (FAILEDTOALLOC(id)) {
+ trackedpackage_free(pkg);
+ (void)cursor_close(t->table, cursor);
+ return RET_ERROR_OOM;
+ }
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ const char *filekey = pkg->filekeys.values[i];
+ r = references_increment(filekey, id);
+ RET_UPDATE(result, r);
+ }
+ free(id);
+ trackedpackage_free(pkg);
+ }
+ r = cursor_close(t->table, cursor);
+ RET_UPDATE(result, r);
+ return result;
+}
+
+retvalue tracking_rereference(struct distribution *distribution) {
+ retvalue result, r;
+ trackingdb tracks;
+
+ result = references_remove(distribution->codename);
+ if (distribution->tracking == dt_NONE)
+ return result;
+ r = tracking_initialize(&tracks, distribution, true);
+ RET_UPDATE(result, r);
+ if (!RET_IS_OK(r))
+ return result;
+ r = tracking_recreatereferences(tracks);
+ RET_UPDATE(result, r);
+ r = tracking_done(tracks, distribution);
+ RET_ENDUPDATE(result, r);
+ return result;
+}
+
+retvalue tracking_remove(trackingdb t, const char *sourcename, const char *version) {
+ retvalue result, r;
+ struct cursor *cursor;
+ const char *data;
+ size_t datalen;
+ char *id;
+ struct trackedpackage *pkg SETBUTNOTUSED(= NULL);
+
+ r = table_newpairedcursor(t->table, sourcename, version, &cursor,
+ &data, &datalen);
+ if (!RET_IS_OK(r))
+ return r;
+
+ id = calc_trackreferee(t->codename, sourcename, version);
+ if (FAILEDTOALLOC(id)) {
+ (void)cursor_close(t->table, cursor);
+ return RET_ERROR_OOM;
+ }
+
+ result = parse_data(sourcename, version, data, datalen, &pkg);
+ if (RET_IS_OK(r)) {
+ assert (pkg != NULL);
+ r = references_delete(id, &pkg->filekeys, NULL);
+ RET_UPDATE(result, r);
+ trackedpackage_free(pkg);
+ } else {
+ RET_UPDATE(result, r);
+ fprintf(stderr,
+"Could not parse data, removing all references blindly...\n");
+ r = references_remove(id);
+ RET_UPDATE(result, r);
+ }
+ free(id);
+ r = cursor_delete(t->table, cursor, sourcename, version);
+ if (RET_IS_OK(r))
+ fprintf(stderr, "Removed %s_%s from %s.\n",
+ sourcename, version, t->codename);
+ RET_UPDATE(result, r);
+ r = cursor_close(t->table, cursor);
+ RET_ENDUPDATE(result, r);
+ return result;
+}
+
+static void print(const char *codename, const struct trackedpackage *pkg){
+ int i;
+
+ printf("Distribution: %s\n", codename);
+ printf("Source: %s\n", pkg->sourcename);
+ printf("Version: %s\n", pkg->sourceversion);
+ printf("Files:\n");
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ const char *filekey = pkg->filekeys.values[i];
+
+ printf(" %s %c %d\n", filekey,
+ pkg->filetypes[i], pkg->refcounts[i]);
+ }
+ (void)fputs("\n", stdout);
+}
+
+retvalue tracking_printall(trackingdb t) {
+ struct cursor *cursor;
+ retvalue result, r;
+ struct trackedpackage *pkg;
+ const char *key, *value, *data;
+ size_t datalen;
+
+ r = table_newglobalcursor(t->table, true, &cursor);
+ if (!RET_IS_OK(r))
+ return r;
+
+ result = RET_NOTHING;
+
+ while (cursor_nextpair(t->table, cursor,
+ &key, &value, &data, &datalen)) {
+ r = parse_data(key, value, data, datalen, &pkg);
+ if (RET_IS_OK(r)) {
+ print(t->codename, pkg);
+ trackedpackage_free(pkg);
+ }
+ RET_UPDATE(result, r);
+ }
+ r = cursor_close(t->table, cursor);
+ RET_ENDUPDATE(result, r);
+ return result;
+}
+
+retvalue tracking_foreach_ro(struct distribution *d, tracking_foreach_ro_action *action) {
+ trackingdb t;
+ struct cursor *cursor;
+ retvalue result, r;
+ struct trackedpackage *pkg;
+ const char *key, *value, *data;
+ size_t datalen;
+
+ r = tracking_initialize(&t, d, true);
+ if (!RET_IS_OK(r))
+ return r;
+
+ r = table_newglobalcursor(t->table, true, &cursor);
+ if (!RET_IS_OK(r)) {
+ (void)tracking_done(t, d);
+ return r;
+ }
+
+ result = RET_NOTHING;
+ while (cursor_nextpair(t->table, cursor,
+ &key, &value, &data, &datalen)) {
+ r = parse_data(key, value, data, datalen, &pkg);
+ if (RET_IS_OK(r)) {
+ r = action(d, pkg);
+ trackedpackage_free(pkg);
+ }
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ }
+ r = cursor_close(t->table, cursor);
+ RET_ENDUPDATE(result, r);
+ r = tracking_done(t, d);
+ RET_ENDUPDATE(result, r);
+ return result;
+}
+
+retvalue tracking_parse(struct distribution *d, struct configiterator *iter) {
+ enum trackingflags { tf_keep, tf_all, tf_minimal,
+ tf_includechanges, tf_includebyhand, tf_includelogs,
+ tf_includebuildinfos,
+ tf_keepsources,
+ tf_needsources, tf_embargoalls,
+ tf_COUNT /* must be last */
+ };
+ static const struct constant trackingflags[] = {
+ {"keep", tf_keep},
+ {"all", tf_all},
+ {"minimal", tf_minimal},
+ {"includechanges", tf_includechanges},
+ {"includebuildinfos", tf_includebuildinfos},
+ {"includelogs", tf_includelogs},
+ {"includebyhand", tf_includebyhand},
+ {"keepsources", tf_keepsources},
+ {"needsources", tf_needsources},
+ {"embargoalls", tf_embargoalls},
+ {NULL, -1}
+ };
+ bool flags[tf_COUNT];
+ retvalue r;
+ int modecount;
+
+ assert (d->tracking == dt_NONE);
+ memset(flags, 0, sizeof(flags));
+ r = config_getflags(iter, "Tracking", trackingflags, flags,
+ IGNORABLE(unknownfield), "");
+ assert (r != RET_NOTHING);
+ if (RET_WAS_ERROR(r))
+ return r;
+ modecount = flags[tf_keep]?1:0 + flags[tf_minimal]?1:0 + flags[tf_all]?1:0;
+ if (modecount > 1) {
+ fprintf(stderr,
+"Error parsing config file %s, line %u:\n"
+"Only one of 'keep','all' or 'minimal' can be in one Tracking header.\n",
+ config_filename(iter), config_line(iter));
+ return RET_ERROR;
+ }
+ if (modecount < 1) {
+ fprintf(stderr,
+"Error parsing config file %s, line %u, column %u:\n"
+"Tracking mode ('keep','all' or 'minimal') expected.\n",
+ config_filename(iter), config_line(iter),
+ config_column(iter));
+ return RET_ERROR;
+ }
+ if (flags[tf_keep])
+ d->tracking = dt_KEEP;
+ else if (flags[tf_minimal])
+ d->tracking = dt_MINIMAL;
+ else
+ d->tracking = dt_ALL;
+
+ d->trackingoptions.includechanges = flags[tf_includechanges];
+ d->trackingoptions.includebyhand = flags[tf_includebyhand];
+ d->trackingoptions.includebuildinfos = flags[tf_includebuildinfos];
+ d->trackingoptions.includelogs = flags[tf_includelogs];
+ d->trackingoptions.keepsources = flags[tf_keepsources];
+ d->trackingoptions.needsources = flags[tf_needsources];
+ if (flags[tf_needsources])
+ fprintf(stderr,
+"Warning parsing config file %s, line %u:\n"
+"'needsources' ignored as not yet supported.\n",
+ config_filename(iter), config_line(iter));
+ d->trackingoptions.embargoalls = flags[tf_embargoalls];
+ if (flags[tf_embargoalls])
+ fprintf(stderr,
+"Warning parsing config file %s, line %u:\n"
+"'embargoall' ignored as not yet supported.\n",
+ config_filename(iter), config_line(iter));
+ return RET_OK;
+}
+
+static retvalue trackingdata_remember(struct trackingdata *td, const char*name, const char*version) {
+ struct trackingdata_remember *r;
+
+ r = NEW(struct trackingdata_remember);
+ if (FAILEDTOALLOC(r))
+ return RET_ERROR_OOM;
+ r->name = strdup(name);
+ r->version = strdup(version);
+ if (FAILEDTOALLOC(r->name) || FAILEDTOALLOC(r->version)) {
+ free(r->name);
+ free(r->version);
+ free(r);
+ return RET_ERROR_OOM;
+ }
+ r->next = td->remembered;
+ td->remembered = r;
+ return RET_OK;
+}
+
+retvalue trackingdata_summon(trackingdb tracks, const char *name, const char *version, struct trackingdata *data) {
+ struct trackedpackage *pkg;
+ retvalue r;
+
+ r = tracking_getornew(tracks, name, version, &pkg);
+ assert (r != RET_NOTHING);
+ if (RET_IS_OK(r)) {
+ data->tracks = tracks;
+ data->pkg = pkg;
+ data->remembered = NULL;
+ return r;
+ }
+ return r;
+}
+
+retvalue trackingdata_new(trackingdb tracks, struct trackingdata *data) {
+
+ data->tracks = tracks;
+ data->pkg = NULL;
+ data->remembered = NULL;
+ return RET_OK;
+}
+
+retvalue trackingdata_switch(struct trackingdata *data, const char *source, const char *version) {
+ retvalue r;
+
+ if (data->pkg != NULL) {
+ if (strcmp(data->pkg->sourcename, source) == 0 &&
+ strcmp(data->pkg->sourceversion, version) == 0)
+ return RET_OK;
+ r = tracking_saveonly(data->tracks, data->pkg);
+ if (RET_WAS_ERROR(r))
+ return r;
+ r = trackingdata_remember(data, data->pkg->sourcename,
+ data->pkg->sourceversion);
+ strlist_done(&data->pkg->filekeys);
+ free(data->pkg->sourcename);
+ free(data->pkg->sourceversion);
+ free(data->pkg->refcounts);
+ free(data->pkg->filetypes);
+ free(data->pkg);
+ data->pkg = NULL;
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+ r = tracking_getornew(data->tracks, source, version, &data->pkg);
+ assert (r != RET_NOTHING);
+ if (RET_WAS_ERROR(r))
+ return r;
+ return RET_OK;
+}
+
+retvalue trackingdata_insert(struct trackingdata *data, enum filetype filetype, const struct strlist *filekeys, /*@null@*/const struct package *old, /*@null@*/const struct strlist *oldfilekeys) {
+ retvalue result, r;
+ struct trackedpackage *pkg;
+
+ assert (data != NULL);
+ assert(data->pkg != NULL);
+ result = trackedpackage_adddupfilekeys(data->tracks, data->pkg,
+ filetype, filekeys, true);
+ if (RET_WAS_ERROR(result)) {
+ return result;
+ }
+ if (old == NULL || old->source == NULL || old->sourceversion == NULL
+ || oldfilekeys == NULL) {
+ return RET_OK;
+ }
+ if (strcmp(old->sourceversion, data->pkg->sourceversion) == 0 &&
+ strcmp(old->source, data->pkg->sourcename) == 0) {
+ /* Unlikely, but it may also be the same source version as
+ * the package we are currently adding */
+ return trackedpackage_removefilekeys(data->tracks, data->pkg,
+ oldfilekeys);
+ }
+ r = tracking_get(data->tracks, old->source, old->sourceversion, &pkg);
+ if (RET_WAS_ERROR(r)) {
+ return r;
+ }
+ if (r == RET_NOTHING) {
+ fprintf(stderr,
+"Could not find tracking data for %s_%s in %s to remove old files from it.\n",
+ old->source, old->sourceversion,
+ data->tracks->codename);
+ return result;
+ }
+ r = trackedpackage_removefilekeys(data->tracks, pkg, oldfilekeys);
+ RET_UPDATE(result, r);
+ r = tracking_save(data->tracks, pkg);
+ RET_UPDATE(result, r);
+ r = trackingdata_remember(data, old->source, old->sourceversion);
+ RET_UPDATE(result, r);
+
+ return result;
+}
+
+retvalue trackingdata_remove(struct trackingdata *data, const char* oldsource, const char*oldversion, const struct strlist *oldfilekeys) {
+ retvalue result, r;
+ struct trackedpackage *pkg;
+
+ if (verbose >= 15)
+ fprintf(stderr, "trace: trackingdata_remove(oldsource=%s, oldversion=%s) called.\n",
+ oldsource, oldversion);
+ assert(oldsource != NULL && oldversion != NULL && oldfilekeys != NULL);
+ if (data->pkg != NULL &&
+ strcmp(oldversion, data->pkg->sourceversion) == 0 &&
+ strcmp(oldsource, data->pkg->sourcename) == 0) {
+ /* Unlikely, but it may also be the same source version as
+ * the package we are currently adding */
+ return trackedpackage_removefilekeys(data->tracks,
+ data->pkg, oldfilekeys);
+ }
+ result = tracking_get(data->tracks, oldsource, oldversion, &pkg);
+ if (RET_WAS_ERROR(result)) {
+ return result;
+ }
+ if (result == RET_NOTHING) {
+ fprintf(stderr,
+"Could not find tracking data for %s_%s in %s to remove old files from it.\n",
+ oldsource, oldversion, data->tracks->codename);
+ return RET_OK;
+ }
+ r = trackedpackage_removefilekeys(data->tracks, pkg, oldfilekeys);
+ RET_UPDATE(result, r);
+ r = tracking_save(data->tracks, pkg);
+ RET_UPDATE(result, r);
+ r = trackingdata_remember(data, oldsource, oldversion);
+ RET_UPDATE(result, r);
+
+ return result;
+}
+
+void trackingdata_done(struct trackingdata *d) {
+ trackedpackage_free(d->pkg);
+ d->pkg = NULL;
+ d->tracks = NULL;
+ while (d->remembered != NULL) {
+ struct trackingdata_remember *h = d->remembered;
+ d->remembered = h->next;
+ free(h->name);
+ free(h->version);
+ free(h);
+ }
+
+}
+
+static inline retvalue trackedpackage_removeall(trackingdb tracks, struct trackedpackage *pkg) {
+ retvalue result = RET_OK, r;
+ char *id;
+
+// printf("[trackedpackage_removeall %s %s %s]\n", tracks->codename, pkg->sourcename, pkg->sourceversion);
+ id = calc_trackreferee(tracks->codename, pkg->sourcename,
+ pkg->sourceversion);
+ if (FAILEDTOALLOC(id))
+ return RET_ERROR_OOM;
+
+ pkg->flags.deleted = true;
+ r = references_delete(id, &pkg->filekeys, NULL);
+ RET_UPDATE(result, r);
+ free(id);
+ strlist_done(&pkg->filekeys);
+ strlist_init(&pkg->filekeys);
+ free(pkg->refcounts); pkg->refcounts = NULL;
+ return result;
+}
+
+static inline bool tracking_needed(trackingdb tracks, struct trackedpackage *pkg, int ofs) {
+ if (pkg->refcounts[ofs] > 0)
+ return true;
+ // TODO: add checks so that only .changes, .buildinfo and .log files
+ // belonging to still existing binaries are kept in minimal mode
+ if (pkg->filetypes[ofs] == ft_LOG && tracks->options.includelogs)
+ return true;
+ if (pkg->filetypes[ofs] == ft_BUILDINFO && tracks->options.includebuildinfos)
+ return true;
+ if (pkg->filetypes[ofs] == ft_CHANGES && tracks->options.includechanges)
+ return true;
+ if (pkg->filetypes[ofs] == ft_XTRA_DATA)
+ return true;
+ if (pkg->filetypes[ofs] == ft_SOURCE && tracks->options.keepsources)
+ return true;
+ return false;
+
+}
+
+static inline retvalue trackedpackage_removeunneeded(trackingdb tracks, struct trackedpackage *pkg) {
+ retvalue result = RET_OK, r;
+ char *id = NULL;
+ int i, j, count;
+
+ assert(tracks->type == dt_MINIMAL);
+
+ count = pkg->filekeys.count;
+ j = 0;
+ for (i = 0 ; i < count ; i++) {
+ if (tracking_needed(tracks, pkg, i)) {
+ if (j < i) {
+ pkg->filekeys.values[j] = pkg->filekeys.values[i];
+ pkg->refcounts[j] = pkg->refcounts[i];
+ pkg->filetypes[j] = pkg->filetypes[i];
+ }
+ j++;
+ } else {
+ char *filekey = pkg->filekeys.values[i];
+ pkg->filekeys.values[i] = NULL;
+ if (FAILEDTOALLOC(id)) {
+ id = calc_trackreferee(tracks->codename,
+ pkg->sourcename, pkg->sourceversion);
+ if (id == NULL)
+ result = RET_ERROR_OOM;
+ }
+ if (id != NULL) {
+// printf("[trackedpackage_removeunneeded %s %s %s: '%s']\n", tracks->codename, pkg->sourcename, pkg->sourceversion, filekey);
+ r = references_decrement(filekey, id);
+ RET_UPDATE(result, r);
+ }
+ free(filekey);
+ }
+ }
+ assert (j <= pkg->filekeys.count);
+ pkg->filekeys.count = j;
+ free(id);
+ return result;
+}
+
+static inline retvalue trackedpackage_tidy(trackingdb tracks, struct trackedpackage *pkg) {
+ int i;
+
+ if (tracks->type == dt_KEEP)
+ return RET_OK;
+ /* look if anything clings to this package */
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ if (pkg->refcounts[i] > 0)
+ break;
+ }
+ if (i >= pkg->filekeys.count)
+
+ /* nothing left, remove it all */
+ return trackedpackage_removeall(tracks, pkg);
+
+ else if (tracks->type == dt_MINIMAL)
+
+ /* remove all files no longer needed */
+ return trackedpackage_removeunneeded(tracks, pkg);
+ else
+ return RET_OK;
+}
+
+retvalue trackingdata_finish(trackingdb tracks, struct trackingdata *d) {
+ retvalue r;
+ assert (d->tracks == tracks);
+ if (d->pkg != NULL) {
+ r = trackedpackage_tidy(tracks, d->pkg);
+ r = tracking_save(tracks, d->pkg);
+ } else
+ r = RET_OK;
+ d->pkg = NULL;
+ /* call for all remembered actions... */
+ while (d->remembered != NULL) {
+ struct trackingdata_remember *h = d->remembered;
+ struct trackedpackage *pkg;
+ d->remembered = h->next;
+ r = tracking_get(tracks, h->name, h->version, &pkg);
+ free(h->name);
+ free(h->version);
+ free(h);
+ if (RET_IS_OK(r)) {
+ r = trackedpackage_tidy(tracks, pkg);
+ r = tracking_save(tracks, pkg);
+ }
+ }
+ d->tracks = NULL;
+ return r;
+
+}
+
+retvalue tracking_tidyall(trackingdb t) {
+ struct cursor *cursor;
+ retvalue result, r;
+ struct trackedpackage *pkg;
+ const char *key, *value, *data;
+ size_t datalen;
+
+ r = table_newglobalcursor(t->table, true, &cursor);
+ if (!RET_IS_OK(r))
+ return r;
+
+ result = RET_NOTHING;
+
+ while (cursor_nextpair(t->table, cursor,
+ &key, &value, &data, &datalen)) {
+ r = parse_data(key, value, data, datalen, &pkg);
+ if (RET_WAS_ERROR(r)) {
+ result = r;
+ break;
+ }
+ r = trackedpackage_tidy(t, pkg);
+ RET_UPDATE(result, r);
+ r = tracking_saveatcursor(t, cursor, pkg);
+ RET_UPDATE(result, r);
+ trackedpackage_free(pkg);
+ }
+ r = cursor_close(t->table, cursor);
+ RET_UPDATE(result, r);
+ return result;
+}
+
+retvalue tracking_reset(trackingdb t) {
+ struct cursor *cursor;
+ retvalue result, r;
+ struct trackedpackage *pkg;
+ const char *key, *value, *data;
+ char *newdata;
+ size_t datalen, newdatalen;
+ int i;
+
+ r = table_newglobalcursor(t->table, true, &cursor);
+ if (!RET_IS_OK(r))
+ return r;
+
+ result = RET_NOTHING;
+
+ while (cursor_nextpair(t->table, cursor,
+ &key, &value, &data, &datalen)) {
+ // this would perhaps be more stable if it just replaced
+ // everything within the string just received...
+ result = parse_data(key, value, data, datalen, &pkg);
+ if (RET_WAS_ERROR(result))
+ break;
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ pkg->refcounts[i] = 0;
+ }
+ result = gen_data(pkg, &newdata, &newdatalen);
+ trackedpackage_free(pkg);
+ if (RET_IS_OK(result))
+ result = cursor_replace(t->table, cursor,
+ newdata, newdatalen);
+ free(newdata);
+ if (RET_WAS_ERROR(result))
+ break;
+ }
+ r = cursor_close(t->table, cursor);
+ RET_UPDATE(result, r);
+ return result;
+}
+
+static retvalue tracking_foreachversion(trackingdb t, struct distribution *distribution, const char *sourcename, retvalue (action)(trackingdb t, struct trackedpackage *, struct distribution *)) {
+ struct cursor *cursor;
+ retvalue result, r;
+ struct trackedpackage *pkg;
+ const char *value, *data;
+ size_t datalen;
+
+ r = table_newduplicatepairedcursor(t->table, sourcename, &cursor,
+ &value, &data, &datalen);
+ if (!RET_IS_OK(r))
+ return r;
+
+ result = RET_NOTHING;
+
+ do {
+ r = parse_data(sourcename, value, data, datalen, &pkg);
+ if (RET_WAS_ERROR(r)) {
+ result = r;
+ break;
+ }
+ if (verbose > 10)
+ printf("Processing track of '%s' version '%s'\n",
+ pkg->sourcename, pkg->sourceversion);
+ r = action(t, pkg, distribution);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r)) {
+ (void)cursor_close(t->table, cursor);
+ trackedpackage_free(pkg);
+ return r;
+ }
+ r = trackedpackage_tidy(t, pkg);
+ RET_ENDUPDATE(result, r);
+ r = tracking_saveatcursor(t, cursor, pkg);
+ RET_UPDATE(result, r);
+ trackedpackage_free(pkg);
+ } while (cursor_nextpair(t->table, cursor, NULL,
+ &value, &data, &datalen));
+ r = cursor_close(t->table, cursor);
+ RET_UPDATE(result, r);
+ return result;
+}
+
+
+static retvalue targetremovesourcepackage(trackingdb t, struct trackedpackage *pkg, struct distribution *distribution, struct target *target) {
+ size_t component_len, arch_len;
+ retvalue result, r;
+ int i;
+ const char *packagetype = atoms_packagetypes[target->packagetype];
+ const char *architecture = atoms_architectures[target->architecture];
+ const char *component = atoms_components[target->component];
+
+ if (verbose >= 15)
+ fprintf(stderr, "trace: targetremovesourcepackage(pkg={sourcename: %s, sourceversion: %s}, distribution.codename=%s, target.identifier=%s) called.\n",
+ pkg->sourcename, pkg->sourceversion, distribution->codename, target->identifier);
+
+ result = RET_NOTHING;
+
+ component_len = strlen(component);
+ arch_len = strlen(architecture);
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ const char *s, *s2, *basefilename, *filekey = pkg->filekeys.values[i];
+ char *packagename, *packageversion;
+ struct package package;
+ struct strlist filekeys;
+ bool savedstaletracking;
+
+ if (pkg->refcounts[i] <= 0)
+ continue;
+ if (strncmp(filekey, "pool/", 5) != 0)
+ continue;
+ if (strncmp(filekey+5, component,
+ component_len) != 0)
+ continue;
+ if (filekey[5+component_len] != '/')
+ continue;
+ /* check this file could actuall be in this target */
+ if (pkg->filetypes[i] == ft_ALL_BINARY) {
+ if (target->packagetype == pt_dsc)
+ continue;
+ s = strrchr(filekey, '.');
+ if (s == NULL)
+ continue;
+ if (strcmp(s+1, packagetype) != 0)
+ continue;
+ } else if (pkg->filetypes[i] == ft_SOURCE) {
+ if (target->packagetype != pt_dsc)
+ continue;
+ s = strrchr(filekey, '.');
+ if (s == NULL)
+ continue;
+ if (strcmp(s+1, "dsc") != 0)
+ continue;
+ } else if (pkg->filetypes[i] == ft_ARCH_BINARY) {
+ if (target->packagetype == pt_dsc)
+ continue;
+ s = strrchr(filekey, '_');
+ if (s == NULL)
+ continue;
+ s++;
+ if (strncmp(s, architecture, arch_len) != 0
+ || s[arch_len] != '.'
+ || strcmp(s+arch_len+1, packagetype) != 0)
+ continue;
+ } else
+ continue;
+ /* get this package, check it has the right source and version,
+ * and if yes, remove... */
+ basefilename = strrchr(filekey, '/');
+ if (basefilename == NULL)
+ basefilename = filekey;
+ else
+ basefilename++;
+ s = strchr(basefilename, '_');
+ packagename = strndup(basefilename, s - basefilename);
+ if (FAILEDTOALLOC(packagename))
+ return RET_ERROR_OOM;
+ s2 = strrchr(s, '.');
+ packageversion = strndup(s + 1, s2 - s - 1);
+ if (FAILEDTOALLOC(packageversion))
+ return RET_ERROR_OOM;
+ r = package_get(target, packagename, packageversion, &package);
+ if (RET_WAS_ERROR(r)) {
+ free(packagename);
+ free(packageversion);
+ return r;
+ }
+ if (r == RET_NOTHING) {
+ if (pkg->filetypes[i] != ft_ALL_BINARY
+ && verbose >= -1) {
+ fprintf(stderr,
+"Warning: tracking data might be inconsistent:\n"
+"cannot find '%s=%s' in '%s', but '%s' should be there.\n",
+ packagename, packageversion, target->identifier,
+ filekey);
+ }
+ free(packagename);
+ free(packageversion);
+ continue;
+ }
+ // TODO: ugly
+ package.pkgname = packagename;
+ packagename = NULL;
+ free(packageversion);
+
+ r = package_getsource(&package);
+ assert (r != RET_NOTHING);
+ if (RET_WAS_ERROR(r)) {
+ package_done(&package);
+ return r;
+ }
+ if (strcmp(package.source, pkg->sourcename) != 0) {
+ if (pkg->filetypes[i] != ft_ALL_BINARY
+ && verbose >= -1) {
+ fprintf(stderr,
+"Warning: tracking data might be inconsistent:\n"
+"'%s' has '%s' of source '%s', but source '%s' contains '%s'.\n",
+ target->identifier, package.name,
+ package.source, pkg->sourcename,
+ filekey);
+ }
+ package_done(&package);
+ continue;
+ }
+ if (strcmp(package.sourceversion, pkg->sourceversion) != 0) {
+ if (pkg->filetypes[i] != ft_ALL_BINARY
+ && verbose >= -1) {
+ fprintf(stderr,
+"Warning: tracking data might be inconsistent:\n"
+"'%s' has '%s' of source version '%s', but version '%s' contains '%s'.\n",
+ target->identifier, package.name,
+ package.sourceversion,
+ pkg->sourceversion,
+ filekey);
+ }
+ package_done(&package);
+ continue;
+ }
+ r = target->getfilekeys(package.control, &filekeys);
+ assert (r != RET_NOTHING);
+ if (RET_WAS_ERROR(r)) {
+ package_done(&package);
+ return r;
+ }
+
+ /* we remove the tracking data outself, so this is not
+ * told to remove the tracking data, so it might mark things
+ * as stale, which we do not want.. */
+ savedstaletracking = target->staletracking;
+ r = package_remove(&package, distribution->logger, NULL);
+ target->staletracking = savedstaletracking;
+ package_done(&package);
+ assert (r != RET_NOTHING);
+ if (RET_WAS_ERROR(r)) {
+ strlist_done(&filekeys);
+ return r;
+ }
+ trackedpackage_removefilekeys(t, pkg, &filekeys);
+ strlist_done(&filekeys);
+ result = RET_OK;
+ }
+ return result;
+}
+
+/* Try to remove all packages causing refcounts in this tracking record */
+static retvalue removesourcepackage(trackingdb t, struct trackedpackage *pkg, struct distribution *distribution) {
+ struct target *target;
+ retvalue result, r;
+ int i;
+
+ if (verbose >= 15)
+ fprintf(stderr, "trace: removesourcepackage(pkg={sourcename: %s, sourceversion: %s}, distribution={codename: %s}) called.\n",
+ pkg->sourcename, pkg->sourceversion, distribution->codename);
+
+ result = RET_NOTHING;
+ for (target = distribution->targets ; target != NULL ;
+ target = target->next) {
+ r = target_initpackagesdb(target, READWRITE);
+ RET_ENDUPDATE(result, r);
+ if (RET_IS_OK(r)) {
+ r = targetremovesourcepackage(t, pkg,
+ distribution, target);
+ RET_UPDATE(result, r);
+ RET_UPDATE(distribution->status, r);
+ r = target_closepackagesdb(target);
+ RET_ENDUPDATE(result, r);
+ RET_ENDUPDATE(distribution->status, r);
+ if (RET_WAS_ERROR(result))
+ return result;
+ }
+ }
+ for (i = 0 ; i < pkg->filekeys.count ; i++) {
+ const char *filekey = pkg->filekeys.values[i];
+
+ if (pkg->refcounts[i] <= 0)
+ continue;
+ if (pkg->filetypes[i] != ft_ALL_BINARY &&
+ pkg->filetypes[i] != ft_SOURCE &&
+ pkg->filetypes[i] != ft_ARCH_BINARY)
+ continue;
+ fprintf(stderr,
+"There was an inconsistency in the tracking data of '%s':\n"
+"'%s' has refcount > 0, but was nowhere found.\n",
+ distribution->codename,
+ filekey);
+ pkg->refcounts[i] = 0;
+ }
+ return result;
+}
+
+retvalue tracking_removepackages(trackingdb t, struct distribution *distribution, const char *sourcename, /*@null@*/const char *version) {
+ struct trackedpackage *pkg;
+ retvalue result, r;
+
+ if (verbose >= 15)
+ fprintf(stderr, "trace: tracking_removepackages(distribution={codename: %s}, sourcename=%s, version=%s) called.\n",
+ distribution->codename, sourcename, version);
+
+ if (version == NULL)
+ return tracking_foreachversion(t, distribution,
+ sourcename, removesourcepackage);
+ result = tracking_get(t, sourcename, version, &pkg);
+ if (RET_IS_OK(result)) {
+ result = removesourcepackage(t, pkg, distribution);
+ if (RET_IS_OK(result)) {
+ r = trackedpackage_tidy(t, pkg);
+ RET_ENDUPDATE(result, r);
+ r = tracking_save(t, pkg);
+ RET_ENDUPDATE(result, r);
+ } else
+ trackedpackage_free(pkg);
+ }
+ return result;
+}
+
+static retvalue package_retrack(struct package *package, void *data) {
+ trackingdb tracks = data;
+
+ return package->target->doretrack(package->name,
+ package->control, tracks);
+}
+
+retvalue tracking_retrack(struct distribution *d, bool needsretrack) {
+ struct target *t;
+ trackingdb tracks;
+ retvalue r, rr;
+
+ if (d->tracking == dt_NONE)
+ return RET_NOTHING;
+
+ for (t = d->targets ; !needsretrack && t != NULL ; t = t->next) {
+ if (t->staletracking)
+ needsretrack = true;
+ }
+ if (!needsretrack)
+ return RET_NOTHING;
+
+ if (verbose > 0)
+ printf("Retracking %s...\n", d->codename);
+
+ r = tracking_initialize(&tracks, d, false);
+ if (!RET_IS_OK(r))
+ return r;
+ /* first forget that any package is there*/
+ r = tracking_reset(tracks);
+ if (!RET_WAS_ERROR(r)) {
+ /* add back information about actually used files */
+ r = package_foreach(d,
+ atom_unknown, atom_unknown, atom_unknown,
+ package_retrack, NULL, tracks);
+ }
+ if (RET_IS_OK(r)) {
+ for (t = d->targets ; t != NULL ; t = t->next) {
+ t->staletracking = false;
+ }
+ }
+ if (!RET_WAS_ERROR(r)) {
+ /* now remove everything no longer needed */
+ r = tracking_tidyall(tracks);
+ }
+ rr = tracking_done(tracks, d);
+ RET_ENDUPDATE(r, rr);
+ return r;
+}