diff options
Diffstat (limited to '')
-rw-r--r-- | target.c | 1245 |
1 files changed, 1245 insertions, 0 deletions
diff --git a/target.c b/target.c new file mode 100644 index 0000000..0984fc4 --- /dev/null +++ b/target.c @@ -0,0 +1,1245 @@ +/* This file is part of "reprepro" + * Copyright (C) 2004,2005,2007,2008,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 <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <assert.h> +#include <unistd.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#include "error.h" +#include "ignore.h" +#include "mprintf.h" +#include "strlist.h" +#include "names.h" +#include "chunks.h" +#include "database.h" +#include "reference.h" +#include "binaries.h" +#include "sources.h" +#include "names.h" +#include "dirs.h" +#include "dpkgversions.h" +#include "tracking.h" +#include "log.h" +#include "files.h" +#include "descriptions.h" +#include "package.h" +#include "target.h" + +static char *calc_identifier(const char *codename, component_t component, architecture_t architecture, packagetype_t packagetype) { + assert (strchr(codename, '|') == NULL); + assert (codename != NULL); assert (atom_defined(component)); + assert (atom_defined(architecture)); + assert (atom_defined(packagetype)); + if (packagetype == pt_udeb) + return mprintf("u|%s|%s|%s", codename, + atoms_components[component], + atoms_architectures[architecture]); + else if (packagetype == pt_ddeb) + return mprintf("d|%s|%s|%s", codename, + atoms_components[component], + atoms_architectures[architecture]); + else + return mprintf("%s|%s|%s", codename, + atoms_components[component], + atoms_architectures[architecture]); +} + + +static retvalue target_initialize(/*@dependant@*/struct distribution *distribution, component_t component, architecture_t architecture, packagetype_t packagetype, get_version getversion, get_installdata getinstalldata, get_architecture getarchitecture, get_filekeys getfilekeys, get_checksums getchecksums, get_sourceandversion getsourceandversion, do_reoverride doreoverride, do_retrack doretrack, complete_checksums docomplete, /*@null@*//*@only@*/char *directory, /*@dependent@*/const struct exportmode *exportmode, bool readonly, bool noexport, /*@out@*/struct target **d) { + struct target *t; + + assert(exportmode != NULL); + if (FAILEDTOALLOC(directory)) + return RET_ERROR_OOM; + + t = zNEW(struct target); + if (FAILEDTOALLOC(t)) { + free(directory); + return RET_ERROR_OOM; + } + t->relativedirectory = directory; + t->exportmode = exportmode; + t->distribution = distribution; + assert (atom_defined(component)); + t->component = component; + assert (atom_defined(architecture)); + t->architecture = architecture; + assert (atom_defined(packagetype)); + t->packagetype = packagetype; + t->identifier = calc_identifier(distribution->codename, + component, architecture, packagetype); + if (FAILEDTOALLOC(t->identifier)) { + (void)target_free(t); + return RET_ERROR_OOM; + } + t->getversion = getversion; + t->getinstalldata = getinstalldata; + t->getarchitecture = getarchitecture; + t->getfilekeys = getfilekeys; + t->getchecksums = getchecksums; + t->getsourceandversion = getsourceandversion; + t->doreoverride = doreoverride; + t->doretrack = doretrack; + t->completechecksums = docomplete; + t->readonly = readonly; + t->noexport = noexport; + *d = t; + return RET_OK; +} + +static const char *dist_component_name(component_t component, /*@null@*/const char *fakecomponentprefix) { + const char *c = atoms_components[component]; + size_t len; + + if (fakecomponentprefix == NULL) + return c; + len = strlen(fakecomponentprefix); + if (strncmp(c, fakecomponentprefix, len) != 0) + return c; + if (c[len] != '/') + return c; + return c + len + 1; +} + +retvalue target_initialize_ubinary(struct distribution *d, component_t component, architecture_t architecture, const struct exportmode *exportmode, bool readonly, bool noexport, const char *fakecomponentprefix, struct target **target) { + return target_initialize(d, component, architecture, pt_udeb, + binaries_getversion, + binaries_getinstalldata, + binaries_getarchitecture, + binaries_getfilekeys, binaries_getchecksums, + binaries_getsourceandversion, + ubinaries_doreoverride, binaries_retrack, + binaries_complete_checksums, + mprintf("%s/debian-installer/binary-%s", + dist_component_name(component, + fakecomponentprefix), + atoms_architectures[architecture]), + exportmode, readonly, noexport, target); +} +retvalue target_initialize_dbinary(struct distribution *d, component_t component, architecture_t architecture, const struct exportmode *exportmode, bool readonly, bool noexport, const char *fakecomponentprefix, struct target **target) { + return target_initialize(d, component, architecture, pt_ddeb, + binaries_getversion, + binaries_getinstalldata, + binaries_getarchitecture, + binaries_getfilekeys, binaries_getchecksums, + binaries_getsourceandversion, + /* we use the main overrides */ + binaries_doreoverride, binaries_retrack, + binaries_complete_checksums, + /* FIXME: we don't know what the Debian archive layout + * is going to look like yet, so take a guess based + * on udebs */ + mprintf("%s/debug/binary-%s", + dist_component_name(component, + fakecomponentprefix), + atoms_architectures[architecture]), + exportmode, readonly, noexport, target); +} +retvalue target_initialize_binary(struct distribution *d, component_t component, architecture_t architecture, const struct exportmode *exportmode, bool readonly, bool noexport, const char *fakecomponentprefix, struct target **target) { + return target_initialize(d, component, architecture, pt_deb, + binaries_getversion, + binaries_getinstalldata, + binaries_getarchitecture, + binaries_getfilekeys, binaries_getchecksums, + binaries_getsourceandversion, + binaries_doreoverride, binaries_retrack, + binaries_complete_checksums, + mprintf("%s/binary-%s", + dist_component_name(component, + fakecomponentprefix), + atoms_architectures[architecture]), + exportmode, readonly, noexport, target); +} + +retvalue target_initialize_source(struct distribution *d, component_t component, const struct exportmode *exportmode, bool readonly, bool noexport, const char *fakecomponentprefix, struct target **target) { + return target_initialize(d, component, architecture_source, pt_dsc, + sources_getversion, + sources_getinstalldata, + sources_getarchitecture, + sources_getfilekeys, sources_getchecksums, + sources_getsourceandversion, + sources_doreoverride, sources_retrack, + sources_complete_checksums, + mprintf("%s/source", dist_component_name(component, + fakecomponentprefix)), + exportmode, readonly, noexport, target); +} + +retvalue target_free(struct target *target) { + retvalue result = RET_OK; + + if (target == NULL) + return RET_OK; + if (target->packages != NULL) { + result = target_closepackagesdb(target); + } else + result = RET_OK; + if (target->wasmodified && !target->noexport) { + fprintf(stderr, +"Warning: database '%s' was modified but no index file was exported.\n" +"Changes will only be visible after the next 'export'!\n", + target->identifier); + } + + target->distribution = NULL; + free(target->identifier); + free(target->relativedirectory); + free(target); + return result; +} + +/* This opens up the database, if db != NULL, *db will be set to it.. */ +retvalue target_initpackagesdb(struct target *target, bool readonly) { + retvalue r; + + if (!readonly && target->readonly) { + fprintf(stderr, +"Error trying to open '%s' read-write in read-only distribution '%s'\n", + target->identifier, + target->distribution->codename); + return RET_ERROR; + } + + assert (target->packages == NULL); + if (target->packages != NULL) + return RET_OK; + r = database_openpackages(target->identifier, readonly, + &target->packages); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) { + target->packages = NULL; + return r; + } + return r; +} + +/* this closes databases... */ +retvalue target_closepackagesdb(struct target *target) { + retvalue r; + + if (target->packages == NULL) { + fprintf(stderr, "Internal Warning: Double close!\n"); + r = RET_OK; + } else { + r = table_close(target->packages); + target->packages = NULL; + } + return r; +} + +/* Remove a package from the given target. */ +retvalue package_remove(struct package *old, struct logger *logger, struct trackingdata *trackingdata) { + struct strlist files; + retvalue result, r; + char *key; + + assert (old->target != NULL && old->target->packages != NULL); + + (void)package_getversion(old); + if (verbose >= 15) + fprintf(stderr, "trace: package_remove(old.name=%s, old.version=%s, old.target.identifier=%s) called.\n", + old->name, old->version, old->target->identifier); + r = old->target->getfilekeys(old->control, &files); + if (RET_WAS_ERROR(r)) { + return r; + } + if (trackingdata != NULL) { + (void)package_getsource(old); + } + if (verbose > 0) + printf("removing '%s=%s' from '%s'...\n", + old->name, old->version, old->target->identifier); + key = package_primarykey(old->name, old->version); + result = table_deleterecord(old->target->packages, key, false); + free(key); + if (RET_IS_OK(result)) { + old->target->wasmodified = true; + if (trackingdata != NULL && old->source != NULL + && old->sourceversion != NULL) { + r = trackingdata_remove(trackingdata, + old->source, old->sourceversion, &files); + RET_UPDATE(result, r); + } + if (trackingdata == NULL) + old->target->staletracking = true; + if (logger != NULL) + logger_log(logger, old->target, old->name, + NULL, + (old->version == NULL) + ? "#unparseable#" + : old->version, + NULL, &files, + NULL, NULL); + r = references_delete(old->target->identifier, &files, NULL); + RET_UPDATE(result, r); + } + strlist_done(&files); + return result; +} + +/* Remove a package from the given target. */ +retvalue target_removepackage(struct target *target, struct logger *logger, const char *name, const char *version, struct trackingdata *trackingdata) { + struct package old; + retvalue r; + + assert(target != NULL && target->packages != NULL && name != NULL); + if (verbose >= 15) + fprintf(stderr, "trace: target_removepackage(target.identifier=%s, name=%s, version=%s) called.\n", + target->identifier, name, version); + + r = package_get(target, name, version, &old); + if (RET_WAS_ERROR(r)) { + return r; + } + else if (r == RET_NOTHING) { + if (verbose >= 10) { + if (version == NULL) + fprintf(stderr, "Could not find '%s' in '%s'...\n", + name, target->identifier); + else + fprintf(stderr, "Could not find '%s=%s' in '%s'...\n", + name, version, target->identifier); + } + return RET_NOTHING; + } + r = package_remove(&old, logger, trackingdata); + package_done(&old); + return r; +} + +/* Like target_removepackage, but delete the package record by cursor */ +retvalue package_remove_by_cursor(struct package_cursor *tc, struct logger *logger, struct trackingdata *trackingdata) { + struct target * const target = tc->target; + struct package *old = &tc->current; + struct strlist files; + retvalue result, r; + + assert (target != NULL && target->packages != NULL); + assert (target == old->target); + + if (logger != NULL || verbose > 0) { + (void)package_getversion(old); + } + r = old->target->getfilekeys(old->control, &files); + if (RET_WAS_ERROR(r)) { + return r; + } + if (trackingdata != NULL) { + (void)package_getsource(old); + } + if (verbose > 0) + printf("removing '%s=%s' from '%s'...\n", + old->name, old->version, old->target->identifier); + result = cursor_delete(target->packages, tc->cursor, old->name, old->version); + if (RET_IS_OK(result)) { + old->target->wasmodified = true; + if (trackingdata != NULL && old->source != NULL + && old->sourceversion != NULL) { + r = trackingdata_remove(trackingdata, + old->source, old->sourceversion, &files); + RET_UPDATE(result, r); + } + if (trackingdata == NULL) + old->target->staletracking = true; + if (logger != NULL) + logger_log(logger, old->target, old->name, + NULL, + (old->version == NULL) + ? "#unparseable" + : old->version, + NULL, &files, + NULL, NULL); + r = references_delete(old->target->identifier, &files, NULL); + RET_UPDATE(result, r); + } + strlist_done(&files); + return result; +} + +static retvalue archive_package(struct target *target, const struct package *package, const struct strlist *files, /*@null@*/const char *causingrule, /*@null@*/const char *suitefrom) { + struct strlist filekeys; + struct target *archive_target; + struct trackingdata trackingdata; + trackingdb tracks = NULL; + bool close_database, close_trackingdb = false; + retvalue result, r; + + if (verbose >= 15) + fprintf(stderr, "trace: archive_package(target.identifier=%s, package->name=%s, package->version=%s) called.\n", + target->identifier, package->name, package->version); + + if (target->distribution->archive != NULL) { + archive_target = distribution_gettarget(target->distribution->archive, target->component, + target->architecture, target->packagetype); + if (archive_target == NULL) { + fprintf(stderr, +"Warning: Cannot archive '%s=%s' from '%s' to '%s' since '%s' has no matching component/architecture/packagetype.\n", + package->name, package->version, target->distribution->codename, + target->distribution->archive->codename, + target->distribution->archive->codename); + } else { + close_database = archive_target->packages == NULL; + if (close_database) { + result = target_initpackagesdb(archive_target, READWRITE); + if (RET_WAS_ERROR(result)) { + return result; + } + } + if (files == NULL) { + result = archive_target->getfilekeys(package->control, &filekeys); + if (RET_WAS_ERROR(result)) + return result; + files = &filekeys; + } + if (archive_target->distribution->tracking != dt_NONE) { + close_trackingdb = archive_target->distribution->trackingdb == NULL; + if (close_trackingdb) { + r = tracking_initialize(&tracks, archive_target->distribution, false); + if (RET_WAS_ERROR(r)) + return r; + } else { + tracks = archive_target->distribution->trackingdb; + } + r = trackingdata_summon(tracks, package->source, package->version, &trackingdata); + if (RET_WAS_ERROR(r)) + return r; + } + // TODO: Check whether this is the best place to set 'selected' + target->distribution->archive->selected = true; + result = distribution_prepareforwriting(archive_target->distribution); + if (!RET_WAS_ERROR(result)) { + result = target_addpackage(archive_target, target->distribution->archive->logger, + package->name, package->version, package->control, + files, false, (tracks != NULL) ? &trackingdata : NULL, + target->architecture, causingrule, suitefrom); + RET_UPDATE(target->distribution->archive->status, result); + } + if (close_database) { + r = target_closepackagesdb(archive_target); + RET_UPDATE(result, r); + } + if (tracks != NULL) { + r = trackingdata_finish(tracks, &trackingdata); + RET_UPDATE(result, r); + if (close_trackingdb) { + r = tracking_done(tracks, archive_target->distribution); + RET_UPDATE(result, r); + } + } + if (RET_WAS_ERROR(result)) { + return result; + } + } + } + return RET_OK; +} + +static retvalue addpackages(struct target *target, const char *packagename, const char *controlchunk, const char *version, const struct strlist *files, /*@null@*/const struct package *old, /*@null@*/const struct strlist *oldfiles, /*@null@*/struct logger *logger, /*@null@*/struct trackingdata *trackingdata, architecture_t architecture, /*@null@*/const char *causingrule, /*@null@*/const char *suitefrom) { + + retvalue result = RET_OK, r; + char *key; + struct table *table = target->packages; + enum filetype filetype; + + if (verbose >= 15) + fprintf(stderr, "trace: addpackages(target.identifier=%s, packagename=%s, version=%s, old->version=%s) called.\n", + target->identifier, packagename, version, old != NULL ? old->version : NULL); + assert (atom_defined(architecture)); + + if (architecture == architecture_source) + filetype = ft_SOURCE; + else if (architecture == architecture_all) + filetype = ft_ALL_BINARY; + else + filetype = ft_ARCH_BINARY; + + /* mark it as needed by this distribution */ + + r = references_insert(target->identifier, files, oldfiles); + + if (RET_WAS_ERROR(r)) + return r; + + /* Add package to the distribution's database */ + + if (old != NULL && old->control != NULL) { + key = package_primarykey(old->name, old->version); + r = archive_package(target, old, oldfiles, causingrule, suitefrom); + RET_UPDATE(result, r); + if (RET_IS_OK(r)) { + r = table_deleterecord(table, key, false); + RET_UPDATE(result, r); + } + free(key); + } + + key = package_primarykey(packagename, version); + r = table_adduniqrecord(table, key, controlchunk); + free(key); + + if (RET_WAS_ERROR(r)) + return r; + + if (logger != NULL) { + logger_log(logger, target, packagename, + version, + /* the old version, NULL if there is + * no old package, + * "#unparseable" if there is old but + * no version available */ + (old==NULL) + ? NULL + : (old->version == NULL) + ? "#unparseable" + : old->version, + files, oldfiles, causingrule, suitefrom); + } + + if (trackingdata != NULL) { + r = trackingdata_insert(trackingdata, + filetype, files, old, oldfiles); + RET_UPDATE(result, r); + } + + /* remove old references to files */ + + if (oldfiles != NULL) { + r = references_delete(target->identifier, oldfiles, files); + RET_UPDATE(result, r); + } + + return result; +} + +retvalue target_addpackage(struct target *target, struct logger *logger, const char *name, const char *version, const char *control, const struct strlist *filekeys, bool downgrade, struct trackingdata *trackingdata, architecture_t architecture, const char *causingrule, const char *suitefrom) { + struct strlist oldfilekeys, *ofk = NULL; + char *newcontrol; + struct package_cursor iterator = {NULL}; + struct package old; + retvalue r; + + if (verbose >= 15) + fprintf(stderr, "trace: target_addpackage(target.identifier=%s, name=%s, version=%s) called.\n", + target->identifier, name, version); + assert(target->packages!=NULL); + + r = package_get(target, name, version, &old); + if (RET_WAS_ERROR(r)) { + package_done(&old); + return r; + } else if (RET_IS_OK(r)) { + if (!downgrade) { + fprintf(stderr, "Skipping inclusion of '%s' '%s' in '%s', as this version already exists.\n", + name, version, target->identifier); + package_done(&old); + return RET_NOTHING; + } else { + r = package_getversion(&old); + if (RET_WAS_ERROR(r) && !IGNORING(brokenold, "Error parsing old version!\n")) { + package_done(&old); + return r; + } + fprintf(stderr, "Warning: replacing '%s' version '%s' with equal version '%s' in '%s'!\n", + name, old.version, version, target->identifier); + } + } else if (target->distribution->limit > 0) { + package_done(&old); + r = package_openduplicateiterator(target, name, target->distribution->limit - 1, &iterator); + if (RET_WAS_ERROR(r)) { + return r; + } + if (RET_IS_OK(r)) { + r = package_getversion(&iterator.current); + if (RET_WAS_ERROR(r) && !IGNORING(brokenold, "Error parsing old version!\n")) { + retvalue r2 = package_closeiterator(&iterator); + RET_ENDUPDATE(r, r2); + return r; + } + if (RET_IS_OK(r)) { + int versioncmp; + + r = dpkgversions_cmp(version, iterator.current.version, &versioncmp); + if (RET_WAS_ERROR(r)) { + if (!IGNORING(brokenversioncmp, "Parse errors processing versions of %s.\n", name)) { + retvalue r2 = package_closeiterator(&iterator); + RET_ENDUPDATE(r, r2); + return r; + } + } else if (versioncmp < 0) { + // new Version is older than the old version that will be replaced + if (!downgrade) { + fprintf(stderr, +"Skipping inclusion of '%s' '%s' in '%s', as it has already '%s'.\n", + name, version, + target->identifier, + iterator.current.version); + package_done(&old); + return RET_NOTHING; + } else { + fprintf(stderr, +"Warning: downgrading '%s' from '%s' to '%s' in '%s'!\n", name, + iterator.current.version, + version, + target->identifier); + } + } + old.target = target; + old.name = iterator.current.name; + old.control = iterator.current.control; + old.controllen = iterator.current.controllen; + old.version = iterator.current.version; + } + } + } else { + // Keep all package versions in the archive. + package_done(&old); + } + + if (old.name != NULL) { + r = target->getfilekeys(old.control, &oldfilekeys); + ofk = &oldfilekeys; + if (RET_WAS_ERROR(r)) { + if (IGNORING(brokenold, +"Error parsing files belonging to installed version of %s!\n", name)) { + ofk = NULL; + } else { + package_done(&old); + if (iterator.cursor != NULL) { + retvalue r2 = package_closeiterator(&iterator); + RET_ENDUPDATE(r, r2); + } + return r; + } + } else if (trackingdata != NULL) { + r = package_getsource(&old); + if (RET_WAS_ERROR(r)) { + strlist_done(ofk); + if (IGNORING(brokenold, +"Error searching for source name of installed version of %s!\n", name)) { + // TODO: free something of oldfilekeys? + ofk = NULL; + } else { + package_done(&old); + if (iterator.cursor != NULL) { + retvalue r2 = package_closeiterator(&iterator); + RET_ENDUPDATE(r, r2); + } + return r; + } + } + } + } + + newcontrol = NULL; + r = description_addpackage(target, name, control, &newcontrol); + if (RET_IS_OK(r)) + control = newcontrol; + if (!RET_WAS_ERROR(r)) + r = addpackages(target, name, control, + version, + filekeys, + &old, ofk, + logger, + trackingdata, architecture, + causingrule, suitefrom); + if (ofk != NULL) + strlist_done(ofk); + if (RET_IS_OK(r)) { + target->wasmodified = true; + if (trackingdata == NULL) + target->staletracking = true; + } + free(newcontrol); + package_done(&old); + + if (iterator.cursor != NULL) { + // Remove all older versions (that exceed the current limit) + retvalue r2; + while(package_next(&iterator)) { + r2 = package_getversion(&iterator.current); + RET_UPDATE(r, r2); + if (RET_WAS_ERROR(r2)) + continue; + if (strcmp(version, iterator.current.version) == 0) { + // Do not archive/remove the newly added package! + continue; + } + r2 = package_getsource(&iterator.current); + if (RET_WAS_ERROR(r2)) + continue; + r2 = archive_package(target, &iterator.current, NULL, causingrule, suitefrom); + RET_UPDATE(r, r2); + if (RET_WAS_ERROR(r2)) + continue; + r2 = package_remove_by_cursor(&iterator, logger, trackingdata); + RET_UPDATE(r, r2); + } + r2 = package_closeiterator(&iterator); + RET_ENDUPDATE(r, r2); + } + return r; +} + +retvalue target_checkaddpackage(struct target *target, const char *name, const char *version, bool tracking, bool permitnewerold) { + struct strlist oldfilekeys, *ofk; + struct package old; + retvalue r; + + assert(target->packages!=NULL); + + r = package_get(target, name, NULL, &old); + if (RET_WAS_ERROR(r)) + return r; + if (r == RET_NOTHING) { + ofk = NULL; + } else { + int versioncmp; + + r = package_getversion(&old); + if (RET_WAS_ERROR(r)) { + fprintf(stderr, +"Error extracting version from old '%s' in '%s'. Database corrupted?\n", name, target->identifier); + package_done(&old); + return r; + } + assert (RET_IS_OK(r)); + + r = dpkgversions_cmp(version, old.version, &versioncmp); + if (RET_WAS_ERROR(r)) { + fprintf(stderr, +"Parse error comparing version '%s' of '%s' with old version '%s' in '%s'\n.", + version, name, old.version, + target->identifier); + package_done(&old); + return r; + } + if (versioncmp < 0) { + if (!permitnewerold) { + fprintf(stderr, +"Error: trying to put version '%s' of '%s' in '%s',\n" +"while there already is the stricly newer '%s' in there.\n" +"(To ignore this error add Permit: older_version.)\n", + version, name, + target->identifier, + old.version); + package_done(&old); + return RET_ERROR; + } else if (verbose > 2) { + printf("Puting version '%s' of '%s' in '%s', while there already is '%s' in there.\n", + version, name, target->identifier, old.version); + } + } else if (versioncmp == 0) { + if (verbose > 2) { + printf( +"Will not put '%s' in '%s', as already there with same version '%s'.\n", + name, target->identifier, + old.version); + } + package_done(&old); + return RET_NOTHING; + } + r = target->getfilekeys(old.control, &oldfilekeys); + ofk = &oldfilekeys; + if (RET_WAS_ERROR(r)) { + fprintf(stderr, +"Error extracting installed files from old '%s' in '%s'.\nDatabase corrupted?\n", + name, target->identifier); + package_done(&old); + return r; + } + if (tracking) { + r = package_getsource(&old); + if (RET_WAS_ERROR(r)) { + fprintf(stderr, +"Error extracting source name and version from '%s' in '%s'. Database corrupted?\n", + name, target->identifier); + strlist_done(ofk); + package_done(&old); + return r; + } + /* TODO: check if tracking would succeed */ + } + strlist_done(ofk); + package_done(&old); + } + return RET_OK; +} + +retvalue target_rereference(struct target *target) { + retvalue result, r; + struct package_cursor iterator; + + if (verbose > 1) { + if (verbose > 2) + printf("Unlocking dependencies of %s...\n", + target->identifier); + else + printf("Rereferencing %s...\n", + target->identifier); + } + + result = references_remove(target->identifier); + if (verbose > 2) + printf("Referencing %s...\n", target->identifier); + + r = package_openiterator(target, READONLY, true, &iterator); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) + return r; + while (package_next(&iterator)) { + struct strlist filekeys; + + r = target->getfilekeys(iterator.current.control, &filekeys); + RET_UPDATE(result, r); + if (!RET_IS_OK(r)) + continue; + if (verbose > 10) { + fprintf(stderr, "adding references to '%s' for '%s': ", + target->identifier, + iterator.current.name); + (void)strlist_fprint(stderr, &filekeys); + (void)putc('\n', stderr); + } + r = references_insert(target->identifier, &filekeys, NULL); + strlist_done(&filekeys); + RET_UPDATE(result, r); + } + r = package_closeiterator(&iterator); + RET_ENDUPDATE(result, r); + return result; +} + +retvalue package_referenceforsnapshot(struct package *package, void *data) { + const char *identifier = data; + struct strlist filekeys; + retvalue r; + + r = package->target->getfilekeys(package->control, &filekeys); + if (RET_WAS_ERROR(r)) + return r; + if (verbose > 15) { + fprintf(stderr, "adding references to '%s' for '%s': ", + identifier, package->name); + (void)strlist_fprint(stderr, &filekeys); + (void)putc('\n', stderr); + } + r = references_add(identifier, &filekeys); + strlist_done(&filekeys); + return r; +} + +retvalue package_check(struct package *package, UNUSED(void *pd)) { + struct target *target = package->target; + struct checksumsarray files; + retvalue result = RET_OK, r; + + r = package_getversion(package); + if (!RET_IS_OK(r)) { + fprintf(stderr, +"Error extraction version number from package control info of '%s'!\n", + package->name); + if (r == RET_NOTHING) + r = RET_ERROR_MISSING; + return r; + } + r = package_getarchitecture(package); + if (!RET_IS_OK(r)) { + fprintf(stderr, +"Error extraction architecture from package control info of '%s'!\n", + package->name); + if (r == RET_NOTHING) + r = RET_ERROR_MISSING; + return r; + } + /* check if the architecture matches the architecture where this + * package belongs to. */ + if (target->architecture != package->architecture && + package->architecture != architecture_all) { + fprintf(stderr, +"Wrong architecture '%s' of package '%s' in '%s'!\n", + atoms_architectures[package->architecture], + package->name, target->identifier); + result = RET_ERROR; + } + r = target->getchecksums(package->control, &files); + if (r == RET_NOTHING) + r = RET_ERROR; + if (RET_WAS_ERROR(r)) { + fprintf(stderr, +"Error extracting information of package '%s'!\n", + package->name); + return r; + } + + if (verbose > 10) { + fprintf(stderr, "checking files of '%s'\n", package->name); + } + r = files_expectfiles(&files.names, files.checksums); + if (RET_WAS_ERROR(r)) { + fprintf(stderr, "Files are missing for '%s'!\n", package->name); + } + RET_UPDATE(result, r); + if (verbose > 10) { + (void)fprintf(stderr, "checking references to '%s' for '%s': ", + target->identifier, package->name); + (void)strlist_fprint(stderr, &files.names); + (void)putc('\n', stderr); + } + r = references_check(target->identifier, &files.names); + RET_UPDATE(result, r); + checksumsarray_done(&files); + return result; +} + +/* Reapply override information */ + +retvalue target_reoverride(struct target *target, struct distribution *distribution) { + struct package_cursor iterator; + retvalue result, r; + + assert(target->packages == NULL); + assert(distribution != NULL); + + if (verbose > 1) { + fprintf(stderr, +"Reapplying overrides packages in '%s'...\n", + target->identifier); + } + + r = package_openiterator(target, READWRITE, true, &iterator); + if (!RET_IS_OK(r)) + return r; + result = RET_NOTHING; + while (package_next(&iterator)) { + char *newcontrolchunk = NULL; + + r = target->doreoverride(target, iterator.current.name, + iterator.current.control, + &newcontrolchunk); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) { + if (verbose > 0) + (void)fputs( +"target_reoverride: Stopping procession of further packages due to previous errors\n", + stderr); + break; + } + if (RET_IS_OK(r)) { + r = package_newcontrol_by_cursor(&iterator, + newcontrolchunk, strlen(newcontrolchunk)); + free(newcontrolchunk); + if (RET_WAS_ERROR(r)) { + result = r; + break; + } + target->wasmodified = true; + } + } + r = package_closeiterator(&iterator); + RET_ENDUPDATE(result, r); + return result; +} + +/* Readd checksum information */ + +static retvalue complete_package_checksums(struct target *target, const char *control, char **n) { + struct checksumsarray files; + retvalue r; + + r = target->getchecksums(control, &files); + if (!RET_IS_OK(r)) + return r; + + r = files_checkorimprove(&files.names, files.checksums); + if (!RET_IS_OK(r)) { + checksumsarray_done(&files); + return r; + } + r = target->completechecksums(control, + &files.names, files.checksums, n); + checksumsarray_done(&files); + return r; +} + +retvalue target_redochecksums(struct target *target, struct distribution *distribution) { + struct package_cursor iterator; + retvalue result, r; + + assert(target->packages == NULL); + assert(distribution != NULL); + + if (verbose > 1) { + fprintf(stderr, +"Redoing checksum information for packages in '%s'...\n", + target->identifier); + } + + r = package_openiterator(target, READWRITE, true, &iterator); + if (!RET_IS_OK(r)) + return r; + result = RET_NOTHING; + while (package_next(&iterator)) { + char *newcontrolchunk = NULL; + + r = complete_package_checksums(target, iterator.current.control, + &newcontrolchunk); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + if (RET_IS_OK(r)) { + r = package_newcontrol_by_cursor(&iterator, + newcontrolchunk, strlen(newcontrolchunk)); + free(newcontrolchunk); + if (RET_WAS_ERROR(r)) { + result = r; + break; + } + target->wasmodified = true; + } + } + r = package_closeiterator(&iterator); + RET_ENDUPDATE(result, r); + return result; +} + +/* export a database */ + +retvalue target_export(struct target *target, bool onlyneeded, bool snapshot, struct release *release) { + retvalue result; + bool onlymissing; + + assert (!target->noexport); + + if (verbose > 5) { + if (onlyneeded) + printf(" looking for changes in '%s'...\n", + target->identifier); + else + printf(" exporting '%s'...\n", target->identifier); + } + + /* not exporting if file is already there? */ + onlymissing = onlyneeded && !target->wasmodified; + + result = export_target(target->relativedirectory, target, + target->exportmode, release, onlymissing, snapshot); + + if (!RET_WAS_ERROR(result) && !snapshot) { + target->saved_wasmodified = + target->saved_wasmodified || target->wasmodified; + target->wasmodified = false; + } + return result; +} + +retvalue package_rerunnotifiers(struct package *package, UNUSED(void *data)) { + struct target *target = package->target; + struct logger *logger = target->distribution->logger; + struct strlist filekeys; + retvalue r; + + r = package_getversion(package); + if (!RET_IS_OK(r)) { + fprintf(stderr, +"Error extraction version number from package control info of '%s'!\n", + package->name); + if (r == RET_NOTHING) + r = RET_ERROR_MISSING; + return r; + } + r = target->getfilekeys(package->control, &filekeys); + if (RET_WAS_ERROR(r)) { + fprintf(stderr, +"Error extracting information about used files from package '%s'!\n", + package->name); + return r; + } + r = logger_reruninfo(logger, target, package->name, package->version, + &filekeys); + strlist_done(&filekeys); + return r; +} + +retvalue package_get(struct target *target, const char *name, const char *version, struct package *pkg) { + retvalue result, r; + bool database_closed; + + if (verbose >= 15) + fprintf(stderr, "trace: package_get(target.identifier=%s, packagename=%s, version=%s) called.\n", + target->identifier, name, version); + + memset(pkg, 0, sizeof(*pkg)); + + database_closed = target->packages == NULL; + + if (database_closed) { + r = target_initpackagesdb(target, READONLY); + if (RET_WAS_ERROR(r)) + return r; + } + + if (version == NULL) { + result = table_getrecord(target->packages, true, name, + &pkg->pkgchunk, &pkg->controllen); + } else { + char *key = package_primarykey(name, version); + result = table_getrecord(target->packages, false, key, + &pkg->pkgchunk, &pkg->controllen); + free(key); + } + if (RET_IS_OK(result)) { + pkg->target = target; + pkg->name = name; + pkg->control = pkg->pkgchunk; + } + if (database_closed) { + r = target_closepackagesdb(target); + if (RET_WAS_ERROR(r)) { + package_done(pkg); + return r; + } + } + return result; +} + +retvalue package_openiterator(struct target *t, bool readonly, bool duplicate, /*@out@*/struct package_cursor *tc) { + retvalue r, r2; + struct cursor *c; + + if (verbose >= 15) + fprintf(stderr, "trace: package_openiterator(target={identifier: %s}, readonly=%s, duplicate=%s) called.\n", + t->identifier, readonly ? "true" : "false", duplicate ? "true" : "false"); + + tc->close_database = t->packages == NULL; + r = target_initpackagesdb(t, readonly); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) + return r; + r = table_newglobalcursor(t->packages, duplicate, &c); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) { + r2 = target_closepackagesdb(t); + RET_UPDATE(r, r2); + return r; + } + tc->target = t; + tc->cursor = c; + memset(&tc->current, 0, sizeof(tc->current)); + return RET_OK; +} + +retvalue package_openduplicateiterator(struct target *t, const char *name, long long skip, /*@out@*/struct package_cursor *tc) { + retvalue r, r2; + struct cursor *c; + + tc->close_database = t->packages == NULL; + if (tc->close_database) { + r = target_initpackagesdb(t, READONLY); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) + return r; + } + + memset(&tc->current, 0, sizeof(tc->current)); + r = table_newduplicatecursor(t->packages, name, skip, &c, &tc->current.name, + &tc->current.control, &tc->current.controllen); + if (!RET_IS_OK(r)) { + if (tc->close_database) { + r2 = target_closepackagesdb(t); + RET_ENDUPDATE(r, r2); + } + return r; + } + tc->current.target = t; + tc->target = t; + tc->cursor = c; + return RET_OK; +} + +bool package_next(struct package_cursor *tc) { + bool success; + + if (verbose >= 15) + fprintf(stderr, "trace: package_next(tc={current: {name: %s, version: %s}}) called.\n", tc->current.name, tc->current.version); + + package_done(&tc->current); + success = cursor_nexttempdata(tc->target->packages, tc->cursor, + &tc->current.name, &tc->current.control, + &tc->current.controllen); + if (!success) + memset(&tc->current, 0, sizeof(tc->current)); + else + tc->current.target = tc->target; + return success; +} + +retvalue package_closeiterator(struct package_cursor *tc) { + retvalue result, r; + + package_done(&tc->current); + result = cursor_close(tc->target->packages, tc->cursor); + if (tc->close_database) { + r = target_closepackagesdb(tc->target); + RET_UPDATE(result, r); + } else { + tc->target = NULL; + } + return result; +} + +retvalue package_getversion(struct package *package) { + retvalue r; + + if (package->version != NULL) + return RET_OK; + + r = package->target->getversion(package->control, &package->pkgversion); + if (RET_IS_OK(r)) { + assert (package->pkgversion != NULL); + package->version = package->pkgversion; + } + return r; +} + +retvalue package_getarchitecture(struct package *package) { + if (atom_defined(package->architecture)) + return RET_OK; + + return package->target->getarchitecture(package->control, + &package->architecture); +} + +retvalue package_getsource(struct package *package) { + retvalue r; + + if (package->source != NULL) + return RET_OK; + + r = package->target->getsourceandversion(package->control, package->name, + &package->pkgsource, &package->pkgsrcversion); + if (RET_IS_OK(r)) { + assert (package->pkgsource != NULL); + package->source = package->pkgsource; + package->sourceversion = package->pkgsrcversion; + } + return r; +} |