diff --git a/.gitignore b/.gitignore index 2574042a..4e41cbb5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,9 @@ Makefile /debian/reprepro.debhelper.* /debian/reprepro.substvars /debian/reprepro +/autom4te.cache/ +/config.h.in~ +/tests/.deps/ +/tests/testpkgs/ +/tests/testrepo/ +/tests/upstreamrepo/ diff --git a/README.md b/README.md new file mode 100644 index 00000000..ac037856 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +reprepro with multiple versions support +======================================= + +This git repository hosts a branch for reprepro which adds multiple versions +support to it. See [Debian bug #570623](https://bugs.debian.org/570623) for +details and updates. + +The upstream repository can be found on https://salsa.debian.org/brlink/reprepro + +Release Notes +============= + +The multiple-versions patch set adds following features: + +* Add shunit2 based tests (Closes: [#857302](https://bugs.debian.org/857302)) +* Support multiple versions. (Closes: [#570623](https://bugs.debian.org/570623)) +* Add the commands move, movesrc, movematched, movefilter +* Add Limit and Archive option + +How to keep multiple versions +----------------------------- + +The default behavior of this reprepro is identical to upstream's version. To +keep multiple versions of the same package in the archive, you have to set the +`Limit` option to the desired maximum amount (or to 0 for unlimited). See the +description in the man page for details. + +Database layout changes +----------------------- + +The database layout changes from the upstream release to the +multiple versions patch set. The difference is as following: + +### upstream + +* packages.db maps "package name" to "control file" without duplicates +* no packagenames.db + +### multiple versions + +* packages.db maps "package name|version" to "control file" without +duplicates +* packagenames.db maps "package name" to "package name|version" +allowing duplicates and duplicates sorted by dpkg --compare-versions +descending + +The first time the database is opened by reprepro with multiple versions +support, the database will be upgraded from the upstream layout to the multiple +versions layout. *Warning*: There is no way back (but could be done with a +simple Python script)! + +Howto rebase +============ + +1. Rebase the `multiple-versions` branch on top of the updated upstream +`master` branch and push it to https://salsa.debian.org/bdrung/reprepro/ + +2. Refresh the `multiple-versions-debian` branch by taking the upstream +`debian` branch. Apply patch `debian: Switch to dh` and +`Run shunit2 tests on build time`. Cherry-pick all commits from +`multiple-versions`. Then apply patch `Add trace debugging output`, +`debian: Update changelog` and `Add README.md describing this git branch`. diff --git a/archallflood.c b/archallflood.c index 4c7a43bc..1a373394 100644 --- a/archallflood.c +++ b/archallflood.c @@ -327,7 +327,7 @@ static retvalue floodlist_initialize(struct floodlist **fl, struct target *t) { /* Begin with the packages currently in the archive */ - r = package_openiterator(t, READONLY, &iterator); + r = package_openiterator(t, READONLY, true, &iterator); if (RET_WAS_ERROR(r)) { floodlist_free(list); return r; @@ -549,7 +549,7 @@ static retvalue floodlist_pull(struct floodlist *list, struct target *source) { struct package_cursor iterator; list->last = NULL; - r = package_openiterator(source, READONLY, &iterator); + r = package_openiterator(source, READONLY, true, &iterator); if (RET_WAS_ERROR(r)) return r; result = RET_NOTHING; diff --git a/atoms.c b/atoms.c index 62f8b09e..a8309fd3 100644 --- a/atoms.c +++ b/atoms.c @@ -26,7 +26,7 @@ const char **atoms_architectures; const char **atoms_components; -const char * const packagetypes[4] = { "!!NONE!!", "dsc", "deb", "udeb" }; +const char * const packagetypes[5] = { "!!NONE!!", "dsc", "deb", "udeb", "ddeb" }; const char **atoms_packagetypes = (const char **)&packagetypes; const char **atoms_commands; static int command_count; @@ -159,6 +159,8 @@ packagetype_t packagetype_find(const char *value) { return pt_deb; else if (strcmp(value, "udeb") == 0) return pt_udeb; + else if (strcmp(value, "ddeb") == 0) + return pt_ddeb; else return atom_unknown; } @@ -169,8 +171,12 @@ packagetype_t packagetype_find_l(const char *value, size_t len) { return pt_dsc; else if (strncmp(value, "deb", 3) == 0) return pt_deb; - } else if (len == 4 && strncmp(value, "udeb", 4) == 0) - return pt_udeb; + } else if (len == 4) { + if (strncmp(value, "udeb", 4) == 0) + return pt_udeb; + else if (strncmp(value, "ddeb", 4) == 0) + return pt_ddeb; + } return atom_unknown; } diff --git a/atoms.h b/atoms.h index be787139..660adb43 100644 --- a/atoms.h +++ b/atoms.h @@ -19,6 +19,7 @@ enum atom_type { at_architecture, at_component, at_packagetype, at_command }; #define pt_dsc ((packagetype_t)1) #define pt_deb ((packagetype_t)2) #define pt_udeb ((packagetype_t)3) +#define pt_ddeb ((packagetype_t)4) #define atom_defined(a) ((a) > (atom_t)0) diff --git a/changes.c b/changes.c index 2cfb33d4..8b4b5c82 100644 --- a/changes.c +++ b/changes.c @@ -164,6 +164,8 @@ retvalue changes_parsefileline(const char *fileline, /*@out@*/filetype *result_t if (l >= 4 && memcmp(p-4, ".deb", 4) == 0) type = fe_DEB; + else if (l >= 5 && memcmp(p-5, ".ddeb", 5) == 0) + type = fe_DDEB; else if (l >= 5 && memcmp(p-5, ".udeb", 5) == 0) type = fe_UDEB; else @@ -189,7 +191,7 @@ retvalue changes_parsefileline(const char *fileline, /*@out@*/filetype *result_t if (type == fe_DEB) archend = versionstart + l - 4; else { - assert (type == fe_UDEB); + assert (type == fe_DDEB || type == fe_UDEB); archend = versionstart + l - 5; } if (archend - archstart == 6 && @@ -204,7 +206,7 @@ retvalue changes_parsefileline(const char *fileline, /*@out@*/filetype *result_t bool issignature = false; /* without those, it gets more complicated. - * It's not .deb or .udeb, so most likely a + * It's not .deb, .ddeb or .udeb, so most likely a * source file (or perhaps a log (reprepro extension)) */ /* if it uses a known compression, things are easy, diff --git a/changes.h b/changes.h index b4206d9b..d046e18c 100644 --- a/changes.h +++ b/changes.h @@ -7,7 +7,7 @@ typedef enum { fe_UNKNOWN=0, - fe_DEB, fe_UDEB, + fe_DEB, fe_UDEB, fe_DDEB, fe_DSC, fe_DIFF, fe_ORIG, fe_TAR, fe_SIG, fe_ALTSRC, @@ -15,8 +15,8 @@ typedef enum { fe_BUILDINFO } filetype; -#define FE_PACKAGE(ft) ((ft) == fe_DEB || (ft) == fe_UDEB || (ft) == fe_DSC) -#define FE_BINARY(ft) ((ft) == fe_DEB || (ft) == fe_UDEB) +#define FE_PACKAGE(ft) ((ft) == fe_DEB || (ft) == fe_UDEB || (ft) == fe_DSC || (ft) == fe_DDEB) +#define FE_BINARY(ft) ((ft) == fe_DEB || (ft) == fe_DDEB || (ft) == fe_UDEB) #define FE_SOURCE(ft) ((ft) == fe_DIFF || (ft) == fe_ORIG || (ft) == fe_TAR || (ft) == fe_DSC || (ft) == fe_UNKNOWN || (ft) == fe_ALTSRC || (ft) == fe_SIG) struct hash_data; diff --git a/checkin.c b/checkin.c index 8bedffa2..8ff7fe37 100644 --- a/checkin.c +++ b/checkin.c @@ -207,6 +207,11 @@ static retvalue newentry(struct fileentry **entry, const char *fileline, const s *ignoredlines_p = true; return RET_NOTHING; } + if (e->type == fe_DDEB && limitations_missed(packagetypes, pt_ddeb)) { + freeentries(e); + *ignoredlines_p = true; + return RET_NOTHING; + } if (e->type != fe_LOG && e->type != fe_BUILDINFO && e->architecture_into == architecture_source && strcmp(e->name, sourcename) != 0) { @@ -1243,6 +1248,15 @@ static retvalue changes_checkpkgs(struct distribution *distribution, struct chan e->filekey, e->checksums, &changes->binaries, changes->source, changes->sourceversion); + } else if (e->type == fe_DDEB) { + r = deb_prepare(&e->pkg.deb, + e->component, e->architecture_into, + e->section, e->priority, + pt_ddeb, + distribution, fullfilename, + e->filekey, e->checksums, + &changes->binaries, + changes->source, changes->sourceversion); } else if (e->type == fe_DSC) { if (!changes->isbinnmu || IGNORING(dscinbinnmu, "File '%s' looks like a source package, but this .changes looks like a binNMU\n" @@ -1291,6 +1305,15 @@ static retvalue changes_includepkgs(struct distribution *distribution, struct ch pt_deb, distribution, trackingdata); if (r == RET_NOTHING) *missed_p = true; + } else if (e->type == fe_DDEB) { + r = deb_addprepared(e->pkg.deb, + /* architecture all needs this, the rest is + * already filtered out */ + (e->architecture_into == architecture_all)? + forcearchitectures:NULL, + pt_ddeb, distribution, trackingdata); + if (r == RET_NOTHING) + *missed_p = true; } else if (e->type == fe_UDEB) { r = deb_addprepared(e->pkg.deb, /* architecture all needs this, the rest is diff --git a/checkindeb.c b/checkindeb.c index 6d03c00a..00c0de47 100644 --- a/checkindeb.c +++ b/checkindeb.c @@ -105,7 +105,21 @@ static retvalue deb_preparelocation(struct debpackage *pkg, component_t forcecom const struct overridedata *oinfo; retvalue r; - if (packagetype == pt_udeb) { + if (verbose >= 15) { + fprintf(stderr, "trace: deb_preparelocation(pkg={deb={name=%s}}, forcecomponent=%s, forcearchitectures=[", + pkg == NULL ? NULL : pkg->deb.name, atoms_components[forcecomponent]); + atomlist_fprint(stderr, at_architecture, forcearchitectures); + fprintf(stderr, "], forcesection=%s, packagetype=%s, distribution={codename=%s}, debfilename=%s) called.\n", + forcesection, atoms_packagetypes[packagetype], distribution->codename, debfilename); + } + + if (packagetype == pt_ddeb) { + /* ddebs don't have overrides */ + forcesection = "debug"; + forcepriority = "extra"; + binoverride = NULL; + components = &distribution->ddebcomponents; + } else if (packagetype == pt_udeb) { binoverride = distribution->overrides.udeb; components = &distribution->udebcomponents; } else { @@ -113,14 +127,21 @@ static retvalue deb_preparelocation(struct debpackage *pkg, component_t forcecom components = &distribution->components; } - oinfo = override_search(binoverride, pkg->deb.name); - *oinfo_ptr = oinfo; - if (forcesection == NULL) { - forcesection = override_get(oinfo, SECTION_FIELDNAME); - } - if (forcepriority == NULL) { - forcepriority = override_get(oinfo, PRIORITY_FIELDNAME); + if (binoverride == NULL) { + oinfo = NULL; + } else { + oinfo = override_search(binoverride, pkg->deb.name); + + if (forcesection == NULL) { + forcesection = override_get(oinfo, SECTION_FIELDNAME); + } + + if (forcepriority == NULL) { + forcepriority = override_get(oinfo, PRIORITY_FIELDNAME); + } } + *oinfo_ptr = oinfo; + if (!atom_defined(forcecomponent)) { const char *fc; @@ -261,7 +282,29 @@ retvalue deb_prepare(/*@out@*/struct debpackage **deb, component_t forcecomponen base = NULL; } - if (!strlist_in(allowed_binaries, packagenametocheck) && + if (packagetype == pt_ddeb) { + /* ddebs are allowed if they are an allowed + * binary + "-dbgsym" */ + int i; + bool found = false; + + for (i = 0; i < allowed_binaries->count; i++) { + const char *s = allowed_binaries->values[i]; + size_t len = strlen(s); + + if (strncmp(s, pkg->deb.name, len) == 0 && + strcmp(pkg->deb.name + len, "-dbgsym") == 0) { + found = true; + } + } + + if (!found && !IGNORING(surprisingbinary, + "'%s' has packagename '%s' not corresponding to a .deb listed in the .changes file!\n", + debfilename, pkg->deb.name)) { + deb_free(pkg); + return RET_ERROR; + } + } else if (!strlist_in(allowed_binaries, packagenametocheck) && !IGNORING(surprisingbinary, "'%s' has packagename '%s' not listed in the .changes file!\n", debfilename, packagenametocheck)) { diff --git a/configparser.h b/configparser.h index 7442b977..e583b49f 100644 --- a/configparser.h +++ b/configparser.h @@ -226,6 +226,11 @@ static retvalue configparser_ ## sname ## _set_ ## name(UNUSED(void *dummy), con struct sname *item = data; \ return config_gettruth(iter, name, &item->field); \ } +#define CFnumberSETPROC(sname, minval, maxval, field) \ +static retvalue configparser_ ## sname ## _set_ ## field(UNUSED(void *dummy), UNUSED(const char *name), void *data, struct configiterator *iter) { \ + struct sname *item = data; \ + return config_getnumber(iter, name, &item->field, minval, maxval); \ +} #define CFallSETPROC(sname, field) \ static retvalue configparser_ ## sname ## _set_ ## field(UNUSED(void *dummy), UNUSED(const char *name), void *data, struct configiterator *iter) { \ struct sname *item = data; \ diff --git a/contents.c b/contents.c index 495fb234..22ef046f 100644 --- a/contents.c +++ b/contents.c @@ -43,6 +43,7 @@ retvalue contentsoptions_parse(struct distribution *distribution, struct configi cf_uncompressed, cf_gz, cf_bz2, cf_xz, cf_percomponent, cf_allcomponents, cf_compatsymlink, cf_nocompatsymlink, + cf_ddebs, cf_COUNT }; bool flags[cf_COUNT]; @@ -60,6 +61,7 @@ retvalue contentsoptions_parse(struct distribution *distribution, struct configi {".bz2", cf_bz2}, {".gz", cf_gz}, {".", cf_uncompressed}, + {"ddebs", cf_ddebs}, {NULL, -1} }; retvalue r; @@ -130,6 +132,7 @@ retvalue contentsoptions_parse(struct distribution *distribution, struct configi distribution->contents.compressions |= IC_FLAG(ic_xz); #endif distribution->contents.flags.udebs = flags[cf_udebs]; + distribution->contents.flags.ddebs = flags[cf_ddebs]; distribution->contents.flags.nodebs = flags[cf_nodebs]; if (flags[cf_allcomponents]) distribution->contents.flags.allcomponents = true; @@ -173,20 +176,36 @@ static retvalue gentargetcontents(struct target *target, struct release *release struct filetorelease *file; struct filelist_list *contents; struct package_cursor iterator; + const char *suffix; + const char *symlink_prefix; if (onlyneeded && target->saved_wasmodified) onlyneeded = false; + switch (target->packagetype) { + case pt_ddeb: + symlink_prefix = "d"; + suffix = "-ddeb"; + break; + case pt_udeb: + symlink_prefix = "s"; + suffix = "-udeb"; + break; + default: + symlink_prefix = ""; + suffix = ""; + } + contentsfilename = mprintf("%s/Contents%s-%s", atoms_components[target->component], - (target->packagetype == pt_udeb)?"-udeb":"", + suffix, atoms_architectures[target->architecture]); if (FAILEDTOALLOC(contentsfilename)) return RET_ERROR_OOM; if (symlink) { char *symlinkas = mprintf("%sContents-%s", - (target->packagetype == pt_udeb)?"s":"", + symlink_prefix, atoms_architectures[target->architecture]); if (FAILEDTOALLOC(symlinkas)) { free(contentsfilename); @@ -215,7 +234,7 @@ static retvalue gentargetcontents(struct target *target, struct release *release release_abortfile(file); return r; } - result = package_openiterator(target, READONLY, &iterator); + result = package_openiterator(target, READONLY, true, &iterator); if (RET_IS_OK(result)) { while (package_next(&iterator)) { r = addpackagetocontents(&iterator.current, contents); @@ -244,17 +263,30 @@ static retvalue genarchcontents(struct distribution *distribution, architecture_ const struct atomlist *components; struct target *target; bool combinedonlyifneeded; + const char *prefix; + const char *symlink_prefix; - if (type == pt_udeb) { + if (type == pt_ddeb) { + if (distribution->contents_components_set) + components = &distribution->contents_dcomponents; + else + components = &distribution->ddebcomponents; + prefix = "d"; + symlink_prefix = "d"; + } else if (type == pt_udeb) { if (distribution->contents_components_set) components = &distribution->contents_ucomponents; else components = &distribution->udebcomponents; + prefix = "u"; + symlink_prefix = "s"; } else { if (distribution->contents_components_set) components = &distribution->contents_components; else components = &distribution->components; + prefix = ""; + symlink_prefix = ""; } if (components->count == 0) @@ -286,7 +318,7 @@ static retvalue genarchcontents(struct distribution *distribution, architecture_ if (!distribution->contents.flags.allcomponents) { if (!distribution->contents.flags.compatsymlink) { char *symlinkas = mprintf("%sContents-%s", - (type == pt_udeb)?"s":"", + symlink_prefix, atoms_architectures[architecture]); if (FAILEDTOALLOC(symlinkas)) return RET_ERROR_OOM; @@ -298,7 +330,7 @@ static retvalue genarchcontents(struct distribution *distribution, architecture_ } contentsfilename = mprintf("%sContents-%s", - (type == pt_udeb)?"u":"", + prefix, atoms_architectures[architecture]); if (FAILEDTOALLOC(contentsfilename)) return RET_ERROR_OOM; @@ -365,6 +397,12 @@ retvalue contents_generate(struct distribution *distribution, struct release *re release, onlyneeded); RET_UPDATE(result, r); } + if (distribution->contents.flags.ddebs) { + r = genarchcontents(distribution, + architecture, pt_ddeb, + release, onlyneeded); + RET_UPDATE(result, r); + } } return result; } diff --git a/contents.h b/contents.h index 921f6a00..b8a214d2 100644 --- a/contents.h +++ b/contents.h @@ -19,6 +19,7 @@ struct contentsoptions { bool percomponent; bool allcomponents; bool compatsymlink; + bool ddebs; } flags; compressionset compressions; }; diff --git a/copypackages.c b/copypackages.c index 4598fd38..428ce135 100644 --- a/copypackages.c +++ b/copypackages.c @@ -42,6 +42,7 @@ struct target_package_list { struct target_package_list *next; struct target *target; + struct target *fromtarget; struct selectedpackage { /*@null@*/struct selectedpackage *next; char *name; @@ -59,31 +60,44 @@ struct package_list { /*@null@*/struct target_package_list *targets; }; -static retvalue list_newpackage(struct package_list *list, struct target *target, const char *sourcename, const char *sourceversion, const char *packagename, const char *packageversion, /*@out@*/struct selectedpackage **package_p) { +// cascade_strcmp compares the two strings s1 and s2. If the strings are equal, the strings +// t1 and t2 are compared. +static int cascade_strcmp(const char *s1, const char *s2, const char *t1, const char *t2) { + int result; + + result = strcmp(s1, s2); + if (result == 0) { + result = strcmp(t1, t2); + } + return result; +} + +static retvalue list_newpackage(struct package_list *list, struct target *desttarget, struct target *fromtarget, const char *sourcename, const char *sourceversion, const char *packagename, const char *packageversion, /*@out@*/struct selectedpackage **package_p) { struct target_package_list *t, **t_p; struct selectedpackage *package, **p_p; int c; t_p = &list->targets; - while (*t_p != NULL && (*t_p)->target != target) + while (*t_p != NULL && (*t_p)->target != desttarget && (*t_p)->fromtarget != fromtarget) t_p = &(*t_p)->next; if (*t_p == NULL) { t = zNEW(struct target_package_list); if (FAILEDTOALLOC(t)) return RET_ERROR_OOM; - t->target = target; + t->target = desttarget; + t->fromtarget = fromtarget; t->next = *t_p; *t_p = t; } else t = *t_p; p_p = &t->packages; - while (*p_p != NULL && (c = strcmp(packagename, (*p_p)->name)) < 0) + while (*p_p != NULL && (c = cascade_strcmp(packagename, (*p_p)->name, packageversion, (*p_p)->version)) < 0) p_p = &(*p_p)->next; if (*p_p != NULL && c == 0) { // TODO: improve this message..., or some context elsewhere - fprintf(stderr, "Multiple occurrences of package '%s'!\n", - packagename); + fprintf(stderr, "Multiple occurrences of package '%s' with version '%s'!\n", + packagename, packageversion); return RET_ERROR_EXIST; } package = zNEW(struct selectedpackage); @@ -154,12 +168,12 @@ static void list_cancelpackage(struct package_list *list, /*@only@*/struct selec assert (package == NULL); } -static retvalue list_prepareadd(struct package_list *list, struct target *target, struct package *package) { +static retvalue list_prepareadd(struct package_list *list, struct target *desttarget, struct target *fromtarget, struct package *package) { struct selectedpackage *new SETBUTNOTUSED(= NULL); retvalue r; int i; - assert (target->packagetype == package->target->packagetype); + assert (desttarget->packagetype == package->target->packagetype); r = package_getversion(package); assert (r != RET_NOTHING); @@ -173,7 +187,7 @@ static retvalue list_prepareadd(struct package_list *list, struct target *target assert (r != RET_NOTHING); if (RET_WAS_ERROR(r)) return r; - r = list_newpackage(list, target, + r = list_newpackage(list, desttarget, fromtarget, package->source, package->sourceversion, package->name, package->version, &new); assert (r != RET_NOTHING); @@ -182,7 +196,7 @@ static retvalue list_prepareadd(struct package_list *list, struct target *target assert (new != NULL); new->architecture = package->architecture; - r = target->getinstalldata(target, package, + r = desttarget->getinstalldata(desttarget, package, &new->control, &new->filekeys, &new->origfiles); assert (r != RET_NOTHING); if (RET_WAS_ERROR(r)) { @@ -244,7 +258,7 @@ static retvalue list_prepareadd(struct package_list *list, struct target *target return RET_OK; } -static retvalue package_add(struct distribution *into, /*@null@*/trackingdb tracks, struct target *target, const struct selectedpackage *package, /*@null@*/ const char *suitefrom) { +static retvalue package_add(struct distribution *into, /*@null@*/trackingdb tracks, struct target *target, const struct selectedpackage *package, /*@null@*/ struct distribution *from, /*@null@*/trackingdb fromtracks, struct target *fromtarget, bool remove_source) { struct trackingdata trackingdata; retvalue r; @@ -274,27 +288,58 @@ static retvalue package_add(struct distribution *into, /*@null@*/trackingdb trac (tracks != NULL)? &trackingdata:NULL, package->architecture, - NULL, suitefrom); + NULL, from != NULL ? from->codename : NULL); RET_UPDATE(into->status, r); + if (tracks != NULL) { retvalue r2; r2 = trackingdata_finish(tracks, &trackingdata); RET_ENDUPDATE(r, r2); } + + if (!RET_WAS_ERROR(r) && remove_source) { + if (fromtracks != NULL) { + r = trackingdata_summon(fromtracks, package->sourcename, + package->version, &trackingdata); + if (RET_WAS_ERROR(r)) + return r; + } + r = target_removepackage(fromtarget, + from->logger, + package->name, package->version, + (tracks != NULL) ? &trackingdata : NULL); + RET_UPDATE(from->status, r); + if (fromtracks != NULL) { + retvalue r2; + + r2 = trackingdata_finish(fromtracks, &trackingdata); + RET_ENDUPDATE(r, r2); + } + } return r; } -static retvalue packagelist_add(struct distribution *into, const struct package_list *list, /*@null@*/const char *suitefrom) { +static retvalue packagelist_add(struct distribution *into, const struct package_list *list, /*@null@*/struct distribution *from, bool remove_source) { retvalue result, r; struct target_package_list *tpl; struct selectedpackage *package; - trackingdb tracks; + trackingdb tracks, fromtracks = NULL; + + if (verbose >= 15) + fprintf(stderr, "trace: packagelist_add(into.codename=%s, from.codename=%s) called.\n", + into->codename, from != NULL ? from->codename : NULL); r = distribution_prepareforwriting(into); if (RET_WAS_ERROR(r)) return r; + if (remove_source) { + r = distribution_prepareforwriting(from); + if (RET_WAS_ERROR(r)) + return r; + } + if (into->tracking != dt_NONE) { r = tracking_initialize(&tracks, into, false); if (RET_WAS_ERROR(r)) @@ -302,25 +347,53 @@ static retvalue packagelist_add(struct distribution *into, const struct package_ } else tracks = NULL; + if (from->tracking != dt_NONE) { + r = tracking_initialize(&fromtracks, from, false); + if (RET_WAS_ERROR(r)) + return r; + } + result = RET_NOTHING; for (tpl = list->targets; tpl != NULL ; tpl = tpl->next) { struct target *target = tpl->target; + struct target *fromtarget = tpl->fromtarget; + + if (verbose >= 15) + fprintf(stderr, "trace: Processing add/move from '%s' to '%s'...\n", + fromtarget != NULL ? fromtarget->identifier : NULL, target->identifier); r = target_initpackagesdb(target, READWRITE); RET_ENDUPDATE(result, r); if (RET_WAS_ERROR(r)) break; + + if (remove_source) { + r = target_initpackagesdb(fromtarget, READWRITE); + RET_ENDUPDATE(result, r); + if (RET_WAS_ERROR(r)) { + (void)target_closepackagesdb(target); + break; + } + } + for (package = tpl->packages; package != NULL ; package = package->next) { r = package_add(into, tracks, target, - package, suitefrom); + package, from, fromtracks, fromtarget, remove_source); RET_UPDATE(result, r); } + if (remove_source) { + r = target_closepackagesdb(fromtarget); + RET_UPDATE(into->status, r); + RET_ENDUPDATE(result, r); + } r = target_closepackagesdb(target); RET_UPDATE(into->status, r); RET_ENDUPDATE(result, r); } - r = tracking_done(tracks); + r = tracking_done(fromtracks, from); + RET_ENDUPDATE(result, r); + r = tracking_done(tracks, into); RET_ENDUPDATE(result, r); return result; } @@ -363,41 +436,48 @@ struct namelist { }; static retvalue by_name(struct package_list *list, struct target *desttarget, struct target *fromtarget, void *data) { - struct namelist *d = data; + struct nameandversion *nameandversion = data; + struct nameandversion *prev; retvalue result, r; - int i, j; result = RET_NOTHING; - for (i = 0 ; i < d->argc ; i++) { + for (struct nameandversion *d = nameandversion; d->name != NULL ; d++) { struct package package; - const char *name = d->argv[i]; - for (j = 0 ; j < i ; j++) - if (strcmp(d->argv[i], d->argv[j]) == 0) + for (prev = nameandversion ; prev < d ; prev++) { + if (strcmp(prev->name, d->name) == 0 && strcmp2(prev->version, d->version) == 0) break; - if (j < i) { - if (verbose >= 0 && ! d->warnedabout[j]) - fprintf(stderr, + } + if (prev < d) { + if (verbose >= 0 && ! prev->warnedabout) { + if (d->version == NULL) { + fprintf(stderr, "Hint: '%s' was listed multiple times, ignoring all but first!\n", - d->argv[i]); - d->warnedabout[j] = true; + d->name); + } else { + fprintf(stderr, +"Hint: '%s=%s' was listed multiple times, ignoring all but first!\n", + d->name, d->version); + } + } + prev->warnedabout = true; /* do not complain second is missing if we ignore it: */ - d->found[i] = true; + d->found = true; continue; } - r = package_get(fromtarget, name, NULL, &package); + r = package_get(fromtarget, d->name, d->version, &package); if (r == RET_NOTHING) continue; RET_ENDUPDATE(result, r); if (RET_WAS_ERROR(r)) break; - r = list_prepareadd(list, desttarget, &package); + r = list_prepareadd(list, desttarget, fromtarget, &package); package_done(&package); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) break; - d->found[i] = true; + d->found = true; } return result; } @@ -418,30 +498,23 @@ static void packagelist_done(struct package_list *list) { } } -retvalue copy_by_name(struct distribution *into, struct distribution *from, int argc, const char **argv, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes) { +retvalue copy_by_name(struct distribution *into, struct distribution *from, struct nameandversion *nameandversion, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes, bool remove_source) { struct package_list list; - struct namelist names = { - argc, argv, nzNEW(argc, bool), nzNEW(argc, bool) - }; retvalue r; - if (FAILEDTOALLOC(names.warnedabout) || FAILEDTOALLOC(names.found)) { - free(names.found); - free(names.warnedabout); - return RET_ERROR_OOM; + for (struct nameandversion *d = nameandversion; d->name != NULL; d++) { + d->found = false; + d->warnedabout = false; } memset(&list, 0, sizeof(list)); r = copy_by_func(&list, into, from, components, - architectures, packagetypes, by_name, &names); - free(names.warnedabout); + architectures, packagetypes, by_name, nameandversion); if (verbose >= 0 && !RET_WAS_ERROR(r)) { - int i; bool first = true; - assert(names.found != NULL); - for (i = 0 ; i < argc ; i++) { - if (names.found[i]) + for (struct nameandversion *d = nameandversion; d->name != NULL; d++) { + if (d->found) continue; if (first) (void)fputs( @@ -449,17 +522,20 @@ retvalue copy_by_name(struct distribution *into, struct distribution *from, int else (void)fputs(", ", stderr); first = false; - (void)fputs(argv[i], stderr); + (void)fputs(d->name, stderr); + if (d->version != NULL) { + (void)fputs("=", stderr); + (void)fputs(d->version, stderr); + } } if (!first) { (void)fputc('.', stderr); (void)fputc('\n', stderr); } } - free(names.found); if (!RET_IS_OK(r)) return r; - r = packagelist_add(into, &list, from->codename); + r = packagelist_add(into, &list, from, remove_source); packagelist_done(&list); return r; } @@ -471,7 +547,7 @@ static retvalue by_source(struct package_list *list, struct target *desttarget, assert (d->argc > 0); - r = package_openiterator(fromtarget, READONLY, &iterator); + r = package_openiterator(fromtarget, READONLY, true, &iterator); assert (r != RET_NOTHING); if (!RET_IS_OK(r)) return r; @@ -513,7 +589,7 @@ static retvalue by_source(struct package_list *list, struct target *desttarget, continue; } } - r = list_prepareadd(list, desttarget, &iterator.current); + r = list_prepareadd(list, desttarget, fromtarget, &iterator.current); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) break; @@ -525,7 +601,7 @@ static retvalue by_source(struct package_list *list, struct target *desttarget, return result; } -retvalue copy_by_source(struct distribution *into, struct distribution *from, int argc, const char **argv, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes) { +retvalue copy_by_source(struct distribution *into, struct distribution *from, int argc, const char **argv, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes, bool remove_source) { struct package_list list; struct namelist names = { argc, argv, NULL, nzNEW(argc, bool) }; retvalue r; @@ -599,7 +675,7 @@ retvalue copy_by_source(struct distribution *into, struct distribution *from, in free(names.found); if (!RET_IS_OK(r)) return r; - r = packagelist_add(into, &list, from->codename); + r = packagelist_add(into, &list, from, remove_source); packagelist_done(&list); return r; } @@ -609,7 +685,7 @@ static retvalue by_formula(struct package_list *list, struct target *desttarget, struct package_cursor iterator; retvalue result, r; - r = package_openiterator(fromtarget, READONLY, &iterator); + r = package_openiterator(fromtarget, READONLY, true, &iterator); assert (r != RET_NOTHING); if (!RET_IS_OK(r)) return r; @@ -623,7 +699,7 @@ static retvalue by_formula(struct package_list *list, struct target *desttarget, result = r; break; } - r = list_prepareadd(list, desttarget, &iterator.current); + r = list_prepareadd(list, desttarget, fromtarget, &iterator.current); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) break; @@ -638,7 +714,7 @@ static retvalue by_glob(struct package_list *list, struct target *desttarget, st struct package_cursor iterator; retvalue result, r; - r = package_openiterator(fromtarget, READONLY, &iterator); + r = package_openiterator(fromtarget, READONLY, true, &iterator); assert (r != RET_NOTHING); if (!RET_IS_OK(r)) return r; @@ -646,7 +722,7 @@ static retvalue by_glob(struct package_list *list, struct target *desttarget, st while (package_next(&iterator)) { if (!globmatch(iterator.current.name, glob)) continue; - r = list_prepareadd(list, desttarget, &iterator.current); + r = list_prepareadd(list, desttarget, fromtarget, &iterator.current); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) break; @@ -656,7 +732,7 @@ static retvalue by_glob(struct package_list *list, struct target *desttarget, st return result; } -retvalue copy_by_glob(struct distribution *into, struct distribution *from, const char *glob, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes) { +retvalue copy_by_glob(struct distribution *into, struct distribution *from, const char *glob, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes, bool remove_source) { struct package_list list; retvalue r; @@ -666,12 +742,12 @@ retvalue copy_by_glob(struct distribution *into, struct distribution *from, cons packagetypes, by_glob, (void*)glob); if (!RET_IS_OK(r)) return r; - r = packagelist_add(into, &list, from->codename); + r = packagelist_add(into, &list, from, remove_source); packagelist_done(&list); return r; } -retvalue copy_by_formula(struct distribution *into, struct distribution *from, const char *filter, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes) { +retvalue copy_by_formula(struct distribution *into, struct distribution *from, const char *filter, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes, bool remove_source) { struct package_list list; term *condition; retvalue r; @@ -687,7 +763,7 @@ retvalue copy_by_formula(struct distribution *into, struct distribution *from, c term_free(condition); if (!RET_IS_OK(r)) return r; - r = packagelist_add(into, &list, from->codename); + r = packagelist_add(into, &list, from, remove_source); packagelist_done(&list); return r; } @@ -778,7 +854,14 @@ retvalue copy_from_file(struct distribution *into, component_t component, archit into->codename, atoms_architectures[architecture]); } - if (packagetype != pt_udeb) { + if (packagetype == pt_ddeb) { + if (!atomlist_in(&into->ddebcomponents, component)) { + fprintf(stderr, +"Distribution '%s' does not contain ddeb component '%s!'\n", + into->codename, + atoms_components[component]); + } + } else if (packagetype != pt_udeb) { if (!atomlist_in(&into->components, component)) { fprintf(stderr, "Distribution '%s' does not contain component '%s!'\n", @@ -808,7 +891,7 @@ retvalue copy_from_file(struct distribution *into, component_t component, archit while (indexfile_getnext(i, &package, target, false)) { r = choose_by_name(&package, &d); if (RET_IS_OK(r)) - r = list_prepareadd(&list, target, &package); + r = list_prepareadd(&list, target, NULL, &package); package_done(&package); RET_UPDATE(result, r); if (RET_WAS_ERROR(result)) @@ -817,7 +900,7 @@ retvalue copy_from_file(struct distribution *into, component_t component, archit r = indexfile_close(i); RET_ENDUPDATE(result, r); if (RET_IS_OK(result)) - result = packagelist_add(into, &list, NULL); + result = packagelist_add(into, &list, NULL, false); packagelist_done(&list); return result; } @@ -830,6 +913,7 @@ static retvalue restore_from_snapshot(struct distribution *into, const struct at struct target *target; char *basedir; enum compression compression; + struct distribution pseudo_from; // just stores the codename basedir = calc_snapshotbasedir(into->codename, snapshotname); if (FAILEDTOALLOC(basedir)) @@ -897,7 +981,7 @@ static retvalue restore_from_snapshot(struct distribution *into, const struct at result = action(&package, d); if (RET_IS_OK(result)) result = list_prepareadd(&list, - target, &package); + target, NULL, &package); package_done(&package); if (RET_WAS_ERROR(result)) break; @@ -911,7 +995,9 @@ static retvalue restore_from_snapshot(struct distribution *into, const struct at free(basedir); if (RET_WAS_ERROR(result)) return result; - r = packagelist_add(into, &list, snapshotname); + memset(&pseudo_from, 0, sizeof(struct distribution)); + pseudo_from.codename = (char*)snapshotname; + r = packagelist_add(into, &list, &pseudo_from, false); packagelist_done(&list); return r; } diff --git a/copypackages.h b/copypackages.h index 3515bcd4..0f4771ff 100644 --- a/copypackages.h +++ b/copypackages.h @@ -5,10 +5,17 @@ #include "strlist.h" #endif -retvalue copy_by_name(struct distribution * /*into*/, struct distribution * /*from*/, int, const char **, const struct atomlist *, const struct atomlist *, const struct atomlist *); -retvalue copy_by_source(struct distribution * /*into*/, struct distribution * /*from*/, int, const char **, const struct atomlist *, const struct atomlist *, const struct atomlist *); -retvalue copy_by_formula(struct distribution * /*into*/, struct distribution * /*from*/, const char *formula, const struct atomlist *, const struct atomlist *, const struct atomlist *); -retvalue copy_by_glob(struct distribution * /*into*/, struct distribution * /*from*/, const char * /*glob*/, const struct atomlist *, const struct atomlist *, const struct atomlist *); +struct nameandversion { + const char *name; + const char /*@null@*/ *version; + bool warnedabout; + bool found; +}; + +retvalue copy_by_name(struct distribution * /*into*/, struct distribution * /*from*/, struct nameandversion *, const struct atomlist *, const struct atomlist *, const struct atomlist *, bool); +retvalue copy_by_source(struct distribution * /*into*/, struct distribution * /*from*/, int, const char **, const struct atomlist *, const struct atomlist *, const struct atomlist *, bool); +retvalue copy_by_formula(struct distribution * /*into*/, struct distribution * /*from*/, const char *formula, const struct atomlist *, const struct atomlist *, const struct atomlist *, bool); +retvalue copy_by_glob(struct distribution * /*into*/, struct distribution * /*from*/, const char * /*glob*/, const struct atomlist *, const struct atomlist *, const struct atomlist *, bool); retvalue copy_from_file(struct distribution * /*into*/, component_t, architecture_t, packagetype_t, const char * /*filename*/ , int, const char **); diff --git a/database.c b/database.c index 7f852e76..7e8528c6 100644 --- a/database.c +++ b/database.c @@ -43,6 +43,7 @@ #include "dpkgversions.h" #include "distribution.h" #include "database_p.h" +#include "chunks.h" #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -54,10 +55,9 @@ static bool rdb_initialized, rdb_used, rdb_locked, rdb_verbose; static int rdb_dircreationdepth; static bool rdb_nopackages, rdb_readonly; -static bool rdb_packagesdatabaseopen; -static bool rdb_trackingdatabaseopen; static /*@null@*/ char *rdb_version, *rdb_lastsupportedversion, *rdb_dbversion, *rdb_lastsupporteddbversion; +static DB_ENV *rdb_env = NULL; struct table *rdb_checksums, *rdb_contents; struct table *rdb_references; @@ -65,6 +65,14 @@ static struct { bool createnewtables; } rdb_capabilities; +struct opened_tables { + struct opened_tables *next; + const char *name; + const char *subname; +}; + +struct opened_tables *opened_tables = NULL; + static void database_free(void) { if (!rdb_initialized) return; @@ -83,6 +91,36 @@ static inline char *dbfilename(const char *filename) { return calc_dirconcat(global.dbdir, filename); } +static retvalue database_openenv(void) { + int dbret; + + dbret = db_env_create(&rdb_env, 0); + if (dbret != 0) { + fprintf(stderr, "db_env_create: %s\n", db_strerror(dbret)); + return RET_ERROR; + } + + // DB_INIT_LOCK is needed to open multiple databases in one file (e.g. for move command) + dbret = rdb_env->open(rdb_env, global.dbdir, + DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_INIT_LOCK, 0664); + if (dbret != 0) { + rdb_env->err(rdb_env, dbret, "environment open: %s", global.dbdir); + return RET_ERROR; + } + + return RET_OK; +} + +static void database_closeenv(void) { + int dbret; + + dbret = rdb_env->close(rdb_env, 0); + if (dbret != 0) { + fprintf(stderr, "Error: DB_ENV->close: %s\n", db_strerror(dbret)); + } + rdb_env = NULL; +} + /**********************/ /* lock file handling */ /**********************/ @@ -148,6 +186,13 @@ static retvalue database_lock(size_t waitforlock) { } free(lockfile); rdb_locked = true; + + r = database_openenv(); + if (RET_WAS_ERROR(r)) { + (void)unlink(lockfile); + free(lockfile); + return r; + } return RET_OK; } @@ -156,6 +201,7 @@ static void releaselock(void) { assert (rdb_locked); + database_closeenv(); lockfile = dbfilename("lockfile"); if (lockfile == NULL) return; @@ -211,16 +257,17 @@ static retvalue database_hasdatabasefile(const char *filename, /*@out@*/bool *ex enum database_type { dbt_QUERY, - dbt_BTREE, dbt_BTREEDUP, dbt_BTREEPAIRS, + dbt_BTREE, dbt_BTREEDUP, dbt_BTREEPAIRS, dbt_BTREEVERSIONS, dbt_HASH, dbt_COUNT /* must be last */ }; static const uint32_t types[dbt_COUNT] = { DB_UNKNOWN, - DB_BTREE, DB_BTREE, DB_BTREE, + DB_BTREE, DB_BTREE, DB_BTREE, DB_BTREE, DB_HASH }; +static int debianversioncompare(UNUSED(DB *db), const DBT *a, const DBT *b); #if DB_VERSION_MAJOR >= 6 static int paireddatacompare(UNUSED(DB *db), const DBT *a, const DBT *b, size_t *locp); #else @@ -228,26 +275,26 @@ static int paireddatacompare(UNUSED(DB *db), const DBT *a, const DBT *b); #endif static retvalue database_opentable(const char *filename, /*@null@*/const char *subtable, enum database_type type, uint32_t flags, /*@out@*/DB **result) { - char *fullfilename; DB *table; int dbret; - fullfilename = dbfilename(filename); - if (FAILEDTOALLOC(fullfilename)) - return RET_ERROR_OOM; - - dbret = db_create(&table, NULL, 0); + dbret = db_create(&table, rdb_env, 0); if (dbret != 0) { fprintf(stderr, "db_create: %s\n", db_strerror(dbret)); - free(fullfilename); return RET_DBERR(dbret); } - if (type == dbt_BTREEDUP || type == dbt_BTREEPAIRS) { + if (type == dbt_BTREEPAIRS || type == dbt_BTREEVERSIONS) { dbret = table->set_flags(table, DB_DUPSORT); if (dbret != 0) { table->err(table, dbret, "db_set_flags(DB_DUPSORT):"); (void)table->close(table, 0); - free(fullfilename); + return RET_DBERR(dbret); + } + } else if (type == dbt_BTREEDUP) { + dbret = table->set_flags(table, DB_DUP); + if (dbret != 0) { + table->err(table, dbret, "db_set_flags(DB_DUP):"); + (void)table->close(table, 0); return RET_DBERR(dbret); } } @@ -256,7 +303,14 @@ static retvalue database_opentable(const char *filename, /*@null@*/const char *s if (dbret != 0) { table->err(table, dbret, "db_set_dup_compare:"); (void)table->close(table, 0); - free(fullfilename); + return RET_DBERR(dbret); + } + } + if (type == dbt_BTREEVERSIONS) { + dbret = table->set_dup_compare(table, debianversioncompare); + if (dbret != 0) { + table->err(table, dbret, "db_set_dup_compare:"); + (void)table->close(table, 0); return RET_DBERR(dbret); } } @@ -277,24 +331,21 @@ static retvalue database_opentable(const char *filename, /*@null@*/const char *s #endif #endif #endif - dbret = DB_OPEN(table, fullfilename, subtable, types[type], flags); + dbret = DB_OPEN(table, filename, subtable, types[type], flags); if (dbret == ENOENT && !ISSET(flags, DB_CREATE)) { (void)table->close(table, 0); - free(fullfilename); return RET_NOTHING; } if (dbret != 0) { if (subtable != NULL) table->err(table, dbret, "db_open(%s:%s)[%d]", - fullfilename, subtable, dbret); + filename, subtable, dbret); else table->err(table, dbret, "db_open(%s)[%d]", - fullfilename, dbret); + filename, dbret); (void)table->close(table, 0); - free(fullfilename); return RET_DBERR(dbret); } - free(fullfilename); *result = table; return RET_OK; } @@ -945,33 +996,88 @@ static const char databaseerror[] = "Internal error of the underlying BerkeleyDB There is nothing that cannot be solved by another layer of indirection, except too many levels of indirection. (Source forgotten) */ +struct cursor { + DBC *cursor; + uint32_t flags; + retvalue r; +}; + struct table { char *name, *subname; DB *berkeleydb; - bool *flagreset; + DB *sec_berkeleydb; bool readonly, verbose; + uint32_t flags; }; static void table_printerror(struct table *table, int dbret, const char *action) { - if (table->subname != NULL) - table->berkeleydb->err(table->berkeleydb, dbret, - "%sWithin %s subtable %s at %s", - databaseerror, table->name, table->subname, - action); - else - table->berkeleydb->err(table->berkeleydb, dbret, - "%sWithin %s at %s", - databaseerror, table->name, action); + char *error_msg; + + switch (dbret) { + case DB_MALFORMED_KEY: + error_msg = "DB_MALFORMED_KEY: Primary key does not contain the separator '|'."; + break; + case RET_ERROR_OOM: + error_msg = "RET_ERROR_OOM: Out of memory."; + break; + default: + error_msg = NULL; + break; + } + + if (error_msg == NULL) { + if (table->subname != NULL) + table->berkeleydb->err(table->berkeleydb, dbret, + "%sWithin %s subtable %s at %s", + databaseerror, table->name, table->subname, + action); + else + table->berkeleydb->err(table->berkeleydb, dbret, + "%sWithin %s at %s", + databaseerror, table->name, action); + } else { + if (table->subname != NULL) + table->berkeleydb->errx(table->berkeleydb, + "%sWithin %s subtable %s at %s: %s", + databaseerror, table->name, table->subname, + action, error_msg); + else + table->berkeleydb->errx(table->berkeleydb, + "%sWithin %s at %s: %s", + databaseerror, table->name, action, error_msg); + } +} + +static void print_opened_tables(FILE *stream) { + if (opened_tables == NULL) { + fprintf(stream, "No tables are opened.\n"); + } else { + fprintf(stream, "Opened tables:\n"); + for (struct opened_tables *iter = opened_tables; iter != NULL; iter = iter->next) { + fprintf(stream, " * %s - '%s'\n", iter->name, iter->subname); + } + } } retvalue table_close(struct table *table) { + struct opened_tables *prev = NULL; int dbret; - retvalue result; + retvalue result = RET_OK; + if (verbose >= 15) + fprintf(stderr, "trace: table_close(table.name=%s, table.subname=%s) called.\n", + table == NULL ? NULL : table->name, table == NULL ? NULL : table->subname); if (table == NULL) return RET_NOTHING; - if (table->flagreset != NULL) - *table->flagreset = false; + if (table->sec_berkeleydb != NULL) { + dbret = table->sec_berkeleydb->close(table->sec_berkeleydb, 0); + if (dbret != 0) { + fprintf(stderr, "db_sec_close(%s, %s): %s\n", + table->name, table->subname, + db_strerror(dbret)); + result = RET_DBERR(dbret); + } + } if (table->berkeleydb == NULL) { assert (table->readonly); dbret = 0; @@ -982,17 +1088,34 @@ retvalue table_close(struct table *table) { table->name, table->subname, db_strerror(dbret)); result = RET_DBERR(dbret); - } else - result = RET_OK; + } + + for (struct opened_tables *iter = opened_tables; iter != NULL; iter = iter->next) { + if(strcmp2(iter->name, table->name) == 0 && strcmp2(iter->subname, table->subname) == 0) { + if (prev == NULL) { + opened_tables = iter->next; + } else { + prev->next = iter->next; + } + free(iter); + break; + } + prev = iter; + } + + if (verbose >= 25) + print_opened_tables(stderr); + free(table->name); free(table->subname); free(table); return result; } -retvalue table_getrecord(struct table *table, const char *key, char **data_p, size_t *datalen_p) { +retvalue table_getrecord(struct table *table, bool secondary, const char *key, char **data_p, size_t *datalen_p) { int dbret; DBT Key, Data; + DB *db; assert (table != NULL); if (table->berkeleydb == NULL) { @@ -1004,8 +1127,11 @@ retvalue table_getrecord(struct table *table, const char *key, char **data_p, si CLEARDBT(Data); Data.flags = DB_DBT_MALLOC; - dbret = table->berkeleydb->get(table->berkeleydb, NULL, - &Key, &Data, 0); + if (secondary) + db = table->sec_berkeleydb; + else + db = table->berkeleydb; + dbret = db->get(db, NULL, &Key, &Data, 0); // TODO: find out what error code means out of memory... if (dbret == DB_NOTFOUND) return RET_NOTHING; @@ -1019,11 +1145,11 @@ retvalue table_getrecord(struct table *table, const char *key, char **data_p, si ((const char*)Data.data)[Data.size-1] != '\0') { if (table->subname != NULL) fprintf(stderr, -"Database %s(%s) returned corrupted (not null-terminated) data!", +"Database %s(%s) returned corrupted (not null-terminated) data!\n", table->name, table->subname); else fprintf(stderr, -"Database %s returned corrupted (not null-terminated) data!", +"Database %s returned corrupted (not null-terminated) data!\n", table->name); free(Data.data); return RET_ERROR; @@ -1107,11 +1233,11 @@ retvalue table_gettemprecord(struct table *table, const char *key, const char ** ((const char*)Data.data)[Data.size-1] != '\0') { if (table->subname != NULL) fprintf(stderr, -"Database %s(%s) returned corrupted (not null-terminated) data!", +"Database %s(%s) returned corrupted (not null-terminated) data!\n", table->name, table->subname); else fprintf(stderr, -"Database %s returned corrupted (not null-terminated) data!", +"Database %s returned corrupted (not null-terminated) data!\n", table->name); return RET_ERROR; } @@ -1204,7 +1330,7 @@ retvalue table_addrecord(struct table *table, const char *key, const char *data, SETDBT(Key, key); SETDBTl(Data, data, datalen + 1); dbret = table->berkeleydb->put(table->berkeleydb, NULL, - &Key, &Data, DB_NODUPDATA); + &Key, &Data, ISSET(table->flags, DB_DUPSORT) ? DB_NODUPDATA : 0); if (dbret != 0 && !(ignoredups && dbret == DB_KEYEXIST)) { table_printerror(table, dbret, "put"); return RET_DBERR(dbret); @@ -1251,6 +1377,9 @@ retvalue table_adduniqsizedrecord(struct table *table, const char *key, const ch return RET_OK; } retvalue table_adduniqrecord(struct table *table, const char *key, const char *data) { + if (verbose >= 15) + fprintf(stderr, "trace: table_adduniqrecord(table={name: %s, subname: %s}, key=%s) called.\n", + table->name, table->subname, key); return table_adduniqsizedrecord(table, key, data, strlen(data)+1, false, false); } @@ -1287,26 +1416,34 @@ retvalue table_deleterecord(struct table *table, const char *key, bool ignoremis retvalue table_replacerecord(struct table *table, const char *key, const char *data) { retvalue r; + if (verbose >= 15) + fprintf(stderr, "trace: table_replacerecord(table={name: %s, subname: %s}, key=%s) called.\n", + table->name, table->subname, key); r = table_deleterecord(table, key, false); if (r != RET_ERROR_MISSING && RET_WAS_ERROR(r)) return r; return table_adduniqrecord(table, key, data); } -struct cursor { - DBC *cursor; - uint32_t flags; - retvalue r; -}; - -retvalue table_newglobalcursor(struct table *table, struct cursor **cursor_p) { +static retvalue newcursor(struct table *table, uint32_t flags, struct cursor **cursor_p) { + DB *berkeleydb; struct cursor *cursor; int dbret; - if (table->berkeleydb == NULL) { + if (verbose >= 15) + fprintf(stderr, "trace: newcursor(table={name: %s, subname: %s}) called.\n", + table->name, table->subname); + + if (table->sec_berkeleydb == NULL) { + berkeleydb = table->berkeleydb; + } else { + berkeleydb = table->sec_berkeleydb; + } + + if (berkeleydb == NULL) { assert (table->readonly); *cursor_p = NULL; - return RET_OK; + return RET_NOTHING; } cursor = zNEW(struct cursor); @@ -1314,9 +1451,9 @@ retvalue table_newglobalcursor(struct table *table, struct cursor **cursor_p) { return RET_ERROR_OOM; cursor->cursor = NULL; - cursor->flags = DB_NEXT; + cursor->flags = flags; cursor->r = RET_OK; - dbret = table->berkeleydb->cursor(table->berkeleydb, NULL, + dbret = berkeleydb->cursor(berkeleydb, NULL, &cursor->cursor, 0); if (dbret != 0) { table_printerror(table, dbret, "cursor"); @@ -1327,6 +1464,39 @@ retvalue table_newglobalcursor(struct table *table, struct cursor **cursor_p) { return RET_OK; } +retvalue table_newglobalcursor(struct table *table, bool duplicate, struct cursor **cursor_p) { + retvalue r; + + r = newcursor(table, duplicate ? DB_NEXT : DB_NEXT_NODUP, cursor_p); + if (r == RET_NOTHING) { + // table_newglobalcursor returned RET_OK when table->berkeleydb == NULL. Is that return value wanted? + r = RET_OK; + } + return r; +} + +static inline retvalue parse_data(struct table *table, DBT Key, DBT Data, /*@null@*//*@out@*/const char **key_p, /*@out@*/const char **data_p, /*@out@*/size_t *datalen_p) { + if (Key.size <= 0 || Data.size <= 0 || + ((const char*)Key.data)[Key.size-1] != '\0' || + ((const char*)Data.data)[Data.size-1] != '\0') { + if (table->subname != NULL) + fprintf(stderr, +"Database %s(%s) returned corrupted (not null-terminated) data!", + table->name, table->subname); + else + fprintf(stderr, +"Database %s returned corrupted (not null-terminated) data!", + table->name); + return RET_ERROR; + } + if (key_p != NULL) + *key_p = Key.data; + *data_p = Data.data; + if (datalen_p != NULL) + *datalen_p = Data.size - 1; + return RET_OK; +} + static inline retvalue parse_pair(struct table *table, DBT Key, DBT Data, /*@null@*//*@out@*/const char **key_p, /*@out@*/const char **value_p, /*@out@*/const char **data_p, /*@out@*/size_t *datalen_p) { /*@dependant@*/ const char *separator; @@ -1347,11 +1517,11 @@ static inline retvalue parse_pair(struct table *table, DBT Key, DBT Data, /*@nul if (separator == NULL) { if (table->subname != NULL) fprintf(stderr, -"Database %s(%s) returned corrupted data!", +"Database %s(%s) returned corrupted data!\n", table->name, table->subname); else fprintf(stderr, -"Database %s returned corrupted data!", +"Database %s returned corrupted data!\n", table->name); return RET_ERROR; } @@ -1363,32 +1533,72 @@ static inline retvalue parse_pair(struct table *table, DBT Key, DBT Data, /*@nul return RET_OK; } -retvalue table_newduplicatecursor(struct table *table, const char *key, struct cursor **cursor_p, const char **value_p, const char **data_p, size_t *datalen_p) { +retvalue table_newduplicatecursor(struct table *table, const char *key, long long skip, struct cursor **cursor_p, const char **key_p, const char **data_p, size_t *datalen_p) { struct cursor *cursor; int dbret; DBT Key, Data; retvalue r; - if (table->berkeleydb == NULL) { - assert (table->readonly); - *cursor_p = NULL; + r = newcursor(table, DB_NEXT_DUP, &cursor); + if(!RET_IS_OK(r)) { + return r; + } + SETDBT(Key, key); + CLEARDBT(Data); + dbret = cursor->cursor->c_get(cursor->cursor, &Key, &Data, DB_SET); + if (dbret == DB_NOTFOUND || dbret == DB_KEYEMPTY) { + (void)cursor->cursor->c_close(cursor->cursor); + free(cursor); return RET_NOTHING; } - - cursor = zNEW(struct cursor); - if (FAILEDTOALLOC(cursor)) - return RET_ERROR_OOM; - - cursor->cursor = NULL; - cursor->flags = DB_NEXT_DUP; - cursor->r = RET_OK; - dbret = table->berkeleydb->cursor(table->berkeleydb, NULL, - &cursor->cursor, 0); if (dbret != 0) { - table_printerror(table, dbret, "cursor"); + table_printerror(table, dbret, "c_get(DB_SET)"); + (void)cursor->cursor->c_close(cursor->cursor); free(cursor); return RET_DBERR(dbret); } + + while (skip > 0) { + CLEARDBT(Key); + CLEARDBT(Data); + + dbret = cursor->cursor->c_get(cursor->cursor, &Key, &Data, cursor->flags); + if (dbret == DB_NOTFOUND) { + (void)cursor->cursor->c_close(cursor->cursor); + free(cursor); + return RET_NOTHING; + } + if (dbret != 0) { + table_printerror(table, dbret, "c_get(DB_NEXT_DUP)"); + (void)cursor->cursor->c_close(cursor->cursor); + free(cursor); + return RET_DBERR(dbret); + } + + skip--; + } + + r = parse_data(table, Key, Data, key_p, data_p, datalen_p); + if (RET_WAS_ERROR(r)) { + (void)cursor->cursor->c_close(cursor->cursor); + free(cursor); + return r; + } + *cursor_p = cursor; + return RET_OK; +} + +retvalue table_newduplicatepairedcursor(struct table *table, const char *key, struct cursor **cursor_p, const char **value_p, const char **data_p, size_t *datalen_p) { + struct cursor *cursor; + int dbret; + DBT Key, Data; + retvalue r; + + r = newcursor(table, DB_NEXT_DUP, cursor_p); + if(!RET_IS_OK(r)) { + return r; + } + cursor = *cursor_p; SETDBT(Key, key); CLEARDBT(Data); dbret = cursor->cursor->c_get(cursor->cursor, &Key, &Data, DB_SET); @@ -1422,27 +1632,12 @@ retvalue table_newpairedcursor(struct table *table, const char *key, const char retvalue r; size_t valuelen = strlen(value); - if (table->berkeleydb == NULL) { - assert (table->readonly); - *cursor_p = NULL; - return RET_NOTHING; - } - - cursor = zNEW(struct cursor); - if (FAILEDTOALLOC(cursor)) - return RET_ERROR_OOM; - - cursor->cursor = NULL; /* cursor_next is not allowed with this type: */ - cursor->flags = DB_GET_BOTH; - cursor->r = RET_OK; - dbret = table->berkeleydb->cursor(table->berkeleydb, NULL, - &cursor->cursor, 0); - if (dbret != 0) { - table_printerror(table, dbret, "cursor"); - free(cursor); - return RET_DBERR(dbret); + r = newcursor(table, DB_GET_BOTH, cursor_p); + if(!RET_IS_OK(r)) { + return r; } + cursor = *cursor_p; SETDBT(Key, key); SETDBTl(Data, value, valuelen + 1); dbret = cursor->cursor->c_get(cursor->cursor, &Key, &Data, DB_GET_BOTH); @@ -1496,110 +1691,57 @@ retvalue cursor_close(struct table *table, struct cursor *cursor) { return r; } -bool cursor_nexttemp(struct table *table, struct cursor *cursor, const char **key, const char **data) { - DBT Key, Data; +static bool cursor_next(struct table *table, struct cursor *cursor, DBT *Key, DBT *Data) { int dbret; if (cursor == NULL) return false; - CLEARDBT(Key); - CLEARDBT(Data); + CLEARDBT(*Key); + CLEARDBT(*Data); - dbret = cursor->cursor->c_get(cursor->cursor, &Key, &Data, DB_NEXT); + dbret = cursor->cursor->c_get(cursor->cursor, Key, Data, + cursor->flags); if (dbret == DB_NOTFOUND) return false; if (dbret != 0) { - table_printerror(table, dbret, "c_get(DB_NEXT)"); + table_printerror(table, dbret, + (cursor->flags==DB_NEXT) + ? "c_get(DB_NEXT)" + : (cursor->flags==DB_NEXT_DUP) + ? "c_get(DB_NEXT_DUP)" + : "c_get(DB_???NEXT)"); cursor->r = RET_DBERR(dbret); return false; } - if (Key.size <= 0 || Data.size <= 0 || - ((const char*)Key.data)[Key.size-1] != '\0' || - ((const char*)Data.data)[Data.size-1] != '\0') { - if (table->subname != NULL) - fprintf(stderr, -"Database %s(%s) returned corrupted (not null-terminated) data!", - table->name, table->subname); - else - fprintf(stderr, -"Database %s returned corrupted (not null-terminated) data!", - table->name); - cursor->r = RET_ERROR; - return false; - } - *key = Key.data; - *data = Data.data; return true; } bool cursor_nexttempdata(struct table *table, struct cursor *cursor, const char **key, const char **data, size_t *len_p) { DBT Key, Data; - int dbret; - - if (cursor == NULL) - return false; - - CLEARDBT(Key); - CLEARDBT(Data); - - dbret = cursor->cursor->c_get(cursor->cursor, &Key, &Data, DB_NEXT); - if (dbret == DB_NOTFOUND) - return false; + bool success; + retvalue r; - if (dbret != 0) { - table_printerror(table, dbret, "c_get(DB_NEXT)"); - cursor->r = RET_DBERR(dbret); + success = cursor_next(table, cursor, &Key, &Data); + if (!success) return false; - } - if (Key.size <= 0 || Data.size <= 0 || - ((const char*)Key.data)[Key.size-1] != '\0' || - ((const char*)Data.data)[Data.size-1] != '\0') { - if (table->subname != NULL) - fprintf(stderr, -"Database %s(%s) returned corrupted (not null-terminated) data!", - table->name, table->subname); - else - fprintf(stderr, -"Database %s returned corrupted (not null-terminated) data!", - table->name); - cursor->r = RET_ERROR; + r = parse_data(table, Key, Data, key, data, len_p); + if (RET_WAS_ERROR(r)) { + cursor->r = r; return false; } - if (key != NULL) - *key = Key.data; - *data = Data.data; - *len_p = Data.size - 1; return true; } bool cursor_nextpair(struct table *table, struct cursor *cursor, /*@null@*/const char **key_p, const char **value_p, const char **data_p, size_t *datalen_p) { DBT Key, Data; - int dbret; + bool success; retvalue r; - if (cursor == NULL) - return false; - - CLEARDBT(Key); - CLEARDBT(Data); - - dbret = cursor->cursor->c_get(cursor->cursor, &Key, &Data, - cursor->flags); - if (dbret == DB_NOTFOUND) - return false; - - if (dbret != 0) { - table_printerror(table, dbret, - (cursor->flags==DB_NEXT) - ? "c_get(DB_NEXT)" - : (cursor->flags==DB_NEXT_DUP) - ? "c_get(DB_NEXT_DUP)" - : "c_get(DB_???NEXT)"); - cursor->r = RET_DBERR(dbret); + success = cursor_next(table, cursor, &Key, &Data); + if (!success) return false; - } r = parse_pair(table, Key, Data, key_p, value_p, data_p, datalen_p); if (RET_WAS_ERROR(r)) { cursor->r = r; @@ -1705,10 +1847,26 @@ retvalue database_haspackages(const char *identifier) { /**************************************************************************** * Open the different types of tables with their needed flags: * ****************************************************************************/ -static retvalue database_table(const char *filename, const char *subtable, enum database_type type, uint32_t flags, /*@out@*/struct table **table_p) { +static retvalue database_table_secondary(const char *filename, const char *subtable, enum database_type type, uint32_t flags, + const char *secondary_filename, enum database_type secondary_type, /*@out@*/struct table **table_p) { struct table *table; + struct opened_tables *opened_table; retvalue r; + if (verbose >= 15) + fprintf(stderr, "trace: database_table_secondary(filename=%s, subtable=%s, type=%i, flags=%u, secondary_filename=%s, secondary_type=%i) called.\n", + filename, subtable, type, flags, secondary_filename, secondary_type); + + for (struct opened_tables *iter = opened_tables; iter != NULL; iter = iter->next) { + if(strcmp2(iter->name, filename) == 0 && strcmp2(iter->subname, subtable) == 0) { + fprintf(stderr, + "Internal Error: Trying to open table '%s' from file '%s' multiple times.\n" + "This should normally not happen (to avoid triggering bugs in the underlying BerkeleyDB)\n", + subtable, filename); + return RET_ERROR; + } + } + table = zNEW(struct table); if (FAILEDTOALLOC(table)) return RET_ERROR_OOM; @@ -1729,6 +1887,7 @@ static retvalue database_table(const char *filename, const char *subtable, enum table->subname = NULL; table->readonly = ISSET(flags, DB_RDONLY); table->verbose = rdb_verbose; + table->flags = flags; r = database_opentable(filename, subtable, type, flags, &table->berkeleydb); if (RET_WAS_ERROR(r)) { @@ -1750,10 +1909,56 @@ static retvalue database_table(const char *filename, const char *subtable, enum } } + + if (secondary_filename != NULL) { + r = database_opentable(secondary_filename, subtable, secondary_type, flags, + &table->sec_berkeleydb); + if (RET_WAS_ERROR(r)) { + table->berkeleydb->close(table->berkeleydb, 0); + free(table->subname); + free(table->name); + free(table); + return r; + } + if (r == RET_NOTHING) { + if (ISSET(flags, DB_RDONLY)) { + /* sometimes we don't want a return here, when? */ + table->sec_berkeleydb = NULL; + r = RET_OK; + } else { + table->berkeleydb->close(table->berkeleydb, 0); + free(table->subname); + free(table->name); + free(table); + return r; + } + + } + } + + opened_table = zNEW(struct opened_tables); + if (FAILEDTOALLOC(opened_table)) { + free(table->subname); + free(table->name); + free(table); + return RET_ERROR_OOM; + } + opened_table->name = table->name; + opened_table->subname = table->subname; + opened_table->next = opened_tables; + opened_tables = opened_table; + + if (verbose >= 25) + print_opened_tables(stderr); + *table_p = table; return r; } +static retvalue database_table(const char *filename, const char *subtable, enum database_type type, uint32_t flags, /*@out@*/struct table **table_p) { + return database_table_secondary(filename, subtable, type, flags, NULL, 0, table_p); +} + retvalue database_openreferences(void) { retvalue r; @@ -1769,6 +1974,45 @@ retvalue database_openreferences(void) { return RET_OK; } +static int debianversioncompare(UNUSED(DB *db), const DBT *a, const DBT *b) { + const char *a_version; + const char *b_version; + int versioncmp; + // There is no way to indicate an error to the caller + // Thus return -1 in case of an error + retvalue r = -1; + + if (a->size == 0 || ((char*)a->data)[a->size-1] != '\0') { + fprintf(stderr, "Database value '%.*s' empty or not NULL terminated.\n", a->size, (char*)a->data); + return r; + } + if (b->size == 0 || ((char*)b->data)[b->size-1] != '\0') { + fprintf(stderr, "Database value '%.*s' empty or not NULL terminated.\n", b->size, (char*)b->data); + return r; + } + + a_version = strchr(a->data, '|'); + if (a_version == NULL) { + fprintf(stderr, "Database value '%s' malformed. It should be 'package|version'.\n", (char*)a->data); + return r; + } + a_version++; + b_version = strchr(b->data, '|'); + if (b_version == NULL) { + fprintf(stderr, "Database value '%s' malformed. It should be 'package|version'.\n", (char*)b->data); + return r; + } + b_version++; + + r = dpkgversions_cmp(a_version, b_version, &versioncmp); + if (RET_WAS_ERROR(r)) { + fprintf(stderr, "Parse errors processing versions.\n"); + return r; + } + + return -versioncmp; +} + /* only compare the first 0-terminated part of the data */ static int paireddatacompare(UNUSED(DB *db), const DBT *a, const DBT *b #if DB_VERSION_MAJOR >= 6 @@ -1794,24 +2038,159 @@ retvalue database_opentracking(const char *codename, bool readonly, struct table stderr); return RET_ERROR; } - if (rdb_trackingdatabaseopen) { - (void)fputs( -"Internal Error: Trying to open multiple tracking databases at the same time.\nThis should normally not happen (to avoid triggering bugs in the underlying BerkeleyDB)\n", - stderr); - return RET_ERROR; - } r = database_table("tracking.db", codename, dbt_BTREEPAIRS, readonly?DB_RDONLY:DB_CREATE, &table); assert (r != RET_NOTHING); if (RET_WAS_ERROR(r)) return r; - table->flagreset = &rdb_trackingdatabaseopen; - rdb_trackingdatabaseopen = true; *table_p = table; return RET_OK; } +static int get_package_name(DB *secondary, const DBT *pkey, const DBT *pdata, DBT *skey) { + const char *separator; + size_t length; + + separator = memchr(pkey->data, '|', pkey->size); + if (unlikely(separator == NULL)) { + return DB_MALFORMED_KEY; + } + + length = (size_t)separator - (size_t)pkey->data; + skey->flags = DB_DBT_APPMALLOC; + skey->data = strndup(pkey->data, length); + if (FAILEDTOALLOC(skey->data)) { + return RET_ERROR_OOM; + } + skey->size = length + 1; + return 0; +} + +static retvalue database_translate_legacy_packages(void) { + struct cursor *databases_cursor, *cursor; + struct table *legacy_databases, *legacy_table, *packages; + const char *chunk, *packagename; + char *identifier, *key, *legacy_filename, *packages_filename, *packageversion; + retvalue r, result; + int ret, e; + size_t chunk_len; + DBT Key, Data; + + if (verbose >= 15) + fprintf(stderr, "trace: database_translate_legacy_packages() called.\n"); + + if (!isdir(global.dbdir)) { + fprintf(stderr, "Cannot find directory '%s'!\n", global.dbdir); + return RET_ERROR; + } + + packages_filename = dbfilename("packages.db"); + legacy_filename = dbfilename("packages.legacy.db"); + ret = rename(packages_filename, legacy_filename); + if (ret != 0) { + e = errno; + fprintf(stderr, "error %d renaming %s to %s: %s\n", + e, packages_filename, legacy_filename, strerror(e)); + return (e != 0)?e:EINVAL; + } + if (verbose >= 15) + fprintf(stderr, "trace: Moved '%s' to '%s'.\n", packages_filename, legacy_filename); + + r = database_table("packages.legacy.db", NULL, dbt_BTREE, DB_RDONLY, &legacy_databases); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) + return r; + + r = table_newglobalcursor(legacy_databases, true, &databases_cursor); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) { + (void)table_close(legacy_databases); + return r; + } + result = RET_NOTHING; + // Iterate over all databases inside the packages.db file. + while (cursor_next(legacy_databases, databases_cursor, &Key, &Data)) { + identifier = strndup(Key.data, Key.size); + if (FAILEDTOALLOC(identifier)) { + RET_UPDATE(result, RET_ERROR_OOM); + break; + } + if (verbose >= 15) + fprintf(stderr, "Converting table '%s' to new layout...\n", identifier); + + r = database_table("packages.legacy.db", identifier, dbt_BTREE, DB_RDONLY, &legacy_table); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) { + free(identifier); + RET_UPDATE(result, r); + break; + } + + r = table_newglobalcursor(legacy_table, true, &cursor); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) { + (void)table_close(legacy_table); + free(identifier); + RET_UPDATE(result, r); + break; + } + + r = database_openpackages(identifier, false, &packages); + free(identifier); + identifier = NULL; + if (RET_WAS_ERROR(r)) { + (void)cursor_close(legacy_databases, databases_cursor); + (void)table_close(legacy_table); + RET_UPDATE(result, r); + break; + } + + while (cursor_nexttempdata(legacy_table, cursor, &packagename, &chunk, &chunk_len)) { + r = chunk_getvalue(chunk, "Version", &packageversion); + if (!RET_IS_OK(r)) { + RET_UPDATE(result, r); + break; + } + key = package_primarykey(packagename, packageversion); + r = table_addrecord(packages, key, chunk, chunk_len, false); + free(key); + if (RET_WAS_ERROR(r)) { + RET_UPDATE(result, r); + break; + } + } + + r = table_close(packages); + RET_UPDATE(result, r); + r = cursor_close(legacy_table, cursor); + RET_UPDATE(result, r); + r = table_close(legacy_table); + RET_UPDATE(result, r); + + if (RET_WAS_ERROR(result)) { + break; + } + result = RET_OK; + } + r = cursor_close(legacy_databases, databases_cursor); + RET_ENDUPDATE(result, r); + r = table_close(legacy_databases); + RET_ENDUPDATE(result, r); + + if (RET_IS_OK(result)) { + e = deletefile(legacy_filename); + if (e != 0) { + fprintf(stderr, "Could not delete '%s'!\n" +"It can now safely be deleted and it all that is left to be done!\n", + legacy_filename); + return RET_ERRNO(e); + } + } + + return result; +} + retvalue database_openpackages(const char *identifier, bool readonly, struct table **table_p) { struct table *table; retvalue r; @@ -1822,21 +2201,34 @@ retvalue database_openpackages(const char *identifier, bool readonly, struct tab stderr); return RET_ERROR; } - if (rdb_packagesdatabaseopen) { - (void)fputs( -"Internal Error: Trying to open multiple packages databases at the same time.\n" -"This should normally not happen (to avoid triggering bugs in the underlying BerkeleyDB)\n", - stderr); - return RET_ERROR; - } - r = database_table("packages.db", identifier, - dbt_BTREE, readonly?DB_RDONLY:DB_CREATE, &table); + r = database_table_secondary("packages.db", identifier, + dbt_BTREE, readonly?DB_RDONLY:DB_CREATE, + "packagenames.db", dbt_BTREEVERSIONS, &table); assert (r != RET_NOTHING); if (RET_WAS_ERROR(r)) return r; - table->flagreset = &rdb_packagesdatabaseopen; - rdb_packagesdatabaseopen = true; + + if (table->berkeleydb != NULL && table->sec_berkeleydb == NULL) { + r = table_close(table); + if (RET_WAS_ERROR(r)) { + return r; + } + r = database_translate_legacy_packages(); + if (RET_WAS_ERROR(r)) { + return r; + } + return database_openpackages(identifier, readonly, table_p); + } + + if (table->berkeleydb != NULL && table->sec_berkeleydb != NULL) { + r = table->berkeleydb->associate(table->berkeleydb, NULL, + table->sec_berkeleydb, get_package_name, 0); + if (RET_WAS_ERROR(r)) { + return r; + } + } + *table_p = table; return RET_OK; } @@ -1848,7 +2240,12 @@ retvalue database_listpackages(struct strlist *identifiers) { /* drop a database */ retvalue database_droppackages(const char *identifier) { - return database_dropsubtable("packages.db", identifier); + retvalue r; + + r = database_dropsubtable("packages.db", identifier); + if (RET_IS_OK(r)) + r = database_dropsubtable("packagenames.db", identifier); + return r; } retvalue database_openfiles(void) { @@ -1969,7 +2366,7 @@ static retvalue table_copy(struct table *oldtable, struct table *newtable) { const char *filekey, *data; size_t data_len; - r = table_newglobalcursor(oldtable, &cursor); + r = table_newglobalcursor(oldtable, true, &cursor); if (!RET_IS_OK(r)) return r; while (cursor_nexttempdata(oldtable, cursor, &filekey, @@ -2093,11 +2490,11 @@ static inline retvalue translate(struct table *oldmd5sums, struct table *newchec /* first add all md5sums to checksums if not there yet */ - r = table_newglobalcursor(oldmd5sums, &cursor); + r = table_newglobalcursor(oldmd5sums, true, &cursor); if (RET_WAS_ERROR(r)) return r; - while (cursor_nexttemp(oldmd5sums, cursor, - &filekey, &md5sum)) { + while (cursor_nexttempdata(oldmd5sums, cursor, + &filekey, &md5sum, NULL)) { struct checksums *n = NULL; const char *combined; size_t combinedlen; @@ -2156,23 +2553,23 @@ static inline retvalue translate(struct table *oldmd5sums, struct table *newchec /* then delete everything from checksums that is not in md5sums */ - r = table_newglobalcursor(oldmd5sums, &cursor); + r = table_newglobalcursor(oldmd5sums, true, &cursor); if (RET_WAS_ERROR(r)) return r; - r = table_newglobalcursor(newchecksums, &newcursor); + r = table_newglobalcursor(newchecksums, true, &newcursor); if (RET_WAS_ERROR(r)) { cursor_close(oldmd5sums, cursor); return r; } - while (cursor_nexttemp(oldmd5sums, cursor, - &filekey, &md5sum)) { + while (cursor_nexttempdata(oldmd5sums, cursor, + &filekey, &md5sum, NULL)) { bool more; int cmp; const char *newfilekey, *dummy; do { - more = cursor_nexttemp(newchecksums, newcursor, - &newfilekey, &dummy); + more = cursor_nexttempdata(newchecksums, newcursor, + &newfilekey, &dummy, NULL); /* should have been added in the last step */ assert (more); cmp = strcmp(filekey, newfilekey); diff --git a/database.h b/database.h index e4791949..439ca037 100644 --- a/database.h +++ b/database.h @@ -35,7 +35,7 @@ retvalue database_haspackages(const char *); bool table_recordexists(struct table *, const char *); /* retrieve a record from the database, return RET_NOTHING if there is none: */ -retvalue table_getrecord(struct table *, const char *, /*@out@*/char **, /*@out@*/ /*@null@*/ size_t *); +retvalue table_getrecord(struct table *, bool, const char *, /*@out@*/char **, /*@out@*/ /*@null@*/ size_t *); retvalue table_gettemprecord(struct table *, const char *, /*@out@*//*@null@*/const char **, /*@out@*//*@null@*/size_t *); retvalue table_getpair(struct table *, const char *, const char *, /*@out@*/const char **, /*@out@*/size_t *); @@ -47,10 +47,10 @@ retvalue table_deleterecord(struct table *, const char *key, bool ignoremissing) retvalue table_checkrecord(struct table *, const char *key, const char *data); retvalue table_removerecord(struct table *, const char *key, const char *data); -retvalue table_newglobalcursor(struct table *, /*@out@*/struct cursor **); -retvalue table_newduplicatecursor(struct table *, const char *, /*@out@*/struct cursor **, /*@out@*/const char **, /*@out@*/const char **, /*@out@*/size_t *); +retvalue table_newglobalcursor(struct table *, bool /*duplicate*/, /*@out@*/struct cursor **); +retvalue table_newduplicatecursor(struct table *, const char *, long long, /*@out@*/struct cursor **, /*@out@*/const char **, /*@out@*/const char **, /*@out@*/size_t *); +retvalue table_newduplicatepairedcursor(struct table *, const char *, /*@out@*/struct cursor **, /*@out@*/const char **, /*@out@*/const char **, /*@out@*/size_t *); retvalue table_newpairedcursor(struct table *, const char *, const char *, /*@out@*/struct cursor **, /*@out@*//*@null@*/const char **, /*@out@*//*@null@*/size_t *); -bool cursor_nexttemp(struct table *, struct cursor *, /*@out@*/const char **, /*@out@*/const char **); bool cursor_nexttempdata(struct table *, struct cursor *, /*@out@*/const char **, /*@out@*/const char **, /*@out@*/size_t *); bool cursor_nextpair(struct table *, struct cursor *, /*@null@*//*@out@*/const char **, /*@out@*/const char **, /*@out@*/const char **, /*@out@*/size_t *); retvalue cursor_replace(struct table *, struct cursor *, const char *, size_t); diff --git a/debian/changelog b/debian/changelog index 8e16eec6..e248235a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +reprepro (5.3.0-1.1) UNRELEASED; urgency=medium + + * Non-maintainer upload. + * Switch to dh 9 + * Support multiple versions. (Closes: #570623) + + -- Benjamin Drung Tue, 02 Apr 2019 16:59:11 +0200 + reprepro (5.3.0-1) unstable; urgency=medium * new release diff --git a/debian/control b/debian/control index eb750151..4c2fca62 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: reprepro Section: utils Priority: optional Maintainer: Bernhard R. Link -Build-Depends: debhelper (>= 10), libgpgme-dev, libdb-dev, libz-dev, libbz2-dev, liblzma-dev, libarchive-dev +Build-Depends: debhelper (>= 10), libgpgme-dev, libdb-dev, libz-dev, libbz2-dev, liblzma-dev, libarchive-dev, shunit2, db-util Standards-Version: 4.3.0 Vcs-Browser: https://salsa.debian.org/brlink/reprepro Vcs-Git: https://salsa.debian.org/brlink/reprepro.git -b debian diff --git a/debian/rules b/debian/rules index 5c6f51bf..dc11016d 100755 --- a/debian/rules +++ b/debian/rules @@ -3,85 +3,29 @@ # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 -include /usr/share/dpkg/architecture.mk +export DEB_BUILD_MAINT_OPTIONS = hardening=+all -# These are used for cross-compiling and for saving the configure script -# from having to guess our platform (since we know it already) -ifeq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) -BUILD_SYSTEM = --build $(DEB_BUILD_GNU_TYPE) -else -BUILD_SYSTEM = --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) -endif - -CFLAGS := $(shell DEB_BUILD_MAINT_OPTIONS=hardening=+all dpkg-buildflags --get CFLAGS) -CPPFLAGS := $(shell DEB_BUILD_MAINT_OPTIONS=hardening=+all dpkg-buildflags --get CPPFLAGS) -LDFLAGS := $(shell DEB_BUILD_MAINT_OPTIONS=hardening=+all dpkg-buildflags --get LDFLAGS) - -CFLAGS += -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wlogical-op -Wshadow -LDFLAGS += -Wl,-z,defs ifneq (,$(filter reprepro-nolibarchive,$(DEB_BUILD_OPTIONS))) ARCHIVEFLAGS= --without-libarchive else ARCHIVEFLAGS= --with-libarchive endif -ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) - MAKEFLAGS += -j$(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) -endif - -config.status: configure - dh_testdir - ./configure $(BUILD_SYSTEM) \ - --prefix=/usr --mandir=\$${prefix}/share/man \ - --with-liblzma --with-libgpgme \ - --with-libbz2 $(ARCHIVEFLAGS) \ - --disable-dependency-tracking \ - CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' \ - || ( echo configure failed with $$? ; echo BEGIN config.log: ; \ - cat config.log ; echo "END config.log" ; exit 1 ) -build-indep: build-indep-stamp -build-arch: build-arch-stamp -build: build-arch-stamp build-indep-stamp +%: + dh $@ -build-indep-stamp: - touch build-indep-stamp -build-arch-stamp: config.status - dh_testdir - $(MAKE) - touch build-arch-stamp +override_dh_auto_configure: + dh_auto_configure -- --with-libbz2 --with-liblzma --with-libgpgme $(ARCHIVEFLAGS) -clean: - dh_testdir - dh_testroot - rm -f build-arch-stamp build-indep-stamp - - # clean up after the build process. - if [ -e config.status ] ; then $(MAKE) distclean ; fi - ! test -f config.log - dh_clean - -# Build architecture-independent files here. -binary-indep: build-indep -# We have nothing to do. - -# Build architecture-dependent files here. -binary-arch: build-arch - dh_testdir - dh_testroot - dh_prep - dh_installdirs +override_dh_auto_install: $(MAKE) install DESTDIR=$(CURDIR)/debian/reprepro install -D -m 644 docs/reprepro.bash_completion debian/reprepro/usr/share/bash-completion/completions/reprepro install -D -m 644 docs/reprepro.zsh_completion debian/reprepro/usr/share/zsh/vendor-completions/_reprepro + +override_dh_installchangelogs: dh_installchangelogs ChangeLog - dh_installdocs - dh_installexamples - dh_link - dh_strip - dh_compress - dh_fixperms - dh_installdeb - dh_shlibdeps + +override_dh_gencontrol: grep -v '^reprepro:.*=' debian/reprepro.substvars > debian/reprepro.substvars.new mv debian/reprepro.substvars.new debian/reprepro.substvars # # if compile without libarchive, we need the program ar from binutils available @@ -98,8 +42,3 @@ binary-arch: build-arch echo "misc:Depends=" >> debian/reprepro.substvars ; \ fi dh_gencontrol - dh_md5sums - dh_builddeb - -binary: binary-indep binary-arch -.PHONY: build build-arch build-indep clean binary-indep binary-arch binary diff --git a/distribution.c b/distribution.c index cb98055d..11e2fe04 100644 --- a/distribution.c +++ b/distribution.c @@ -47,6 +47,7 @@ static retvalue distribution_free(struct distribution *distribution) { bool needsretrack = false; if (distribution != NULL) { + distribution->archive = NULL; free(distribution->suite); free(distribution->fakecomponentprefix); free(distribution->version); @@ -70,8 +71,10 @@ static retvalue distribution_free(struct distribution *distribution) { exportmode_done(&distribution->dsc); exportmode_done(&distribution->deb); exportmode_done(&distribution->udeb); + exportmode_done(&distribution->ddeb); atomlist_done(&distribution->contents_architectures); atomlist_done(&distribution->contents_components); + atomlist_done(&distribution->contents_dcomponents); atomlist_done(&distribution->contents_ucomponents); override_free(distribution->overrides.deb); override_free(distribution->overrides.udeb); @@ -186,6 +189,26 @@ static retvalue createtargets(struct distribution *distribution) { return r; } + if (atomlist_in(&distribution->ddebcomponents, c)) { + r = target_initialize_dbinary( + distribution, + c, a, + &distribution->ddeb, + distribution->readonly, + distribution->exportoptions[deo_noexport], + distribution->fakecomponentprefix, + &t); + if (RET_IS_OK(r)) { + if (last != NULL) { + last->next = t; + } else { + distribution->targets = t; + } + last = t; + } + if (RET_WAS_ERROR(r)) + return r; + } } /* check if this distribution contains source * (yes, yes, source is not really an architecture, but @@ -223,11 +246,17 @@ CFstartparse(distribution) { if (FAILEDTOALLOC(n)) return RET_ERROR_OOM; /* set some default value: */ + n->limit = 1; r = exportmode_init(&n->udeb, true, NULL, "Packages"); if (RET_WAS_ERROR(r)) { (void)distribution_free(n); return r; } + r = exportmode_init(&n->ddeb, true, "Release", "Packages"); + if (RET_WAS_ERROR(r)) { + (void)distribution_free(n); + return r; + } r = exportmode_init(&n->deb, true, "Release", "Packages"); if (RET_WAS_ERROR(r)) { (void)distribution_free(n); @@ -330,6 +359,9 @@ CFfinishparse(distribution) { notpropersuperset(&n->components, "Components", &n->contents_components, "ContentsComponents", atoms_components, n) || + notpropersuperset(&n->ddebcomponents, "DDebComponents", + &n->contents_dcomponents, "ContentsDComponents", + atoms_components, n) || notpropersuperset(&n->udebcomponents, "UDebComponents", &n->contents_ucomponents, "ContentsUComponents", atoms_components, n) || @@ -337,6 +369,9 @@ CFfinishparse(distribution) { // in the rest of the code...: notpropersuperset(&n->components, "Components", &n->udebcomponents, "UDebComponents", + atoms_components, n) || + notpropersuperset(&n->components, "Components", + &n->ddebcomponents, "DDebComponents", atoms_components, n)) { (void)distribution_free(n); return RET_ERROR; @@ -358,6 +393,14 @@ CFfinishparse(distribution) { n->contents.flags.udebs = false; } } + if (n->contents_dcomponents_set) { + if (n->contents_dcomponents.count > 0) { + n->contents.flags.enabled = true; + n->contents.flags.ddebs = true; + } else { + n->contents.flags.ddebs = false; + } + } if (n->contents_architectures_set) { if (n->contents_architectures.count > 0) n->contents.flags.enabled = true; @@ -401,6 +444,7 @@ CFallSETPROC(distribution, label) CFallSETPROC(distribution, description) CFallSETPROC(distribution, signed_by) CFsignwithSETPROC(distribution, signwith) +CFnumberSETPROC(distribution, -1, LLONG_MAX, limit) CFfileSETPROC(distribution, deb_override) CFfileSETPROC(distribution, udeb_override) CFfileSETPROC(distribution, dsc_override) @@ -412,8 +456,10 @@ CFinternatomsSETPROC(distribution, components, checkforcomponent, at_component) CFinternatomsSETPROC(distribution, architectures, checkforarchitecture, at_architecture) CFatomsublistSETPROC(distribution, contents_architectures, at_architecture, architectures, "Architectures") CFatomsublistSETPROC(distribution, contents_components, at_component, components, "Components") +CFatomsublistSETPROC(distribution, ddebcomponents, at_component, components, "Components") CFatomsublistSETPROC(distribution, udebcomponents, at_component, components, "Components") CFatomsublistSETPROC(distribution, contents_ucomponents, at_component, udebcomponents, "UDebComponents") +CFexportmodeSETPROC(distribution, ddeb) CFexportmodeSETPROC(distribution, udeb) CFexportmodeSETPROC(distribution, deb) CFexportmodeSETPROC(distribution, dsc) @@ -421,6 +467,32 @@ CFcheckvalueSETPROC(distribution, codename, checkforcodename) CFcheckvalueSETPROC(distribution, fakecomponentprefix, checkfordirectoryandidentifier) CFtimespanSETPROC(distribution, validfor) +CFuSETPROC(distribution, archive) { + CFSETPROCVARS(distribution, data, mydata); + char *codename; + retvalue r; + + r = config_getall(iter, &codename); + if (!RET_IS_OK(r)) + return r; + + for (struct distribution *d = mydata->distributions; d != NULL; d = d->next) { + if (strcmp(d->codename, codename) == 0) { + data->archive = d; + free(codename); + return RET_OK; + } + } + + fprintf(stderr, +"Error parsing config file %s, line %u:\n" +"No distribution has '%s' as codename.\n" +"Note: The archive distribution '%s' must be specified before '%s'.\n", + config_filename(iter), config_line(iter), codename, codename, data->codename); + free(codename); + return RET_ERROR_MISSING; +} + CFUSETPROC(distribution, Contents) { CFSETPROCVAR(distribution, d); return contentsoptions_parse(d, iter); @@ -456,6 +528,7 @@ CFUSETPROC(distribution, exportoptions) { static const struct configfield distributionconfigfields[] = { CF("AlsoAcceptFor", distribution, alsoaccept), CFr("Architectures", distribution, architectures), + CF("Archive", distribution, archive), CF("ByHandHooks", distribution, byhandhooks), CFr("Codename", distribution, codename), CFr("Components", distribution, components), @@ -463,6 +536,8 @@ static const struct configfield distributionconfigfields[] = { CF("ContentsComponents", distribution, contents_components), CF("Contents", distribution, Contents), CF("ContentsUComponents", distribution, contents_ucomponents), + CF("DDebComponents", distribution, ddebcomponents), + CF("DDebIndices", distribution, ddeb), CF("DebIndices", distribution, deb), CF("DebOverride", distribution, deb_override), CF("Description", distribution, description), @@ -471,6 +546,7 @@ static const struct configfield distributionconfigfields[] = { CF("DscOverride", distribution, dsc_override), CF("FakeComponentPrefix", distribution, fakecomponentprefix), CF("Label", distribution, label), + CF("Limit", distribution, limit), CF("Log", distribution, logger), CF("NotAutomatic", distribution, notautomatic), CF("ButAutomaticUpgrades", distribution, butautomaticupgrades), @@ -552,7 +628,7 @@ retvalue package_foreach(struct distribution *distribution, const struct atomlis if (r == RET_NOTHING) continue; } - r = package_openiterator(t, READONLY, &iterator); + r = package_openiterator(t, READONLY, true, &iterator); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) return result; @@ -584,7 +660,7 @@ retvalue package_foreach_c(struct distribution *distribution, const struct atoml continue; if (limitation_missed(packagetype, t->packagetype)) continue; - r = package_openiterator(t, READONLY, &iterator); + r = package_openiterator(t, READONLY, true, &iterator); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) return result; @@ -809,6 +885,9 @@ static retvalue export(struct distribution *distribution, bool onlyneeded) { retvalue result, r; struct release *release; + if (verbose >= 15) + fprintf(stderr, "trace: export(distribution={codename: %s}, onlyneeded=%s)\n", + distribution->codename, onlyneeded ? "true" : "false"); assert (distribution != NULL); if (distribution->exportoptions[deo_noexport]) @@ -906,6 +985,8 @@ retvalue distribution_exportlist(enum exportwhen when, struct distribution *dist bool todo = false; struct distribution *d; + if (verbose >= 15) + fprintf(stderr, "trace: distribution_exportlist() called.\n"); if (when == EXPORT_SILENT_NEVER) { for (d = distributions ; d != NULL ; d = d->next) { struct target *t; @@ -937,6 +1018,13 @@ retvalue distribution_exportlist(enum exportwhen when, struct distribution *dist result = RET_NOTHING; for (d=distributions; d != NULL; d = d->next) { + if (verbose >= 20) + fprintf(stderr, " looking at distribution {codename: %s, exportoptions[deo_noexport]: %s, omitted: %s, selected: %s, status: %d}.\n", + d->codename, + d->exportoptions[deo_noexport] ? "true" : "false", + d->omitted ? "true" : "false", + d->selected ? "true" : "false", + d->status); if (d->exportoptions[deo_noexport]) continue; if (d->omitted || !d->selected) @@ -1120,7 +1208,7 @@ retvalue package_remove_each(struct distribution *distribution, const struct ato for (t = distribution->targets ; t != NULL ; t = t->next) { if (!target_matches(t, components, architectures, packagetypes)) continue; - r = package_openiterator(t, READWRITE, &iterator); + r = package_openiterator(t, READWRITE, true, &iterator); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) return result; diff --git a/distribution.h b/distribution.h index d03118c5..10780844 100644 --- a/distribution.h +++ b/distribution.h @@ -52,6 +52,9 @@ struct distribution { struct strlist pulls; /* the key to sign with, may have no entries to mean unsigned: */ struct strlist signwith; + long long limit; + /* the codename of the archive distribution (when the limit is exceeded) */ + /*@null@*/struct distribution *archive; /* the override file to use by default */ /*@null@*/char *deb_override, *udeb_override, *dsc_override; /* fake component prefix (and codename antisuffix) for Release files: */ @@ -63,8 +66,10 @@ struct distribution { /* the list of components containing a debian-installer dir, * normally only "main" */ struct atomlist udebcomponents; + /* the list of components containing a debug directory */ + struct atomlist ddebcomponents; /* what kind of index files to generate */ - struct exportmode dsc, deb, udeb; + struct exportmode dsc, deb, udeb, ddeb; bool exportoptions[deo_COUNT]; /* (NONE must be 0 so it is the default) */ enum trackingtype { dt_NONE=0, dt_KEEP, dt_ALL, dt_MINIMAL } tracking; @@ -76,15 +81,19 @@ struct distribution { bool keepsources; bool embargoalls; } trackingoptions; + trackingdb trackingdb; /* what content files to generate */ struct contentsoptions contents; struct atomlist contents_architectures, contents_components, + contents_dcomponents, contents_ucomponents; bool contents_architectures_set, contents_components_set, + contents_dcomponents_set, contents_ucomponents_set, /* not used, just here to keep things simpler: */ + ddebcomponents_set, udebcomponents_set; /* A list of all targets contained in the distribution*/ struct target *targets; diff --git a/docs/reprepro.1 b/docs/reprepro.1 index 88e4c045..a3c57cfa 100644 --- a/docs/reprepro.1 +++ b/docs/reprepro.1 @@ -664,11 +664,12 @@ List the versions of the specified package in all distributions. .B lsbycomponent \fIpackage-name\fP Like ls, but group by component (and print component names). .TP -.B remove \fIcodename\fP \fIpackage-names\fP -Delete all packages in the specified distribution, +.B remove \fIcodename\fP \fIpackage-names\fP\fR[\fP=\fIversion\fP\fR]\fP \fI...\fP +Delete packages in the specified distribution, that have package name listed as argument. -(i.e. remove all packages \fBlist\fP with the same arguments and options -would list, except that an empty package list is not allowed.) +Package versions must be specified by appending '\fB=\fP' and the +version to the name (without spaces). When no version is specified, the latest +package version is removed. Note that like any other operation removing or replacing a package, the old package's files are unreferenced and thus may be automatically @@ -884,11 +885,13 @@ used files. Check all source package tracking information for the given distributions for files no longer to keep. .TP -.B copy \fIdestination-codename\fP \fIsource-codename\fP \fIpackages...\fP +.B copy \fIdestination-codename\fP \fIsource-codename\fP \fIpackage\fP\fR[\fP=\fIversion\fP\fR]\fP \fI...\fP Copy the given packages from one distribution to another. The packages are copied verbatim, no override files are consulted. Only components and architectures present in the source distribution are -copied. +copied. Package versions must be specified by appending '\fB=\fP' and the +version to the name (without spaces). When no version is specified, the latest +package version is copied. .TP .B copysrc \fIdestination-codename\fP \fIsource-codename\fP \fIsource-package\fP \fR[\fP\fIversions\fP\fR]\fP look at each package @@ -916,6 +919,40 @@ The packages are copied verbatim, no override files are consulted. Only components and architectures present in the source distribution are copied. .TP +.B move \fIdestination-codename\fP \fIsource-codename\fP \fIpackage\fP\fR[\fP=\fIversion\fP\fR]\fP \fI...\fP +Move the given packages from one distribution to another. +The packages are moved verbatim, no override files are consulted. +Only components and architectures present in the source distribution are +moved. Package versions must be specified by appending '\fB=\fP' and the +version to the name (without spaces). When no version is specified, the latest +package version is moved. +.TP +.B movesrc \fIdestination-codename\fP \fIsource-codename\fP \fIsource-package\fP \fR[\fP\fIversions\fP\fR]\fP +look at each package +(where package means, as usual, every package be it dsc, deb or udeb) +in the distribution specified by \fIsource-codename\fP +and identifies the relevant source package for each. +All packages matching the specified \fIsource-package\fP name +(and any \fIversion\fP if specified) +are moved to the \fIdestination-codename\fP distribution. +The packages are moved verbatim, no override files are consulted. +Only components and architectures present in the source distribution are +moved. +.TP +.B movematched \fIdestination-codename\fP \fIsource-codename\fP \fIglob\fP +Move packages matching the given glob (see \fBlistmatched\fP). + +The packages are moved verbatim, no override files are consulted. +Only components and architectures present in the source distribution are +moved. +.TP +.B movefilter \fIdestination-codename\fP \fIsource-codename\fP \fIformula\fP +Move packages matching the given formula (see \fBlistfilter\fP). +(all versions if no version is specified). +The packages are moved verbatim, no override files are consulted. +Only components and architectures present in the source distribution are +moved. +.TP .B restore \fIcodename\fP \fIsnapshot\fP \fIpackages...\fP .TP .B restoresrc \fIcodename\fP \fIsnapshot\fP \fIsource-epackage\fP \fR[\fP\fIversions\fP\fR]\fP @@ -1343,6 +1380,9 @@ distribution. See for rules which component packages are included into by default. This will also be copied into the Release files. .TP +.B DDebComponents +List of components containing .ddebs. +.TP .B UDebComponents Components with a debian\-installer subhierarchy containing .udebs. (E.g. simply "main") @@ -1563,6 +1603,21 @@ until no file is needed any more. .B needsources Not yet implemented. .TP +.B Limit +Limit the number of versions of a package per distribution, architecture, +component, and type. The limit must be a number. If the number is positive, +all old package version that exceed these limit will be removed or archived +(see +.B Archive +option), when a new package version is added. If the number is zero or negative, +all package version will be kept. By default only one package version will be +kept. +.TP +.B Archive +Specify a codename which must be declared before (to avoid loops). When packages +exceed the version count limit (specified in \fBLimit\fR), these packages will +be moved to the specified distribution instead of being removed. +.TP .B Log Specify a file to log additions and removals of this distribution into and/or external scripts to call when something is added or diff --git a/docs/reprepro.bash_completion b/docs/reprepro.bash_completion index 7872c155..508a651e 100644 --- a/docs/reprepro.bash_completion +++ b/docs/reprepro.bash_completion @@ -310,6 +310,10 @@ _reprepro() listmatched\ ls\ lsbycomponent\ + move\ + movefilter\ + movematched\ + movesrc\ predelete\ processincoming\ pull\ @@ -539,7 +543,7 @@ _reprepro() fi return 0; ;; - copy|copysrc|copyfilter|copymatched) + copy|copysrc|copyfilter|copymatched|move|movesrc|movefilter|movematched) # first argument is a codename if [[ $i -eq $COMP_CWORD ]] ; then parse_config diff --git a/error.h b/error.h index 0d5a46d7..715b1a87 100644 --- a/error.h +++ b/error.h @@ -10,6 +10,7 @@ bool interrupted(void); /* retvalue is simply an int. * just named to show it follows the given semantics */ /*@numabstract@*/ enum retvalue_enum { + DB_MALFORMED_KEY = -30001, RET_ERROR_INCOMING_DENY = -13, RET_ERROR_INTERNAL = -12, RET_ERROR_BZ2 = -11, diff --git a/exports.c b/exports.c index 0bc86d27..aef8a855 100644 --- a/exports.c +++ b/exports.c @@ -490,7 +490,7 @@ retvalue export_target(const char *relativedir, struct target *target, const st exportdescription(exportmode, buffer, 100)); status = "new"; } - r = package_openiterator(target, READONLY, &iterator); + r = package_openiterator(target, READONLY, true, &iterator); if (RET_WAS_ERROR(r)) { release_abortfile(file); free(relfilename); diff --git a/filelist.c b/filelist.c index 5c39da66..1cb7258d 100644 --- a/filelist.c +++ b/filelist.c @@ -693,7 +693,7 @@ retvalue filelists_translate(struct table *oldtable, struct table *newtable) { size_t olddata_len, newdata_size; char *newdata; - r = table_newglobalcursor(oldtable, &cursor); + r = table_newglobalcursor(oldtable, true, &cursor); if (!RET_IS_OK(r)) return r; while (cursor_nexttempdata(oldtable, cursor, &filekey, diff --git a/files.c b/files.c index 0d2174f8..0f3b6180 100644 --- a/files.c +++ b/files.c @@ -276,11 +276,11 @@ retvalue files_printmd5sums(void) { struct cursor *cursor; const char *filekey, *checksum; - r = table_newglobalcursor(rdb_checksums, &cursor); + r = table_newglobalcursor(rdb_checksums, true, &cursor); if (!RET_IS_OK(r)) return r; result = RET_NOTHING; - while (cursor_nexttemp(rdb_checksums, cursor, &filekey, &checksum)) { + while (cursor_nexttempdata(rdb_checksums, cursor, &filekey, &checksum, NULL)) { result = RET_OK; (void)fputs(filekey, stdout); (void)putchar(' '); @@ -303,11 +303,11 @@ retvalue files_printchecksums(void) { struct cursor *cursor; const char *filekey, *checksum; - r = table_newglobalcursor(rdb_checksums, &cursor); + r = table_newglobalcursor(rdb_checksums, true, &cursor); if (!RET_IS_OK(r)) return r; result = RET_NOTHING; - while (cursor_nexttemp(rdb_checksums, cursor, &filekey, &checksum)) { + while (cursor_nexttempdata(rdb_checksums, cursor, &filekey, &checksum, NULL)) { result = RET_OK; (void)fputs(filekey, stdout); (void)putchar(' '); @@ -329,11 +329,11 @@ retvalue files_foreach(per_file_action action, void *privdata) { struct cursor *cursor; const char *filekey, *checksum; - r = table_newglobalcursor(rdb_checksums, &cursor); + r = table_newglobalcursor(rdb_checksums, true, &cursor); if (!RET_IS_OK(r)) return r; result = RET_NOTHING; - while (cursor_nexttemp(rdb_checksums, cursor, &filekey, &checksum)) { + while (cursor_nexttempdata(rdb_checksums, cursor, &filekey, &checksum, NULL)) { if (interrupted()) { RET_UPDATE(result, RET_ERROR_INTERRUPTED); break; @@ -375,7 +375,7 @@ retvalue files_checkpool(bool fast) { bool improveable = false; result = RET_NOTHING; - r = table_newglobalcursor(rdb_checksums, &cursor); + r = table_newglobalcursor(rdb_checksums, true, &cursor); if (!RET_IS_OK(r)) return r; while (cursor_nexttempdata(rdb_checksums, cursor, @@ -421,7 +421,7 @@ retvalue files_collectnewchecksums(void) { char *fullfilename; result = RET_NOTHING; - r = table_newglobalcursor(rdb_checksums, &cursor); + r = table_newglobalcursor(rdb_checksums, true, &cursor); if (!RET_IS_OK(r)) return r; while (cursor_nexttempdata(rdb_checksums, cursor, diff --git a/globals.h b/globals.h index 945341dc..dca8bb03 100644 --- a/globals.h +++ b/globals.h @@ -1,6 +1,8 @@ #ifndef REPREPRO_GLOBALS_H #define REPREPRO_GLOBALS_H +#include + #ifdef AVOID_CHECKPROBLEMS # define bool _Bool # define true (1==1) @@ -91,4 +93,19 @@ enum compression { c_none, c_gzip, c_bzip2, c_lzma, c_xz, c_lunzip, c_COUNT }; #define nzNEW(num, type) ((type *)calloc(num, sizeof(type))) #define arrayinsert(type, array, position, length) ({type *__var = array; memmove(__var + (position) + 1, __var + (position), sizeof(type) * ((length) - (position)));}) +// strcmp2 behaves like strcmp, but allows both strings to be NULL +inline int strcmp2(const char *s1, const char *s2) { + if (s1 == NULL || s2 == NULL) { + if (s1 == NULL && s2 == NULL) { + return 0; + } else if (s1 == NULL) { + return -1; + } else { + return 1; + } + } else { + return strcmp(s1, s2); + } +} + #endif diff --git a/guesscomponent.c b/guesscomponent.c index 9972372b..8a13fc7e 100644 --- a/guesscomponent.c +++ b/guesscomponent.c @@ -36,10 +36,17 @@ retvalue guess_component(const char *codename, const struct atomlist *components int i; size_t section_len; + if (verbose >= 15) { + fprintf(stderr, "trace: guess_component(codename=%s, components=[", codename); + (void)atomlist_fprint(stderr, at_component, components); + fprintf(stderr, "], package=%s, section=%s, givencomponent=%s) called.\n", + package, section, atoms_components[givencomponent]); + } + if (atom_defined(givencomponent)) { if (!atomlist_in(components, givencomponent)) { (void)fprintf(stderr, -"Could not find '%s' in components of '%s': ", +"Could not find '%s' in components of '%s': '", atoms_components[givencomponent], codename); (void)atomlist_fprint(stderr, diff --git a/incoming.c b/incoming.c index 05bf6127..2ffa9651 100644 --- a/incoming.c +++ b/incoming.c @@ -1267,9 +1267,13 @@ static retvalue prepare_deb(const struct incoming *i, const struct candidate *c, assert (file == package->master); if (file->type == fe_DEB) package->packagetype = pt_deb; + else if (file->type == fe_DDEB) + package->packagetype = pt_ddeb; else package->packagetype = pt_udeb; + /* we use the deb overrides for ddebs too - ddebs aren't + * meant to have overrides so this is probably fine */ oinfo = override_search(file->type==fe_UDEB?into->overrides.udeb: into->overrides.deb, file->name); @@ -1280,6 +1284,16 @@ static retvalue prepare_deb(const struct incoming *i, const struct candidate *c, if (RET_WAS_ERROR(r)) return r; + if (file->type == fe_DDEB && + !atomlist_in(&into->ddebcomponents, package->component)) { + fprintf(stderr, +"Cannot put file '%s' of '%s' into component '%s',\n" +"as it is not listed in DDebComponents of '%s'!\n", + BASENAME(i, file->ofs), BASENAME(i, c->ofs), + atoms_components[package->component], + into->codename); + return RET_ERROR; + } if (file->type == fe_UDEB && !atomlist_in(&into->udebcomponents, package->component)) { fprintf(stderr, @@ -1702,6 +1716,7 @@ static retvalue prepare_for_distribution(const struct incoming *i, const struct switch (file->type) { case fe_UDEB: case fe_DEB: + case fe_DDEB: r = prepare_deb(i, c, d, file); break; case fe_DSC: @@ -1853,7 +1868,7 @@ static retvalue candidate_add_into(const struct incoming *i, const struct candid r = trackingdata_summon(tracks, c->source, c->sourceversion, &trackingdata); if (RET_WAS_ERROR(r)) { - (void)tracking_done(tracks); + (void)tracking_done(tracks, into); return r; } if (into->trackingoptions.needsources) { @@ -1939,7 +1954,7 @@ static retvalue candidate_add_into(const struct incoming *i, const struct candid retvalue r2; r2 = trackingdata_finish(tracks, &trackingdata); RET_UPDATE(r, r2); - r2 = tracking_done(tracks); + r2 = tracking_done(tracks, into); RET_ENDUPDATE(r, r2); } return r; diff --git a/main.c b/main.c index 31aa5cf3..0de6ae48 100644 --- a/main.c +++ b/main.c @@ -245,6 +245,57 @@ O(fast), O(x_morguedir), O(x_outdir), O(x_basedir), O(x_distdir), O(x_dbdir), O( act(const struct atomlist *, packagetypes), \ u(int, argc), u(const char *, argv[])) +static retvalue splitnameandversion(const char *nameandversion, const char **name_p, const char **version_p) { + char *version; + retvalue r; + + version = index(nameandversion, '='); + if (version != NULL) { + if (index(version+1, '=') != NULL) { + fprintf(stderr, +"Cannot parse '%s': more than one '='\n", + nameandversion); + *name_p = NULL; + *version_p = NULL; + r = RET_ERROR; + } else if (version[1] == '\0') { + fprintf(stderr, +"Cannot parse '%s': no version after '='\n", + nameandversion); + *name_p = NULL; + *version_p = NULL; + r = RET_ERROR; + } else if (version == nameandversion) { + fprintf(stderr, +"Cannot parse '%s': no source name found before the '='\n", + nameandversion); + *name_p = NULL; + *version_p = NULL; + r = RET_ERROR; + } else { + *name_p = strndup(nameandversion, version - nameandversion); + if (FAILEDTOALLOC(*name_p)) + r = RET_ERROR_OOM; + else + r = RET_OK; + *version_p = version + 1; + } + } else { + r = RET_OK; + *name_p = nameandversion; + *version_p = NULL; + } + return r; +} + +static inline void splitnameandversion_done(const char **name_p, const char **version_p) { + // In case version_p points to a non-NULL value, name_p needs to be freed after usage. + if (*version_p != NULL) { + free((char*)*name_p); + *name_p = NULL; + } +} + ACTION_N(n, n, y, printargs) { int i; @@ -650,19 +701,19 @@ ACTION_R(n, n, n, y, addreferences) { return ret; } -static retvalue remove_from_target(struct distribution *distribution, struct trackingdata *trackingdata, struct target *target, int count, const char * const *names, int *todo, bool *gotremoved) { +static retvalue remove_from_target(struct distribution *distribution, struct trackingdata *trackingdata, struct target *target, int count, struct nameandversion *nameandversion, int *remaining) { retvalue result, r; int i; result = RET_NOTHING; for (i = 0 ; i < count ; i++){ r = target_removepackage(target, distribution->logger, - names[i], trackingdata); + nameandversion[i].name, nameandversion[i].version, trackingdata); RET_UPDATE(distribution->status, r); if (RET_IS_OK(r)) { - if (!gotremoved[i]) - (*todo)--; - gotremoved[i] = true; + if (!nameandversion[i].found) + (*remaining)--; + nameandversion[i].found = true; } RET_UPDATE(result, r); } @@ -672,9 +723,10 @@ static retvalue remove_from_target(struct distribution *distribution, struct tra ACTION_D(y, n, y, remove) { retvalue result, r; struct distribution *distribution; + struct nameandversion data[argc-2]; struct target *t; - bool *gotremoved; - int todo; + char *delimiter; + int remaining; trackingdb tracks; struct trackingdata trackingdata; @@ -702,17 +754,25 @@ ACTION_D(y, n, y, remove) { } r = trackingdata_new(tracks, &trackingdata); if (RET_WAS_ERROR(r)) { - (void)tracking_done(tracks); + (void)tracking_done(tracks, distribution); + return r; + } + } + + for (int i = 0 ; i < argc-2 ; i++) { + data[i].found = false; + r = splitnameandversion(argv[2 + i], &data[i].name, &data[i].version); + if (RET_WAS_ERROR(r)) { + for (i-- ; i >= 0 ; i--) { + splitnameandversion_done(&data[i].name, &data[i].version); + } return r; } } - todo = argc-2; - gotremoved = nzNEW(argc - 2, bool); + remaining = argc-2; result = RET_NOTHING; - if (FAILEDTOALLOC(gotremoved)) - result = RET_ERROR_OOM; - else for (t = distribution->targets ; t != NULL ; t = t->next) { + for (t = distribution->targets ; t != NULL ; t = t->next) { if (!target_matches(t, components, architectures, packagetypes)) continue; r = target_initpackagesdb(t, READWRITE); @@ -723,8 +783,8 @@ ACTION_D(y, n, y, remove) { (distribution->tracking != dt_NONE) ? &trackingdata : NULL, - t, argc-2, argv+2, - &todo, gotremoved); + t, argc-2, data, + &remaining); RET_UPDATE(result, r); r = target_closepackagesdb(t); RET_UPDATE(distribution->status, r); @@ -738,26 +798,30 @@ ACTION_D(y, n, y, remove) { trackingdata_done(&trackingdata); else trackingdata_finish(tracks, &trackingdata); - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); } - if (verbose >= 0 && !RET_WAS_ERROR(result) && todo > 0) { + if (verbose >= 0 && !RET_WAS_ERROR(result) && remaining > 0) { int i = argc - 2; (void)fputs("Not removed as not found: ", stderr); - while (i > 0) { - i--; - assert(gotremoved != NULL); - if (!gotremoved[i]) { - (void)fputs(argv[2 + i], stderr); - todo--; - if (todo > 0) - (void)fputs(", ", stderr); + delimiter = ""; + for (i = 0; i < argc - 2; i++) { + if (!data[i].found) { + if (data[i].version == NULL) { + fprintf(stderr, "%s%s", delimiter, data[i].name); + } else { + fprintf(stderr, "%s%s=%s", delimiter, data[i].name, data[i].version); + } + remaining--; + delimiter = ", "; } } (void)fputc('\n', stderr); } - free(gotremoved); + for (int i = 0; i < argc - 2; i++) { + splitnameandversion_done(&data[i].name, &data[i].version); + } return result; } @@ -835,7 +899,7 @@ static retvalue remove_packages(struct distribution *distribution, struct remove } } } - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); return result; } @@ -899,44 +963,12 @@ ACTION_D(n, n, y, removesrcs) { } for (i = 0 ; i < argc-2 ; i++) { data[i].found = false; - data[i].sourcename = argv[2 + i]; - data[i].sourceversion = index(data[i].sourcename, '='); - if (data[i].sourceversion != NULL) { - if (index(data[i].sourceversion+1, '=') != NULL) { - fprintf(stderr, -"Cannot parse '%s': more than one '='\n", - data[i].sourcename); - data[i].sourcename = NULL; - r = RET_ERROR; - } else if (data[i].sourceversion[1] == '\0') { - fprintf(stderr, -"Cannot parse '%s': no version after '='\n", - data[i].sourcename); - data[i].sourcename = NULL; - r = RET_ERROR; - } else if (data[i].sourceversion == data[i].sourcename) { - fprintf(stderr, -"Cannot parse '%s': no source name found before the '='\n", - data[i].sourcename); - data[i].sourcename = NULL; - r = RET_ERROR; - } else { - data[i].sourcename = strndup(data[i].sourcename, - data[i].sourceversion - - data[i].sourcename); - if (FAILEDTOALLOC(data[i].sourcename)) - r = RET_ERROR_OOM; - else - r = RET_OK; - } - if (RET_WAS_ERROR(r)) { - for (i-- ; i >= 0 ; i--) { - if (data[i].sourceversion != NULL) - free((char*)data[i].sourcename); - } - return r; + r = splitnameandversion(argv[2 + i], &data[i].sourcename, &data[i].sourceversion); + if (RET_WAS_ERROR(r)) { + for (i--; i >= 0; i--) { + splitnameandversion_done(&data[i].sourcename, &data[i].sourceversion); } - data[i].sourceversion++; + return r; } } data[i].sourcename = NULL; @@ -954,8 +986,7 @@ ACTION_D(n, n, y, removesrcs) { "No package from source '%s' (any version) found.\n", data[i].sourcename); } - if (data[i].sourceversion != NULL) - free((char*)data[i].sourcename); + splitnameandversion_done(&data[i].sourcename, &data[i].sourceversion); } return r; } @@ -1008,7 +1039,7 @@ ACTION_D(y, n, y, removefilter) { else { r = trackingdata_new(tracks, &trackingdata); if (RET_WAS_ERROR(r)) { - (void)tracking_done(tracks); + (void)tracking_done(tracks, distribution); term_free(condition); return r; } @@ -1023,7 +1054,7 @@ ACTION_D(y, n, y, removefilter) { condition); if (tracks != NULL) { trackingdata_finish(tracks, &trackingdata); - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); } term_free(condition); @@ -1070,7 +1101,7 @@ ACTION_D(y, n, y, removematched) { else { r = trackingdata_new(tracks, &trackingdata); if (RET_WAS_ERROR(r)) { - (void)tracking_done(tracks); + (void)tracking_done(tracks, distribution); return r; } } @@ -1084,7 +1115,7 @@ ACTION_D(y, n, y, removematched) { (void*)argv[2]); if (tracks != NULL) { trackingdata_finish(tracks, &trackingdata); - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); } return result; @@ -1184,23 +1215,27 @@ ACTION_C(n, n, n, listcodenames) { } static retvalue list_in_target(struct target *target, const char *packagename) { - retvalue r, result; - struct package pkg; + retvalue r, result = RET_NOTHING; + struct package_cursor iterator; if (listmax == 0) return RET_NOTHING; - result = package_get(target, packagename, NULL, &pkg); - if (RET_IS_OK(result)) { + r = package_openduplicateiterator(target, packagename, 0, &iterator); + if (!RET_IS_OK(r)) + return r; + + do { if (listskip <= 0) { - r = listformat_print(listformat, &pkg); + r = listformat_print(listformat, &iterator.current); RET_UPDATE(result, r); if (listmax > 0) listmax--; } else listskip--; - package_done(&pkg); - } + } while (package_next(&iterator)); + r = package_closeiterator(&iterator); + RET_ENDUPDATE(result, r); return result; } @@ -1277,17 +1312,20 @@ static retvalue newlsversion(struct lsversion **versions_p, struct package *pack static retvalue ls_in_target(struct target *target, const char *packagename, struct lsversion **versions_p) { retvalue r, result; - struct package pkg; + struct package_cursor iterator; - result = package_get(target, packagename, NULL, &pkg); - if (RET_IS_OK(result)) { - r = package_getversion(&pkg); + result = package_openduplicateiterator(target, packagename, 0, &iterator); + if (!RET_IS_OK(result)) + return result; + do { + r = package_getversion(&iterator.current); if (RET_IS_OK(r)) - r = newlsversion(versions_p, &pkg, + r = newlsversion(versions_p, &iterator.current, target->architecture); - package_done(&pkg); RET_UPDATE(result, r); - } + } while (package_next(&iterator)); + r = package_closeiterator(&iterator); + RET_ENDUPDATE(result, r); return result; } @@ -1569,14 +1607,14 @@ ACTION_B(n, n, n, dumpcontents) { result = database_openpackages(argv[1], true, &packages); if (RET_WAS_ERROR(result)) return result; - r = table_newglobalcursor(packages, &cursor); + r = table_newglobalcursor(packages, true, &cursor); assert (r != RET_NOTHING); if (RET_WAS_ERROR(r)) { (void)table_close(packages); return r; } result = RET_NOTHING; - while (cursor_nexttemp(packages, cursor, &package, &chunk)) { + while (cursor_nexttempdata(packages, cursor, &package, &chunk, NULL)) { printf("'%s' -> '%s'\n", package, chunk); result = RET_OK; } @@ -1930,8 +1968,11 @@ ACTION_B(y, n, y, dumppull) { return result; } -ACTION_D(y, n, y, copy) { +static retvalue copy_or_move(struct distribution *alldistributions, const struct atomlist * architectures, const struct atomlist * components, + const struct atomlist * packagetypes, int argc, const char * argv[], bool remove_source) { struct distribution *destination, *source; + struct nameandversion data[argc-2]; + int i; retvalue result; result = distribution_get(alldistributions, argv[1], true, &destination); @@ -1945,19 +1986,44 @@ ACTION_D(y, n, y, copy) { if (destination->readonly) { fprintf(stderr, -"Cannot copy packages to read-only distribution '%s'.\n", - destination->codename); +"Cannot %s packages to read-only distribution '%s'.\n", + remove_source ? "move" : "copy", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); if (RET_WAS_ERROR(result)) return result; - return copy_by_name(destination, source, argc-3, argv+3, - components, architectures, packagetypes); + for (i = 0; i < argc-3; i++) { + result = splitnameandversion(argv[3 + i], &data[i].name, &data[i].version); + if (RET_WAS_ERROR(result)) { + for (i-- ; i >= 0 ; i--) { + splitnameandversion_done(&data[i].name, &data[i].version); + } + return result; + } + } + data[i].name = NULL; + data[i].version = NULL; + + result = copy_by_name(destination, source, data, + components, architectures, packagetypes, remove_source); + for (i = 0; i < argc - 3; i++) { + splitnameandversion_done(&data[i].name, &data[i].version); + } + return result; +} + +ACTION_D(y, n, y, copy) { + return copy_or_move(alldistributions, architectures, components, packagetypes, argc, argv, false); } -ACTION_D(y, n, y, copysrc) { +ACTION_D(y, n, y, move) { + return copy_or_move(alldistributions, architectures, components, packagetypes, argc, argv, true); +} + +static retvalue copysrc_or_movesrc(struct distribution *alldistributions, const struct atomlist * architectures, const struct atomlist * components, + const struct atomlist * packagetypes, int argc, const char * argv[], bool remove_source) { struct distribution *destination, *source; retvalue result; @@ -1971,8 +2037,8 @@ ACTION_D(y, n, y, copysrc) { return result; if (destination->readonly) { fprintf(stderr, -"Cannot copy packages to read-only distribution '%s'.\n", - destination->codename); +"Cannot %s packages to read-only distribution '%s'.\n", + remove_source ? "move" : "copy", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); @@ -1980,11 +2046,20 @@ ACTION_D(y, n, y, copysrc) { return result; return copy_by_source(destination, source, argc-3, argv+3, - components, architectures, packagetypes); + components, architectures, packagetypes, remove_source); return result; } -ACTION_D(y, n, y, copyfilter) { +ACTION_D(y, n, y, copysrc) { + return copysrc_or_movesrc(alldistributions, architectures, components, packagetypes, argc, argv, false); +} + +ACTION_D(y, n, y, movesrc) { + return copysrc_or_movesrc(alldistributions, architectures, components, packagetypes, argc, argv, true); +} + +static retvalue copy_or_move_filter(struct distribution *alldistributions, const struct atomlist * architectures, const struct atomlist * components, + const struct atomlist * packagetypes, int argc, const char * argv[], bool remove_source) { struct distribution *destination, *source; retvalue result; @@ -2000,8 +2075,8 @@ ACTION_D(y, n, y, copyfilter) { return result; if (destination->readonly) { fprintf(stderr, -"Cannot copy packages to read-only distribution '%s'.\n", - destination->codename); +"Cannot %s packages to read-only distribution '%s'.\n", + remove_source ? "move" : "copy", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); @@ -2009,10 +2084,19 @@ ACTION_D(y, n, y, copyfilter) { return result; return copy_by_formula(destination, source, argv[3], - components, architectures, packagetypes); + components, architectures, packagetypes, remove_source); } -ACTION_D(y, n, y, copymatched) { +ACTION_D(y, n, y, copyfilter) { + return copy_or_move_filter(alldistributions, architectures, components, packagetypes, argc, argv, false); +} + +ACTION_D(y, n, y, movefilter) { + return copy_or_move_filter(alldistributions, architectures, components, packagetypes, argc, argv, true); +} + +static retvalue copy_or_move_matched(struct distribution *alldistributions, const struct atomlist * architectures, const struct atomlist * components, + const struct atomlist * packagetypes, int argc, const char * argv[], bool remove_source) { struct distribution *destination, *source; retvalue result; @@ -2028,8 +2112,8 @@ ACTION_D(y, n, y, copymatched) { return result; if (destination->readonly) { fprintf(stderr, -"Cannot copy packages to read-only distribution '%s'.\n", - destination->codename); +"Cannot %s packages to read-only distribution '%s'.\n", + remove_source ? "move" : "copy", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); @@ -2037,7 +2121,15 @@ ACTION_D(y, n, y, copymatched) { return result; return copy_by_glob(destination, source, argv[3], - components, architectures, packagetypes); + components, architectures, packagetypes, remove_source); +} + +ACTION_D(y, n, y, copymatched) { + return copy_or_move_matched(alldistributions, architectures, components, packagetypes, argc, argv, false); +} + +ACTION_D(y, n, y, movematched) { + return copy_or_move_matched(alldistributions, architectures, components, packagetypes, argc, argv, true); } ACTION_D(y, n, y, restore) { @@ -2281,7 +2373,7 @@ ACTION_D(n, n, y, removetrack) { result = tracking_remove(tracks, argv[2], argv[3]); - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); return result; } @@ -2372,7 +2464,7 @@ ACTION_D(n, n, y, tidytracks) { } r = tracking_tidyall(tracks); RET_UPDATE(result, r); - r = tracking_done(tracks); + r = tracking_done(tracks, d); RET_ENDUPDATE(result, r); if (RET_WAS_ERROR(result)) break; @@ -2408,7 +2500,7 @@ ACTION_B(n, n, y, dumptracks) { continue; r = tracking_printall(tracks); RET_UPDATE(result, r); - r = tracking_done(tracks); + r = tracking_done(tracks, d); RET_ENDUPDATE(result, r); if (RET_WAS_ERROR(result)) break; @@ -2519,54 +2611,56 @@ ACTION_F(y, n, y, y, reoverride) { /*****************retrieving Description data from .deb files***************/ static retvalue repair_descriptions(struct target *target) { - struct package_cursor iterator; - retvalue result, r; + struct package_cursor iterator; + retvalue result, r; - assert(target->packages == NULL); - assert(target->packagetype == pt_deb || target->packagetype == pt_udeb); + assert(target->packages == NULL); + assert(target->packagetype == pt_deb || + target->packagetype == pt_udeb || + target->packagetype == pt_ddeb); - if (verbose > 2) { - printf( + if (verbose > 2) { + printf( "Redoing checksum information for packages in '%s'...\n", - target->identifier); - } + target->identifier); + } - r = package_openiterator(target, READWRITE, &iterator); - if (!RET_IS_OK(r)) - return r; - result = RET_NOTHING; - while (package_next(&iterator)) { - char *newcontrolchunk = NULL; + r = package_openiterator(target, READWRITE, true, &iterator); + if (!RET_IS_OK(r)) + return r; + result = RET_NOTHING; + while (package_next(&iterator)) { + char *newcontrolchunk = NULL; if (interrupted()) { result = RET_ERROR_INTERRUPTED; break; } /* replace it by itself to normalize the Description field */ - r = description_addpackage(target, iterator.current.name, + r = description_addpackage(target, iterator.current.name, iterator.current.control, &newcontrolchunk); - RET_UPDATE(result, r); - if (RET_WAS_ERROR(r)) - break; - if (RET_IS_OK(r)) { + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + if (RET_IS_OK(r)) { if (verbose >= 0) { printf( "Fixing description for '%s'...\n", iterator.current.name); } 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; + 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; } ACTION_F(y, n, y, y, repairdescriptions) { @@ -2698,6 +2792,13 @@ ACTION_D(y, y, y, includedeb) { "Calling includeudeb with a -T not containing udeb makes no sense!\n"); return RET_ERROR; } + } else if (strcmp(argv[0], "includeddeb") == 0) { + packagetype = pt_ddeb; + if (limitations_missed(packagetypes, pt_ddeb)) { + fprintf(stderr, +"Calling includeddeb with a -T not containing ddeb makes no sense!\n"); + return RET_ERROR; + } } else if (strcmp(argv[0], "includedeb") == 0) { packagetype = pt_deb; if (limitations_missed(packagetypes, pt_deb)) { @@ -2718,6 +2819,10 @@ ACTION_D(y, y, y, includedeb) { if (!endswith(filename, ".udeb") && !IGNORING(extension, "includeudeb called with file '%s' not ending with '.udeb'\n", filename)) return RET_ERROR; + } else if (packagetype == pt_ddeb) { + if (!endswith(filename, ".ddeb") && !IGNORING(extension, +"includeddeb called with file '%s' not ending with '.ddeb'\n", filename)) + return RET_ERROR; } else { if (!endswith(filename, ".deb") && !IGNORING(extension, "includedeb called with file '%s' not ending with '.deb'\n", filename)) @@ -2740,6 +2845,8 @@ ACTION_D(y, y, y, includedeb) { result = override_read(distribution->udeb_override, &distribution->overrides.udeb, false); else + /* we use the normal deb overrides for ddebs too - + * they're not meant to have overrides anyway */ result = override_read(distribution->deb_override, &distribution->overrides.deb, false); if (RET_WAS_ERROR(result)) { @@ -2786,7 +2893,7 @@ ACTION_D(y, y, y, includedeb) { distribution_unloadoverrides(distribution); - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); return result; } @@ -2860,7 +2967,7 @@ ACTION_D(y, y, y, includedsc) { logger_wait(); distribution_unloadoverrides(distribution); - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); return result; } @@ -2915,7 +3022,7 @@ ACTION_D(y, y, y, include) { } result = distribution_loaduploaders(distribution); if (RET_WAS_ERROR(result)) { - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); return result; } @@ -2927,7 +3034,7 @@ ACTION_D(y, y, y, include) { distribution_unloadoverrides(distribution); distribution_unloaduploaders(distribution); - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); return result; } @@ -3774,7 +3881,7 @@ ACTION_D(y, n, y, flood) { RET_UPDATE(distribution->status, result); if (tracks != NULL) { - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); } return result; @@ -3987,6 +4094,14 @@ static const struct action { 3, 3, "[-C ] [-A ] [-T ] copymatched "}, {"copyfilter", A_Dact(copyfilter), 3, 3, "[-C ] [-A ] [-T ] copyfilter "}, + {"move", A_Dact(move), + 3, -1, "[-C ] [-A ] [-T ] move "}, + {"movesrc", A_Dact(movesrc), + 3, -1, "[-C ] [-A ] [-T ] movesrc []"}, + {"movematched", A_Dact(movematched), + 3, 3, "[-C ] [-A ] [-T ] movematched "}, + {"movefilter", A_Dact(movefilter), + 3, 3, "[-C ] [-A ] [-T ] movefilter "}, {"restore", A_Dact(restore), 3, -1, "[-C ] [-A ] [-T ] restore "}, {"restoresrc", A_Dact(restoresrc), @@ -4003,6 +4118,8 @@ static const struct action { 2, -1, "[--delete] includedeb <.deb-file>"}, {"includeudeb", A_Dactsp(includedeb)|NEED_DELNEW, 2, -1, "[--delete] includeudeb <.udeb-file>"}, + {"includeddeb", A_Dactsp(includedeb)|NEED_DELNEW, + 2, -1, "[--delete] includeddeb <.ddeb-file>"}, {"includedsc", A_Dactsp(includedsc)|NEED_DELNEW, 2, 2, "[--delete] includedsc "}, {"include", A_Dactsp(include)|NEED_DELNEW, @@ -4195,7 +4312,7 @@ static retvalue callaction(command_t command, const struct action *action, int a if (r == RET_NOTHING) { fprintf(stderr, "Error: Packagetype '%s' as given to --packagetype is not know.\n" -"(only dsc, deb, udeb and combinations of those are allowed)\n", +"(only dsc, deb, udeb, ddeb and combinations of those are allowed)\n", unknownitem); r = RET_ERROR; } @@ -4462,7 +4579,7 @@ static void handle_option(int c, const char *argument) { " -P, --priority : Force include* to set priority.\n" " -C, --component : Add,list or delete only in component.\n" " -A, --architecture : Add,list or delete only to architecture.\n" -" -T, --type : Add,list or delete only type (dsc,deb,udeb).\n" +" -T, --type : Add,list or delete only type (dsc,deb,udeb,ddeb).\n" "\n" "actions (selection, for more see manpage):\n" " dumpreferences: Print all saved references\n" @@ -4481,6 +4598,8 @@ static void handle_option(int c, const char *argument) { " Include the given upload.\n" " includedeb <.deb-file>\n" " Include the given binary package.\n" +" includeddeb <.ddeb-file>\n" +" Include the given debug binary package.\n" " includeudeb <.udeb-file>\n" " Include the given installer binary package.\n" " includedsc <.dsc-file>\n" diff --git a/needbuild.c b/needbuild.c index 6b37f675..fa8801a4 100644 --- a/needbuild.c +++ b/needbuild.c @@ -297,7 +297,7 @@ retvalue find_needs_build(struct distribution *distribution, architecture_t arch onlycomponents, architecture_source, pt_dsc, check_source_needs_build, &d); - r = tracking_done(d.tracks); + r = tracking_done(d.tracks, distribution); RET_ENDUPDATE(result, r); return result; } diff --git a/package.h b/package.h index dee9df26..7a46a04c 100644 --- a/package.h +++ b/package.h @@ -68,9 +68,11 @@ struct package_cursor { /*@temp@*/struct target *target; struct cursor *cursor; struct package current; + bool close_database; }; -retvalue package_openiterator(struct target *, bool /*readonly*/, /*@out@*/struct package_cursor *); +retvalue package_openiterator(struct target *, bool /*readonly*/, bool /*duplicate*/, /*@out@*/struct package_cursor *); +retvalue package_openduplicateiterator(struct target *t, const char *name, long long, /*@out@*/struct package_cursor *tc); bool package_next(struct package_cursor *); retvalue package_closeiterator(struct package_cursor *); diff --git a/printlistformat.c b/printlistformat.c index 3b2d2e73..b1442bb1 100644 --- a/printlistformat.c +++ b/printlistformat.c @@ -38,6 +38,10 @@ retvalue listformat_print(const char *listformat, struct package *package) { retvalue r; const char *p, *q; + if (verbose >= 15) + fprintf(stderr, "trace: listformat_print(package={name: %s, version: %s, pkgname: %s}) called.\n", + package->name, package->version, package->pkgname); + if (listformat == NULL) { r = package_getversion(package); diff --git a/pull.c b/pull.c index 622477f5..3c5673df 100644 --- a/pull.c +++ b/pull.c @@ -60,6 +60,8 @@ struct pull_rule { //e.g. "UDebComponents: main" // (not set means all) struct atomlist udebcomponents; bool udebcomponents_set; + // We don't have equivalents for ddebs yet since we don't know + // what the Debian archive layout is going to look like // NULL means no condition /*@null@*/term *includecondition; struct filterlist filterlist; diff --git a/reference.c b/reference.c index 65e1ca1f..ff1f0e7c 100644 --- a/reference.c +++ b/reference.c @@ -57,6 +57,10 @@ retvalue references_check(const char *referee, const struct strlist *filekeys) { retvalue references_increment(const char *needed, const char *neededby) { retvalue r; + if (verbose >= 15) + fprintf(stderr, "trace: references_insert(needed=%s, neededby=%s) called.\n", + needed, neededby); + r = table_addrecord(rdb_references, needed, neededby, strlen(neededby), false); if (RET_IS_OK(r) && verbose > 8) @@ -95,6 +99,20 @@ retvalue references_insert(const char *identifier, retvalue result, r; int i; + if (verbose >= 15) { + fprintf(stderr, "trace: references_insert(identifier=%s, files=[", identifier); + for (i = 0 ; i < files->count ; i++) { + fprintf(stderr, "%s%s", i == 0 ? "" : ", ", files->values[i]); + } + fprintf(stderr, "], exclude=%s", exclude == NULL ? NULL : "["); + if (exclude != NULL) { + for (i = 0 ; i < exclude->count ; i++) { + fprintf(stderr, "%s%s", i == 0 ? "" : ", ", exclude->values[i]); + } + } + fprintf(stderr, "%s) called.\n", exclude == NULL ? "" : "]"); + } + result = RET_NOTHING; for (i = 0 ; i < files->count ; i++) { @@ -152,7 +170,7 @@ retvalue references_remove(const char *neededby) { const char *found_to, *found_by; size_t datalen, l; - r = table_newglobalcursor(rdb_references, &cursor); + r = table_newglobalcursor(rdb_references, true, &cursor); if (!RET_IS_OK(r)) return r; @@ -188,13 +206,13 @@ retvalue references_dump(void) { retvalue result, r; const char *found_to, *found_by; - r = table_newglobalcursor(rdb_references, &cursor); + r = table_newglobalcursor(rdb_references, true, &cursor); if (!RET_IS_OK(r)) return r; result = RET_OK; - while (cursor_nexttemp(rdb_references, cursor, - &found_to, &found_by)) { + while (cursor_nexttempdata(rdb_references, cursor, + &found_to, &found_by, NULL)) { if (fputs(found_by, stdout) == EOF || putchar(' ') == EOF || puts(found_to) == EOF) { diff --git a/release.c b/release.c index 1b2ad516..d44872e2 100644 --- a/release.c +++ b/release.c @@ -175,6 +175,10 @@ retvalue release_init(struct release **release, const char *codename, const char size_t len, suitelen, codenamelen; retvalue r; + if (verbose >= 15) + fprintf(stderr, "trace: release_init(codename=%s, suite=%s, fakecomponentprefix=%s) called.\n", + codename, suite, fakecomponentprefix); + n = zNEW(struct release); if (FAILEDTOALLOC(n)) return RET_ERROR_OOM; @@ -441,7 +445,7 @@ static retvalue release_usecached(struct release *release, if (filename[ic] == NULL) continue; - r = table_getrecord(release->cachedb, filename[ic], + r = table_getrecord(release->cachedb, false, filename[ic], &combinedchecksum, NULL); if (!RET_IS_OK(r)) { result = r; diff --git a/remoterepository.c b/remoterepository.c index 3134ca4a..05ddd4d0 100644 --- a/remoterepository.c +++ b/remoterepository.c @@ -1542,6 +1542,13 @@ struct remote_index *remote_index(struct remote_distribution *rd, const char *ar cachefilename = genlistsfilename("uPackages", 4, rd->repository->name, rd->suite, component, architecture, ENDOFARGUMENTS); + } else if (packagetype == pt_ddeb) { + filename_in_release = mprintf( +"%s/debug/binary-%s/Packages", + component, architecture); + cachefilename = genlistsfilename("dPackages", 4, + rd->repository->name, rd->suite, + component, architecture, ENDOFARGUMENTS); } else if (packagetype == pt_dsc) { filename_in_release = mprintf( "%s/source/Sources", @@ -1564,6 +1571,10 @@ void cachedlistfile_need_index(struct cachedlistfile *list, const char *reposito cachedlistfile_need(list, "uPackages", 4, repository, suite, component, architecture, ENDOFARGUMENTS); + } else if (packagetype == pt_ddeb) { + cachedlistfile_need(list, "dPackages", 4, + repository, suite, + component, architecture, ENDOFARGUMENTS); } else if (packagetype == pt_dsc) { cachedlistfile_need(list, "Sources", 3, repository, suite, @@ -1580,6 +1591,11 @@ struct remote_index *remote_flat_index(struct remote_distribution *rd, packagety cachefilename = genlistsfilename("Packages", 2, rd->repository->name, rd->suite, ENDOFARGUMENTS); + } else if (packagetype == pt_ddeb) { + filename_in_release = strdup("dPackages"); + cachefilename = genlistsfilename("dPackages", 2, + rd->repository->name, rd->suite, + ENDOFARGUMENTS); } else if (packagetype == pt_dsc) { filename_in_release = strdup("Sources"); cachefilename = genlistsfilename("Sources", 2, @@ -1595,6 +1611,9 @@ void cachedlistfile_need_flat_index(struct cachedlistfile *list, const char *rep if (packagetype == pt_deb) { cachedlistfile_need(list, "Packages", 2, repository, suite, ENDOFARGUMENTS); + } else if (packagetype == pt_ddeb) { + cachedlistfile_need(list, "dPackages", 2, + repository, suite, ENDOFARGUMENTS); } else if (packagetype == pt_dsc) { cachedlistfile_need(list, "Sources", 1, repository, suite, ENDOFARGUMENTS); diff --git a/signature.c b/signature.c index 991fec24..93ff207d 100644 --- a/signature.c +++ b/signature.c @@ -53,6 +53,7 @@ retvalue gpgerror(gpg_error_t err) { static gpg_error_t signature_getpassphrase(UNUSED(void *hook), const char *uid_hint, UNUSED(const char *info), int prev_was_bad, int fd) { char *msg; const char *p; + int e = 0; msg = mprintf("%s needs a passphrase\nPlease enter passphrase%s:", (uid_hint!=NULL)?uid_hint:"key", @@ -60,8 +61,18 @@ static gpg_error_t signature_getpassphrase(UNUSED(void *hook), const char *uid_h if (msg == NULL) return gpg_err_make(GPG_ERR_SOURCE_USER_1, GPG_ERR_ENOMEM); p = getpass(msg); - write(fd, p, strlen(p)); - write(fd, "\n", 1); + if (write(fd, p, strlen(p)) < 0) { + e = errno; + } + if (write(fd, "\n", 1) < 0 && e == 0) { + e = errno; + } + if (e != 0) { + fprintf(stderr, "Error %d writing to fd %i: %s\n", + e, fd, strerror(e)); + free(msg); + return RET_ERRNO(e); + } free(msg); return GPG_ERR_NO_ERROR; } diff --git a/sizes.c b/sizes.c index 34f76710..b10ccab8 100644 --- a/sizes.c +++ b/sizes.c @@ -225,7 +225,7 @@ retvalue sizes_distributions(struct distribution *alldistributions, bool specifi } if (ds == NULL) return RET_NOTHING; - r = table_newglobalcursor(rdb_references, &cursor); + r = table_newglobalcursor(rdb_references, true, &cursor); if (!RET_IS_OK(r)) { distribution_sizes_freelist(ds); return r; diff --git a/sourcecheck.c b/sourcecheck.c index 0c23176a..8182639f 100644 --- a/sourcecheck.c +++ b/sourcecheck.c @@ -76,7 +76,7 @@ static retvalue collect_source_versions(struct distribution *d, struct info_sour for (t = d->targets ; t != NULL ; t = t->next) { if (t->architecture != architecture_source) continue; - r = package_openiterator(t, true, &cursor); + r = package_openiterator(t, true, true, &cursor); if (RET_WAS_ERROR(r)) { RET_UPDATE(result, r); break; @@ -196,7 +196,7 @@ static retvalue process_binaries(struct distribution *d, struct info_source *sou for (t = d->targets ; t != NULL ; t = t->next) { if (t->architecture == architecture_source) continue; - r = package_openiterator(t, true, &cursor); + r = package_openiterator(t, true, true, &cursor); if (RET_WAS_ERROR(r)) { RET_UPDATE(result, r); break; diff --git a/target.c b/target.c index 44424cd2..0984fc46 100644 --- a/target.c +++ b/target.c @@ -53,6 +53,10 @@ static char *calc_identifier(const char *codename, component_t component, archit 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], @@ -131,6 +135,25 @@ retvalue target_initialize_ubinary(struct distribution *d, component_t component 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, @@ -227,12 +250,14 @@ retvalue target_closepackagesdb(struct target *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); - if (logger != NULL) { - (void)package_getversion(old); - } + (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; @@ -241,9 +266,11 @@ retvalue package_remove(struct package *old, struct logger *logger, struct track (void)package_getsource(old); } if (verbose > 0) - printf("removing '%s' from '%s'...\n", - old->name, old->target->identifier); - result = table_deleterecord(old->target->packages, old->name, false); + 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 @@ -270,20 +297,28 @@ retvalue package_remove(struct package *old, struct logger *logger, struct track } /* Remove a package from the given target. */ -retvalue target_removepackage(struct target *target, struct logger *logger, const char *name, struct trackingdata *trackingdata) { +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, NULL, &old); + r = package_get(target, name, version, &old); if (RET_WAS_ERROR(r)) { return r; } else if (r == RET_NOTHING) { - if (verbose >= 10) - fprintf(stderr, "Could not find '%s' in '%s'...\n", - name, target->identifier); + 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); @@ -301,7 +336,7 @@ retvalue package_remove_by_cursor(struct package_cursor *tc, struct logger *logg assert (target != NULL && target->packages != NULL); assert (target == old->target); - if (logger != NULL) { + if (logger != NULL || verbose > 0) { (void)package_getversion(old); } r = old->target->getfilekeys(old->control, &files); @@ -312,9 +347,9 @@ retvalue package_remove_by_cursor(struct package_cursor *tc, struct logger *logg (void)package_getsource(old); } if (verbose > 0) - printf("removing '%s' from '%s'...\n", - old->name, old->target->identifier); - result = cursor_delete(target->packages, tc->cursor, old->name, NULL); + 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 @@ -340,12 +375,94 @@ retvalue package_remove_by_cursor(struct package_cursor *tc, struct logger *logg 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, r; + 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) @@ -364,15 +481,23 @@ static retvalue addpackages(struct target *target, const char *packagename, cons /* Add package to the distribution's database */ - if (old != NULL) { - result = table_replacerecord(table, packagename, controlchunk); - - } else { - result = table_adduniqrecord(table, packagename, controlchunk); + 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); } - if (RET_WAS_ERROR(result)) - return result; + 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, @@ -406,67 +531,90 @@ static retvalue addpackages(struct target *target, const char *packagename, cons } 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; + struct strlist oldfilekeys, *ofk = NULL; char *newcontrol; - struct package old, *old_p; + 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, NULL, &old); - if (RET_WAS_ERROR(r)) + r = package_get(target, name, version, &old); + if (RET_WAS_ERROR(r)) { + package_done(&old); return r; - if (r == RET_NOTHING) { - old_p = NULL; - ofk = NULL; - setzero(struct package, &old); - } else { - old_p = &old; - r = package_getversion(&old); - if (RET_WAS_ERROR(r) && !IGNORING(brokenold, -"Error parsing old version!\n")) { + } 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)) { - int versioncmp; - - r = dpkgversions_cmp(version, old.version, - &versioncmp); - if (RET_WAS_ERROR(r)) { - if (!IGNORING(brokenversioncmp, -"Parse errors processing versions of %s.\n", name)) { - package_done(&old); - return r; - } - } else { - if (versioncmp <= 0) { - /* new Version is not newer than - * old version */ + 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, - old.version); + iterator.current.version); package_done(&old); return RET_NOTHING; - } else if (versioncmp < 0) { - fprintf(stderr, -"Warning: downgrading '%s' from '%s' to '%s' in '%s'!\n", name, - old.version, - version, - target->identifier); } else { fprintf(stderr, -"Warning: replacing '%s' version '%s' with equal version '%s' in '%s'!\n", name, - old.version, +"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)) { @@ -475,6 +623,10 @@ retvalue target_addpackage(struct target *target, struct logger *logger, const c 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) { @@ -487,12 +639,16 @@ retvalue target_addpackage(struct target *target, struct logger *logger, const c 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)) @@ -501,7 +657,7 @@ retvalue target_addpackage(struct target *target, struct logger *logger, const c r = addpackages(target, name, control, version, filekeys, - old_p, ofk, + &old, ofk, logger, trackingdata, architecture, causingrule, suitefrom); @@ -514,6 +670,32 @@ retvalue target_addpackage(struct target *target, struct logger *logger, const c } 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; } @@ -550,35 +732,30 @@ retvalue target_checkaddpackage(struct target *target, const char *name, const c package_done(&old); return r; } - if (versioncmp <= 0) { - r = RET_NOTHING; - if (versioncmp < 0) { - if (!permitnewerold) { - fprintf(stderr, + 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); - r = RET_ERROR; - } else if (verbose >= 0) { - printf( -"Warning: trying to put version '%s' of '%s' in '%s',\n" -"while there already is '%s' in there.\n", - version, name, - target->identifier, - old.version); - } + version, name, + target->identifier, + old.version); + package_done(&old); + return RET_ERROR; } else if (verbose > 2) { - printf( + 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); - + name, target->identifier, + old.version); } package_done(&old); - return r; + return RET_NOTHING; } r = target->getfilekeys(old.control, &oldfilekeys); ofk = &oldfilekeys; @@ -624,7 +801,7 @@ retvalue target_rereference(struct target *target) { if (verbose > 2) printf("Referencing %s...\n", target->identifier); - r = package_openiterator(target, READONLY, &iterator); + r = package_openiterator(target, READONLY, true, &iterator); assert (r != RET_NOTHING); if (RET_WAS_ERROR(r)) return r; @@ -748,7 +925,7 @@ retvalue target_reoverride(struct target *target, struct distribution *distribut target->identifier); } - r = package_openiterator(target, READWRITE, &iterator); + r = package_openiterator(target, READWRITE, true, &iterator); if (!RET_IS_OK(r)) return r; result = RET_NOTHING; @@ -816,7 +993,7 @@ retvalue target_redochecksums(struct target *target, struct distribution *distri target->identifier); } - r = package_openiterator(target, READWRITE, &iterator); + r = package_openiterator(target, READWRITE, true, &iterator); if (!RET_IS_OK(r)) return r; result = RET_NOTHING; @@ -906,7 +1083,9 @@ retvalue package_get(struct target *target, const char *name, const char *versio retvalue result, r; bool database_closed; - assert (version == NULL); /* not yet implemented */ + 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)); @@ -917,8 +1096,16 @@ retvalue package_get(struct target *target, const char *name, const char *versio if (RET_WAS_ERROR(r)) return r; } - result = table_getrecord(target->packages, name, - &pkg->pkgchunk, &pkg->controllen); + + 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; @@ -934,15 +1121,20 @@ retvalue package_get(struct target *target, const char *name, const char *versio return result; } -retvalue package_openiterator(struct target *t, bool readonly, /*@out@*/struct package_cursor *tc) { +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, &c); + r = table_newglobalcursor(t->packages, duplicate, &c); assert (r != RET_NOTHING); if (RET_WAS_ERROR(r)) { r2 = target_closepackagesdb(t); @@ -955,8 +1147,40 @@ retvalue package_openiterator(struct target *t, bool readonly, /*@out@*/struct p 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, @@ -973,8 +1197,12 @@ retvalue package_closeiterator(struct package_cursor *tc) { package_done(&tc->current); result = cursor_close(tc->target->packages, tc->cursor); - r = target_closepackagesdb(tc->target); - RET_UPDATE(result, r); + if (tc->close_database) { + r = target_closepackagesdb(tc->target); + RET_UPDATE(result, r); + } else { + tc->target = NULL; + } return result; } diff --git a/target.h b/target.h index 1ccb03a2..32607ee6 100644 --- a/target.h +++ b/target.h @@ -77,6 +77,7 @@ struct target { }; retvalue target_initialize_ubinary(/*@dependant@*/struct distribution *, component_t, architecture_t, /*@dependent@*/const struct exportmode *, bool /*readonly*/, bool /*noexport*/, /*@NULL@*/const char *fakecomponentprefix, /*@out@*/struct target **); +retvalue target_initialize_dbinary(/*@dependant@*/struct distribution *, component_t, architecture_t, /*@dependent@*/const struct exportmode *, bool /*readonly*/, bool /*noexport*/, /*@NULL@*/const char *fakecomponentprefix, /*@out@*/struct target **); retvalue target_initialize_binary(/*@dependant@*/struct distribution *, component_t, architecture_t, /*@dependent@*/const struct exportmode *, bool /*readonly*/, bool /*noexport*/, /*@NULL@*/const char *fakecomponentprefix, /*@out@*/struct target **); retvalue target_initialize_source(/*@dependant@*/struct distribution *, component_t, /*@dependent@*/const struct exportmode *, bool /*readonly*/, bool /*noexport*/, /*@NULL@*/const char *fakecomponentprefix, /*@out@*/struct target **); retvalue target_free(struct target *); @@ -93,7 +94,7 @@ struct logger; struct description; retvalue target_addpackage(struct target *, /*@null@*/struct logger *, const char *name, const char *version, const char *control, const struct strlist *filekeys, bool downgrade, /*@null@*/struct trackingdata *, architecture_t, /*@null@*/const char *causingrule, /*@null@*/const char *suitefrom); retvalue target_checkaddpackage(struct target *, const char *name, const char *version, bool tracking, bool permitnewerold); -retvalue target_removepackage(struct target *, /*@null@*/struct logger *, const char *name, struct trackingdata *); +retvalue target_removepackage(struct target *, /*@null@*/struct logger *, const char *name, const char *version, struct trackingdata *); /* like target_removepackage, but do not read control data yourself but use available */ retvalue target_rereference(struct target *); retvalue target_reoverride(struct target *, struct distribution *); @@ -108,4 +109,18 @@ static inline bool target_matches(const struct target *t, const struct atomlist return false; return true; } + +static inline char *package_primarykey(const char *packagename, const char *version) { + char *key; + + assert (packagename != NULL); + assert (version != NULL); + key = malloc(strlen(packagename) + 1 + strlen(version) + 1); + if (key != NULL) { + strcpy(key, packagename); + strcat(key, "|"); + strcat(key, version); + } + return key; +} #endif diff --git a/tests/Makefile.am b/tests/Makefile.am index 3f4a0db5..709e79f0 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -48,5 +48,15 @@ good.key \ revoked.key \ revoked.pkey \ withsubkeys.key \ -withsubkeys-works.key +withsubkeys-works.key \ +basic.sh \ +multiversion.sh \ +shunit2-helper-functions.sh MAINTAINERCLEANFILES = $(srcdir)/Makefile.in + +check: + ./basic.sh + ./multiversion.sh + +clean-local: + rm -rf testrepo testpkgs diff --git a/tests/basic.sh b/tests/basic.sh new file mode 100755 index 00000000..867c489c --- /dev/null +++ b/tests/basic.sh @@ -0,0 +1,425 @@ +#!/bin/sh +set -u + +# Copyright (C) 2017, Benjamin Drung +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +. "${0%/*}/shunit2-helper-functions.sh" + +setUp() { + create_repo +} + +tearDown() { + check_db +} + +test_empty() { + $REPREPRO -b $REPO export + call $REPREPRO -b $REPO list buster + assertEquals "" "$($REPREPRO -b $REPO list buster)" +} + +test_list() { + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=1.0 REVISION=-1 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_1.0-1_${ARCH}.deb + assertEquals "buster|main|$ARCH: hello 1.0-1" "$($REPREPRO -b $REPO list buster)" +} + +test_ls() { + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster EPOCH="1:" VERSION=2.5 REVISION=-3 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.5-3_${ARCH}.deb + assertEquals "hello | 1:2.5-3 | buster | $ARCH" "$($REPREPRO -b $REPO ls hello)" +} + +test_copy() { + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster EPOCH="1:" VERSION=2.5 REVISION=-3 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.5-3_${ARCH}.deb + assertEquals "hello | 1:2.5-3 | buster | $ARCH" "$($REPREPRO -b $REPO ls hello)" + add_distro bullseye + call $REPREPRO $VERBOSE_ARGS -b $REPO copy bullseye buster hello + assertEquals "bullseye|main|$ARCH: hello 1:2.5-3" "$($REPREPRO -b $REPO list bullseye)" +} + +test_copy_existing() { + add_distro bullseye + (cd $PKGS && PACKAGE=sl SECTION=main DISTRI=buster EPOCH="" VERSION=3.03 REVISION=-1 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/sl_3.03-1_${ARCH}.deb + assertEquals "sl | 3.03-1 | buster | $ARCH" "$($REPREPRO -b $REPO ls sl)" + call $REPREPRO $VERBOSE_ARGS -b $REPO copy bullseye buster sl + assertEquals "\ +sl | 3.03-1 | buster | $ARCH +sl | 3.03-1 | bullseye | $ARCH" "$($REPREPRO -b $REPO ls sl)" + call $REPREPRO $VERBOSE_ARGS -b $REPO copy bullseye buster sl + assertEquals "\ +sl | 3.03-1 | buster | $ARCH +sl | 3.03-1 | bullseye | $ARCH" "$($REPREPRO -b $REPO ls sl)" +} + +test_include_changes() { + (cd $PKGS && PACKAGE=sl SECTION=main DISTRI=buster EPOCH="" VERSION=3.03 REVISION=-1 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main include buster $PKGS/sl_3.03-1_${ARCH}.changes + assertEquals "\ +buster|main|$ARCH: sl 3.03-1 +buster|main|$ARCH: sl-addons 3.03-1 +buster|main|source: sl 3.03-1" "$($REPREPRO -b $REPO list buster)" +} + +test_include_old() { + # Test including an old package version. Expected output: + # Skipping inclusion of 'hello' '2.9-1' in 'buster|main|$ARCH', as it has already '2.9-2'. + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-1 ../genpackage.sh) + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-2 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-2_${ARCH}.deb + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-1_${ARCH}.deb + assertEquals "buster|main|$ARCH: hello 2.9-2" "$($REPREPRO -b $REPO list buster)" +} + +test_limit() { + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-1 ../genpackage.sh) + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-2 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-1_${ARCH}.deb + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-2_${ARCH}.deb + assertEquals "buster|main|$ARCH: hello 2.9-2" "$($REPREPRO -b $REPO list buster)" +} + +test_older_version() { + cat >> $REPO/conf/incoming <> $REPO/conf/distributions + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-1 ../genpackage.sh) + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-2 ../genpackage.sh) + mkdir -p "$REPO/incoming" + cp "$PKGS/hello_2.9-2_${ARCH}.changes" "$PKGS/hello-addons_2.9-2_all.deb" "$PKGS/hello_2.9-2_${ARCH}.deb" "$PKGS/hello_2.9-2.dsc" "$PKGS/hello_2.9.orig.tar.gz" "$PKGS/hello_2.9-2.debian.tar.xz" "$REPO/incoming" + call $REPREPRO $VERBOSE_ARGS -b $REPO processincoming buster-upload hello_2.9-2_${ARCH}.changes + assertEquals "hello | 2.9-2 | buster | $ARCH, source" "$($REPREPRO -b $REPO ls hello)" + cp "$PKGS/hello_2.9-1_${ARCH}.changes" "$PKGS/hello-addons_2.9-1_all.deb" "$PKGS/hello_2.9-1_${ARCH}.deb" "$PKGS/hello_2.9-1.dsc" "$PKGS/hello_2.9.orig.tar.gz" "$PKGS/hello_2.9-1.debian.tar.xz" "$REPO/incoming" + call $REPREPRO $VERBOSE_ARGS -b $REPO processincoming buster-upload hello_2.9-1_${ARCH}.changes + assertEquals "\ +hello | 2.9-2 | buster | $ARCH, source +hello | 2.9-1 | buster | $ARCH, source" "$($REPREPRO -b $REPO ls hello)" +} + +test_too_old_version() { + # Allow only one version per package in the archive + # Test if uploading an older version will not replace the newer version + # in the archive. + cat >> $REPO/conf/incoming <> $REPO/conf/distributions + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-1 ../genpackage.sh) + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-2 ../genpackage.sh) + mkdir -p "$REPO/incoming" + cp "$PKGS/hello_2.9-2_${ARCH}.changes" "$PKGS/hello-addons_2.9-2_all.deb" "$PKGS/hello_2.9-2_${ARCH}.deb" "$PKGS/hello_2.9-2.dsc" "$PKGS/hello_2.9.orig.tar.gz" "$PKGS/hello_2.9-2.debian.tar.xz" "$REPO/incoming" + call $REPREPRO $VERBOSE_ARGS -b $REPO processincoming buster-upload hello_2.9-2_${ARCH}.changes + assertEquals "hello | 2.9-2 | buster | $ARCH, source" "$($REPREPRO -b $REPO ls hello)" + cp "$PKGS/hello_2.9-1_${ARCH}.changes" "$PKGS/hello-addons_2.9-1_all.deb" "$PKGS/hello_2.9-1_${ARCH}.deb" "$PKGS/hello_2.9-1.dsc" "$PKGS/hello_2.9.orig.tar.gz" "$PKGS/hello_2.9-1.debian.tar.xz" "$REPO/incoming" + call $REPREPRO $VERBOSE_ARGS -b $REPO processincoming buster-upload hello_2.9-1_${ARCH}.changes + assertEquals "hello | 2.9-2 | buster | $ARCH, source" "$($REPREPRO -b $REPO ls hello)" +} + +test_remove() { + (cd $PKGS && PACKAGE=sl SECTION=main DISTRI=buster EPOCH="" VERSION=3.03 REVISION=-1 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main include buster $PKGS/sl_3.03-1_${ARCH}.changes + assertEquals "\ +buster|main|$ARCH: sl 3.03-1 +buster|main|$ARCH: sl-addons 3.03-1 +buster|main|source: sl 3.03-1" "$($REPREPRO -b $REPO list buster)" + call $REPREPRO $VERBOSE_ARGS -b $REPO remove buster sl + assertEquals "buster|main|$ARCH: sl-addons 3.03-1" "$($REPREPRO -b $REPO list buster)" +} + +test_listcodenames() { + assertEquals "buster" "$($REPREPRO -b $REPO _listcodenames)" + add_distro bullseye + assertEquals "\ +buster +bullseye" "$($REPREPRO -b $REPO _listcodenames)" +} + +test_copysrc() { + (cd $PKGS && PACKAGE=sl SECTION=main DISTRI=buster EPOCH="" VERSION=3.03 REVISION=-1 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main include buster $PKGS/sl_3.03-1_${ARCH}.changes + add_distro bullseye + call $REPREPRO $VERBOSE_ARGS -b $REPO copysrc bullseye buster sl + assertEquals "\ +bullseye|main|$ARCH: sl 3.03-1 +bullseye|main|$ARCH: sl-addons 3.03-1 +bullseye|main|source: sl 3.03-1" "$($REPREPRO -b $REPO list bullseye)" + assertEquals "\ +buster|main|$ARCH: sl 3.03-1 +buster|main|$ARCH: sl-addons 3.03-1 +buster|main|source: sl 3.03-1" "$($REPREPRO -b $REPO list buster)" +} + +test_copymatched() { + (cd $PKGS && PACKAGE=sl SECTION=main DISTRI=buster EPOCH="" VERSION=3.03 REVISION=-1 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main include buster $PKGS/sl_3.03-1_${ARCH}.changes + add_distro bullseye + call $REPREPRO $VERBOSE_ARGS -b $REPO copymatched bullseye buster "sl-a*on?" + assertEquals "bullseye|main|$ARCH: sl-addons 3.03-1" "$($REPREPRO -b $REPO list bullseye)" + assertEquals "\ +buster|main|$ARCH: sl 3.03-1 +buster|main|$ARCH: sl-addons 3.03-1 +buster|main|source: sl 3.03-1" "$($REPREPRO -b $REPO list buster)" +} + +test_move() { + (cd $PKGS && PACKAGE=sl SECTION=main DISTRI=buster EPOCH="" VERSION=3.03 REVISION=-1 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedsc buster $PKGS/sl_3.03-1.dsc + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/sl_3.03-1_$ARCH.deb $PKGS/sl-addons_3.03-1_all.deb + add_distro bullseye + call $REPREPRO $VERBOSE_ARGS -b $REPO move bullseye buster sl + assertEquals "\ +bullseye|main|$ARCH: sl 3.03-1 +bullseye|main|source: sl 3.03-1" "$($REPREPRO -b $REPO list bullseye)" + assertEquals "buster|main|$ARCH: sl-addons 3.03-1" "$($REPREPRO -b $REPO list buster)" + assertEquals "\ +Distribution: buster +Source: sl +Version: 3.03-1 +Files: + pool/main/s/sl/sl_3.03-1.dsc s 0 + pool/main/s/sl/sl_3.03.orig.tar.gz s 0 + pool/main/s/sl/sl_3.03-1.debian.tar.xz s 0 + pool/main/s/sl/sl_3.03-1_$ARCH.deb b 0 + pool/main/s/sl/sl-addons_3.03-1_all.deb a 1 + +Distribution: bullseye +Source: sl +Version: 3.03-1 +Files: + pool/main/s/sl/sl_3.03-1_$ARCH.deb b 1 + pool/main/s/sl/sl_3.03-1.dsc s 1 + pool/main/s/sl/sl_3.03.orig.tar.gz s 1 + pool/main/s/sl/sl_3.03-1.debian.tar.xz s 1" "$($REPREPRO -b $REPO dumptracks)" +} + +test_movesrc() { + (cd $PKGS && PACKAGE=sl SECTION=main DISTRI=buster EPOCH="" VERSION=3.03 REVISION=-1 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main include buster $PKGS/sl_3.03-1_${ARCH}.changes + add_distro bullseye + call $REPREPRO $VERBOSE_ARGS -b $REPO movesrc bullseye buster sl + assertEquals "\ +bullseye|main|$ARCH: sl 3.03-1 +bullseye|main|$ARCH: sl-addons 3.03-1 +bullseye|main|source: sl 3.03-1" "$($REPREPRO -b $REPO list bullseye)" + assertEquals "" "$($REPREPRO -b $REPO list buster)" +} + +test_movematched() { + (cd $PKGS && PACKAGE=sl SECTION=main DISTRI=buster EPOCH="" VERSION=3.03 REVISION=-1 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main include buster $PKGS/sl_3.03-1_${ARCH}.changes + add_distro bullseye + call $REPREPRO $VERBOSE_ARGS -b $REPO movematched bullseye buster "sl-a*on?" + assertEquals "bullseye|main|$ARCH: sl-addons 3.03-1" "$($REPREPRO -b $REPO list bullseye)" + assertEquals "\ +buster|main|$ARCH: sl 3.03-1 +buster|main|source: sl 3.03-1" "$($REPREPRO -b $REPO list buster)" +} + +test_archive() { + clear_distro + add_distro buster-archive + add_distro buster "Limit: 1\nArchive: buster-archive" + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-1 ../genpackage.sh) + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-2 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-1_${ARCH}.deb + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-2_${ARCH}.deb + assertEquals "\ +hello | 2.9-1 | buster-archive | $ARCH +hello | 2.9-2 | buster | $ARCH" "$($REPREPRO -b $REPO ls hello)" + assertEquals "\ +Distribution: buster-archive +Source: hello +Version: 2.9-1 +Files: + pool/main/h/hello/hello_2.9-1_$ARCH.deb b 1 + +Distribution: buster +Source: hello +Version: 2.9-2 +Files: + pool/main/h/hello/hello_2.9-2_$ARCH.deb b 1" "$($REPREPRO -b $REPO dumptracks)" +} + +test_archive_downgrade() { + clear_distro + add_distro buster-archive + add_distro buster "Limit: 1\nArchive: buster-archive" + add_distro buster-proposed + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-1 ../genpackage.sh) + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-2 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-2_${ARCH}.deb + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster-proposed $PKGS/hello_2.9-1_${ARCH}.deb + call $REPREPRO $VERBOSE_ARGS -b $REPO move buster buster-proposed hello=2.9-1 + assertEquals "\ +hello | 2.9-2 | buster-archive | $ARCH +hello | 2.9-1 | buster | $ARCH" "$($REPREPRO -b $REPO ls hello)" + assertEquals "\ +Distribution: buster-archive +Source: hello +Version: 2.9-2 +Files: + pool/main/h/hello/hello_2.9-2_$ARCH.deb b 1 + +Distribution: buster +Source: hello +Version: 2.9-1 +Files: + pool/main/h/hello/hello_2.9-1_$ARCH.deb b 1" "$($REPREPRO -b $REPO dumptracks)" +} + +test_archive_move() { + clear_distro + add_distro buster-archive "Limit: -1" + add_distro buster "Limit: 1\nArchive: buster-archive" + add_distro buster-proposed "Limit: -1" + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-1 ../genpackage.sh) + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-2 ../genpackage.sh) + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-3 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster-proposed $PKGS/hello_2.9-1_${ARCH}.deb + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster-proposed $PKGS/hello_2.9-2_${ARCH}.deb + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster-proposed $PKGS/hello_2.9-3_${ARCH}.deb + assertEquals "\ +hello | 2.9-3 | buster-proposed | $ARCH +hello | 2.9-2 | buster-proposed | $ARCH +hello | 2.9-1 | buster-proposed | $ARCH" "$($REPREPRO -b $REPO ls hello)" + assertEquals "\ +Distribution: buster-proposed +Source: hello +Version: 2.9-1 +Files: + pool/main/h/hello/hello_2.9-1_$ARCH.deb b 1 + +Distribution: buster-proposed +Source: hello +Version: 2.9-2 +Files: + pool/main/h/hello/hello_2.9-2_$ARCH.deb b 1 + +Distribution: buster-proposed +Source: hello +Version: 2.9-3 +Files: + pool/main/h/hello/hello_2.9-3_$ARCH.deb b 1" "$($REPREPRO -b $REPO dumptracks)" + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main move buster buster-proposed hello=2.9-1 + assertEquals "\ +hello | 2.9-1 | buster | $ARCH +hello | 2.9-3 | buster-proposed | $ARCH +hello | 2.9-2 | buster-proposed | $ARCH" "$($REPREPRO -b $REPO ls hello)" + assertEquals "\ +Distribution: buster +Source: hello +Version: 2.9-1 +Files: + pool/main/h/hello/hello_2.9-1_$ARCH.deb b 1 + +Distribution: buster-proposed +Source: hello +Version: 2.9-2 +Files: + pool/main/h/hello/hello_2.9-2_$ARCH.deb b 1 + +Distribution: buster-proposed +Source: hello +Version: 2.9-3 +Files: + pool/main/h/hello/hello_2.9-3_$ARCH.deb b 1" "$($REPREPRO -b $REPO dumptracks)" + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main move buster buster-proposed hello=2.9-2 + assertEquals "\ +hello | 2.9-1 | buster-archive | $ARCH +hello | 2.9-2 | buster | $ARCH +hello | 2.9-3 | buster-proposed | $ARCH" "$($REPREPRO -b $REPO ls hello)" + assertEquals "\ +Distribution: buster-archive +Source: hello +Version: 2.9-1 +Files: + pool/main/h/hello/hello_2.9-1_$ARCH.deb b 1 + +Distribution: buster +Source: hello +Version: 2.9-2 +Files: + pool/main/h/hello/hello_2.9-2_$ARCH.deb b 1 + +Distribution: buster-proposed +Source: hello +Version: 2.9-3 +Files: + pool/main/h/hello/hello_2.9-3_$ARCH.deb b 1" "$($REPREPRO -b $REPO dumptracks)" + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main move buster buster-proposed hello + assertEquals "\ +hello | 2.9-2 | buster-archive | $ARCH +hello | 2.9-1 | buster-archive | $ARCH +hello | 2.9-3 | buster | $ARCH" "$($REPREPRO -b $REPO ls hello)" + assertEquals "\ +Distribution: buster-archive +Source: hello +Version: 2.9-1 +Files: + pool/main/h/hello/hello_2.9-1_$ARCH.deb b 1 + +Distribution: buster-archive +Source: hello +Version: 2.9-2 +Files: + pool/main/h/hello/hello_2.9-2_$ARCH.deb b 1 + +Distribution: buster +Source: hello +Version: 2.9-3 +Files: + pool/main/h/hello/hello_2.9-3_$ARCH.deb b 1" "$($REPREPRO -b $REPO dumptracks)" +} + +test_archive_move_back() { + clear_distro + add_distro buster-archive "Limit: -1" + add_distro buster "Limit: 1\nArchive: buster-archive" + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-1 ../genpackage.sh) + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-2 ../genpackage.sh) + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-1_${ARCH}.deb + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster-archive $PKGS/hello_2.9-2_${ARCH}.deb + assertEquals "\ +hello | 2.9-2 | buster-archive | $ARCH +hello | 2.9-1 | buster | $ARCH" "$($REPREPRO -b $REPO ls hello)" + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main move buster buster-archive hello=2.9-2 + assertEquals "\ +hello | 2.9-1 | buster-archive | $ARCH +hello | 2.9-2 | buster | $ARCH" "$($REPREPRO -b $REPO ls hello)" +} + +test_ddeb() { + clear_distro + add_distro buster "DDebComponents: main non-free" + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster VERSION=2.9 REVISION=-1 DDEB=1 ../genpackage.sh) + #mv $PKGS/hello_2.9-1_${ARCH}.deb $PKGS/hello_2.9-1_${ARCH}.ddeb + #sed -i "s/hello_2.9-1_${ARCH}.deb/hello_2.9-1_${ARCH}.ddeb/g" $PKGS/hello_2.9-1_${ARCH}.changes + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main include buster $PKGS/hello_2.9-1_${ARCH}.changes + assertEquals "hello | 2.9-1 | buster | $ARCH, source" "$($REPREPRO -b $REPO ls hello)" +} + +. shunit2 diff --git a/tests/genpackage.sh b/tests/genpackage.sh index ac8ffeca..f228918b 100755 --- a/tests/genpackage.sh +++ b/tests/genpackage.sh @@ -5,21 +5,23 @@ set -e #VERSION=0.9-A:Z+a:z #REVISION=-0+aA.9zZ if [ "x$OUTPUT" == "x" ] ; then - OUTPUT=test.changes + OUTPUT=${PACKAGE}_${VERSION}${REVISION}_${ARCH:-$(dpkg-architecture -qDEB_HOST_ARCH)}.changes fi DIR="$PACKAGE-$VERSION" +ARCH="${ARCH:-$(dpkg-architecture -qDEB_HOST_ARCH)}" +rm -rf "$DIR" mkdir "$DIR" mkdir "$DIR"/debian cat >"$DIR"/debian/control < Standards-Version: 0.0 Package: $PACKAGE -Architecture: abacus +Architecture: ${ARCH} Description: bla blub @@ -28,6 +30,17 @@ Architecture: all Description: bla blub END + +if test -n "${DDEB-}" ; then +cat >>"$DIR"/debian/control < Mon, 01 Jan 1980 01:02:02 +0000 END -dpkg-source -b "$DIR" +mkdir -p "$DIR/debian/source" +if test -z "$REVISION"; then + echo "3.0 (native)" > "$DIR/debian/source/format" +else + echo "3.0 (quilt)" > "$DIR/debian/source/format" + orig_tarball="${PACKAGE}_${VERSION}.orig.tar.gz" + if test ! -f "$orig_tarball"; then + tar czvf "$orig_tarball" --files-from /dev/null + fi +fi + +dpkg-source -b "$DIR" > /dev/null mkdir -p "$DIR"/debian/tmp/DEBIAN touch "$DIR"/debian/tmp/x mkdir "$DIR"/debian/tmp/a @@ -51,14 +75,28 @@ mkdir "$DIR"/debian/tmp/dir/subdir touch "$DIR"/debian/tmp/dir/subdir/file cd "$DIR" for pkg in `grep '^Package: ' debian/control | sed -e 's/^Package: //'` ; do + case "$pkg" in + (*-udeb) + deb="${pkg}_${VERSION}${REVISION}_${ARCH}.udeb" + ;; + (*-dbgsym) + deb="${pkg}_${VERSION}${REVISION}_${ARCH}.ddeb" + ;; + (*-addons) + deb="${pkg}_${FAKEVER:-${VERSION}${REVISION}}_all.deb" + ;; + (*) + deb="${pkg}_${VERSION}${REVISION}_${ARCH}.deb" + ;; + esac if [ "x$pkg" != "x${pkg%-addons}" -a -n "$FAKEVER" ] ; then dpkg-gencontrol -p$pkg -v"$FAKEVER" else dpkg-gencontrol -p$pkg fi - dpkg --build debian/tmp .. + dpkg --build debian/tmp ../$deb > /dev/null done -dpkg-genchanges "$@" > "$OUTPUT".pre +dpkg-genchanges -q "$@" > "$OUTPUT".pre # simulate dpkg-genchanges behaviour currently in sid so the testsuite runs for backports, too awk 'BEGIN{inheader=0} /^Files:/ || (inheader && /^ /) {inheader = 1; next} {inheader = 0 ; print}' "$OUTPUT".pre | sed -e 's/ \+$//' >../"$OUTPUT" echo "Files:" >> ../"$OUTPUT" diff --git a/tests/multiversion.sh b/tests/multiversion.sh new file mode 100755 index 00000000..a7b83ee1 --- /dev/null +++ b/tests/multiversion.sh @@ -0,0 +1,353 @@ +#!/bin/sh +set -u + +# Copyright (C) 2017, Benjamin Drung +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +. "${0%/*}/shunit2-helper-functions.sh" + +oneTimeSetUp() { + for revision in 1 2 2+deb8u1 10; do + mkdir -p "$PKGS" + (cd $PKGS && PACKAGE=hello SECTION=main DISTRI=buster EPOCH="" VERSION=2.9 REVISION=-$revision ../genpackage.sh) + done +} + +setUp() { + create_repo + echo "Limit: -1" >> $REPO/conf/distributions +} + +tearDown() { + check_db +} + +four_hellos() { + for revision in 1 2 2+deb8u1 10; do + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-${revision}_${ARCH}.deb + done +} + +test_ls() { + (cd $PKGS && PACKAGE=kvm SECTION=main DISTRI=buster VERSION=1.2.1 REVISION=-8 ../genpackage.sh) + (cd $PKGS && PACKAGE=kvm SECTION=main DISTRI=buster VERSION=1.2.1 REVISION=-9 ../genpackage.sh) + (cd $PKGS && PACKAGE=appdirs SECTION=main DISTRI=buster VERSION=1.3.0 REVISION=-1 ../genpackage.sh) + for package in hello_2.9-1 kvm_1.2.1-8 kvm_1.2.1-9 appdirs_1.3.0-1; do + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/${package}_${ARCH}.deb + done + assertEquals "\ +kvm | 1.2.1-9 | buster | $ARCH +kvm | 1.2.1-8 | buster | $ARCH" "$($REPREPRO -b $REPO ls kvm)" + assertEquals "\ +buster|main|$ARCH: kvm 1.2.1-9 +buster|main|$ARCH: kvm 1.2.1-8" "$($REPREPRO -b $REPO list buster kvm)" +} + +test_sorting() { + four_hellos + assertEquals "\ +buster|main|$ARCH: hello 2.9-10 +buster|main|$ARCH: hello 2.9-2+deb8u1 +buster|main|$ARCH: hello 2.9-2 +buster|main|$ARCH: hello 2.9-1" "$($REPREPRO -b $REPO list buster)" + assertEquals "\ +hello | 2.9-10 | buster | $ARCH +hello | 2.9-2+deb8u1 | buster | $ARCH +hello | 2.9-2 | buster | $ARCH +hello | 2.9-1 | buster | $ARCH" "$($REPREPRO -b $REPO ls hello)" +} + +test_include_twice() { + for revision in 1 2; do + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-${revision}_${ARCH}.deb + done + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-1_${ARCH}.deb + assertEquals "\ +hello | 2.9-2 | buster | $ARCH +hello | 2.9-1 | buster | $ARCH" "$($REPREPRO -b $REPO ls hello)" +} + +test_copy_latest() { + four_hellos + add_distro bullseye "Limit: -1" + call $REPREPRO $VERBOSE_ARGS -b $REPO copy bullseye buster hello hello + assertEquals "bullseye|main|$ARCH: hello 2.9-10" "$($REPREPRO -b $REPO list bullseye)" +} + +test_copy_specific() { + four_hellos + add_distro bullseye "Limit: -1" + call $REPREPRO $VERBOSE_ARGS -b $REPO copy bullseye buster hello=2.9-10 hello=2.9-1 hello=2.9-10 + assertEquals "\ +bullseye|main|$ARCH: hello 2.9-10 +bullseye|main|$ARCH: hello 2.9-1" "$($REPREPRO -b $REPO list bullseye)" +} + +test_remove_latest() { + four_hellos + add_distro bullseye "Limit: -1" + call $REPREPRO $VERBOSE_ARGS -b $REPO copy bullseye buster hello=2.9-10 hello=2.9-1 hello=2.9-10 + call $REPREPRO $VERBOSE_ARGS -b $REPO remove bullseye hello + assertEquals "\ +hello | 2.9-10 | buster | $ARCH +hello | 2.9-2+deb8u1 | buster | $ARCH +hello | 2.9-2 | buster | $ARCH +hello | 2.9-1 | buster | $ARCH +hello | 2.9-1 | bullseye | $ARCH" "$($REPREPRO -b $REPO ls hello)" +} + +test_remove_specific() { + four_hellos + call $REPREPRO $VERBOSE_ARGS -b $REPO remove buster hello=2.9-2+deb8u1 hellox hello=2.9-2+deb8u1 + assertEquals "\ +buster|main|$ARCH: hello 2.9-10 +buster|main|$ARCH: hello 2.9-2 +buster|main|$ARCH: hello 2.9-1" "$($REPREPRO -b $REPO list buster)" +} + +test_removefilter() { + (cd $PKGS && PACKAGE=kvm SECTION=main DISTRI=buster VERSION=1.2.1 REVISION=-8 ../genpackage.sh) + (cd $PKGS && PACKAGE=kvm SECTION=main DISTRI=buster VERSION=1.2.1 REVISION=-9 ../genpackage.sh) + for package in hello_2.9-1 kvm_1.2.1-8 kvm_1.2.1-9; do + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/${package}_${ARCH}.deb + done + assertEquals "\ +buster|main|$ARCH: hello 2.9-1 +buster|main|$ARCH: kvm 1.2.1-9 +buster|main|$ARCH: kvm 1.2.1-8" "$($REPREPRO -b $REPO list buster)" + + add_distro bullseye "Limit: -1" + call $REPREPRO $VERBOSE_ARGS -b $REPO copy bullseye buster kvm + assertEquals "bullseye|main|$ARCH: kvm 1.2.1-9" "$($REPREPRO -b $REPO list bullseye)" + + call $REPREPRO $VERBOSE_ARGS -b $REPO removefilter buster "Package (= kvm)" + assertEquals "buster|main|$ARCH: hello 2.9-1" "$($REPREPRO -b $REPO list buster)" + assertTrue "kvm_1.2.1-8_$ARCH.deb is still in the pool!" "test ! -e $REPO/pool/main/k/kvm/kvm_1.2.1-8_$ARCH.deb" + assertTrue "kvm_1.2.1-9_$ARCH.deb is missing from the pool!" "test -e $REPO/pool/main/k/kvm/kvm_1.2.1-9_$ARCH.deb" + + call $REPREPRO $VERBOSE_ARGS -b $REPO removefilter bullseye "Package (= kvm)" + assertEquals "buster|main|$ARCH: hello 2.9-1" "$($REPREPRO -b $REPO list buster)" + assertTrue "kvm_1.2.1-9_$ARCH.deb is still in the pool!" "test ! -e $REPO/pool/main/k/kvm/kvm_1.2.1-9_$ARCH.deb" +} + +test_readd_distribution() { + # Test case for https://github.com/profitbricks/reprepro/issues/1 + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-1_${ARCH}.deb + + # Add distribution + cp $REPO/conf/distributions $REPO/conf/distributions.backup + add_distro bullseye "Limit: -1" + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb bullseye $PKGS/hello_2.9-2_${ARCH}.deb + + # Remove distribution + mv $REPO/conf/distributions.backup $REPO/conf/distributions + call $REPREPRO $VERBOSE_ARGS -b $REPO --delete clearvanished + + # Re-add distribution again + echo "I: Re-adding bullseye..." + add_distro bullseye "Limit: -1" + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb bullseye $PKGS/hello_2.9-10_${ARCH}.deb + assertEquals "bullseye|main|$ARCH: hello 2.9-10" "$($REPREPRO -b $REPO list bullseye)" +} + +test_limit3() { + sed -i 's/^Limit: .*$/Limit: 3/' $REPO/conf/distributions + for revision in 1 2 2+deb8u1; do + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-${revision}_${ARCH}.deb + done + assertEquals "\ +buster|main|${ARCH}: hello 2.9-2+deb8u1 +buster|main|${ARCH}: hello 2.9-2 +buster|main|${ARCH}: hello 2.9-1" "$($REPREPRO -b $REPO list buster)" + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-10_${ARCH}.deb + assertEquals "\ +buster|main|${ARCH}: hello 2.9-10 +buster|main|${ARCH}: hello 2.9-2+deb8u1 +buster|main|${ARCH}: hello 2.9-2" "$($REPREPRO -b $REPO list buster)" +} + +test_reduce_limit() { + for revision in 1 2 2+deb8u1; do + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-${revision}_${ARCH}.deb + done + assertEquals "\ +buster|main|${ARCH}: hello 2.9-2+deb8u1 +buster|main|${ARCH}: hello 2.9-2 +buster|main|${ARCH}: hello 2.9-1" "$($REPREPRO -b $REPO list buster)" + sed -i 's/^Limit: .*$/Limit: 1/' $REPO/conf/distributions + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-10_${ARCH}.deb + assertEquals "buster|main|${ARCH}: hello 2.9-10" "$($REPREPRO -b $REPO list buster)" + assertEquals "\ +Distribution: buster +Source: hello +Version: 2.9-10 +Files: + pool/main/h/hello/hello_2.9-10_$ARCH.deb b 1" "$($REPREPRO -b $REPO dumptracks)" +} + +test_reduce_limit_archive() { + clear_distro + add_distro buster-archive "Limit: 7" + add_distro buster "Limit: -1\nArchive: buster-archive" + for revision in 1 2; do + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-${revision}_${ARCH}.deb + done + assertEquals "\ +buster|main|${ARCH}: hello 2.9-2 +buster|main|${ARCH}: hello 2.9-1" "$($REPREPRO -b $REPO list buster)" + sed -i 's/^Limit: -1$/Limit: 1/' $REPO/conf/distributions + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-10_${ARCH}.deb + assertEquals "\ +hello | 2.9-2 | buster-archive | $ARCH +hello | 2.9-1 | buster-archive | $ARCH +hello | 2.9-10 | buster | $ARCH" "$($REPREPRO -b $REPO ls hello)" + assertEquals "\ +Distribution: buster-archive +Source: hello +Version: 2.9-1 +Files: + pool/main/h/hello/hello_2.9-1_$ARCH.deb b 1 + +Distribution: buster-archive +Source: hello +Version: 2.9-2 +Files: + pool/main/h/hello/hello_2.9-2_$ARCH.deb b 1 + +Distribution: buster +Source: hello +Version: 2.9-10 +Files: + pool/main/h/hello/hello_2.9-10_$ARCH.deb b 1" "$($REPREPRO -b $REPO dumptracks)" +} + +test_limit_old() { + for revision in 1 2 10; do + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-${revision}_${ARCH}.deb + done + assertEquals "\ +buster|main|${ARCH}: hello 2.9-10 +buster|main|${ARCH}: hello 2.9-2 +buster|main|${ARCH}: hello 2.9-1" "$($REPREPRO -b $REPO list buster)" + sed -i 's/^Limit: .*$/Limit: 2/' $REPO/conf/distributions + call $REPREPRO $VERBOSE_ARGS -b $REPO -C main includedeb buster $PKGS/hello_2.9-2+deb8u1_${ARCH}.deb + assertEquals "\ +buster|main|${ARCH}: hello 2.9-10 +buster|main|${ARCH}: hello 2.9-2+deb8u1" "$($REPREPRO -b $REPO list buster)" +} + +test_update_packages() { + # Test case for https://github.com/profitbricks/reprepro/issues/6 + local upstream_repo + upstream_repo="${0%/*}/upstreamrepo" + + four_hellos + rm -rf "$upstream_repo" + mv "$REPO" "$upstream_repo" + + mkdir -p "$REPO/conf" + cat > "$REPO/conf/distributions" < "$REPO/conf/updates" <> 2.9-2)' + assertEquals "\ +buster|main|$ARCH: hello 2.9-2 +buster|main|$ARCH: hello 2.9-1" "$($REPREPRO -b $REPO list buster)" + assertEquals "\ +bullseye|main|$ARCH: hello 2.9-10 +bullseye|main|$ARCH: hello 2.9-2+deb8u1" "$($REPREPRO -b $REPO list bullseye)" +} + +. shunit2 diff --git a/tests/old-database/conf/distributions b/tests/old-database/conf/distributions new file mode 100644 index 00000000..80ba8587 --- /dev/null +++ b/tests/old-database/conf/distributions @@ -0,0 +1,5 @@ +Codename: bullseye +Architectures: amd64 i386 source +Components: main non-free +Log: testrepo.log +Tracking: all diff --git a/tests/old-database/db/checksums.db b/tests/old-database/db/checksums.db new file mode 100644 index 00000000..842fdd1a Binary files /dev/null and b/tests/old-database/db/checksums.db differ diff --git a/tests/old-database/db/contents.cache.db b/tests/old-database/db/contents.cache.db new file mode 100644 index 00000000..646a2fd1 Binary files /dev/null and b/tests/old-database/db/contents.cache.db differ diff --git a/tests/old-database/db/packages.db b/tests/old-database/db/packages.db new file mode 100644 index 00000000..6a7d4985 Binary files /dev/null and b/tests/old-database/db/packages.db differ diff --git a/tests/old-database/db/references.db b/tests/old-database/db/references.db new file mode 100644 index 00000000..7b8e4f8b Binary files /dev/null and b/tests/old-database/db/references.db differ diff --git a/tests/old-database/db/release.caches.db b/tests/old-database/db/release.caches.db new file mode 100644 index 00000000..7958f476 Binary files /dev/null and b/tests/old-database/db/release.caches.db differ diff --git a/tests/old-database/db/version b/tests/old-database/db/version new file mode 100644 index 00000000..50d2a22f --- /dev/null +++ b/tests/old-database/db/version @@ -0,0 +1,4 @@ +5.2.0 +3.3.0 +bdb5.3.28 +bdb5.3.0 diff --git a/tests/shunit2-helper-functions.sh b/tests/shunit2-helper-functions.sh new file mode 100644 index 00000000..8f664b8b --- /dev/null +++ b/tests/shunit2-helper-functions.sh @@ -0,0 +1,68 @@ +# Copyright (C) 2017, Benjamin Drung +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +REPO="${0%/*}/testrepo" +PKGS="${0%/*}/testpkgs" +ARCH=${ARCH:-$(dpkg-architecture -qDEB_HOST_ARCH)} +REPREPRO=$(realpath -m "${0%/*}/.." --relative-base=.)/reprepro +VERBOSE_ARGS="${VERBOSE_ARGS-}" + +call() { + command="$@" + echo "I: Calling $@" + "$@" || fail "Command '$command' failed with exit code $?." +} + +check_db() { + db_verify $REPO/db/packages.db || fail "BerkeleyDB 'packages.db' is broken." + db_verify -o $REPO/db/packagenames.db || fail "BerkeleyDB 'packagenames.db' is broken." +} + +add_distro() { + local name="$1" + if test -e $REPO/conf/distributions; then + echo >> $REPO/conf/distributions + fi + cat >> $REPO/conf/distributions <> $REPO/conf/distributions + fi +} + +clear_distro() { + rm -f $REPO/conf/distributions +} + +create_repo() { + rm -rf $REPO + mkdir -p $REPO/conf + add_distro buster + mkdir -p $PKGS + $REPREPRO -b $REPO export +} + +# See https://github.com/wting/shunit2/issues/23 +if test -n "${TEST_CASES-}"; then + suite() { + for testcase in "${TEST_CASES}" ; do + suite_addTest $testcase + done + } +fi diff --git a/tests/trackingcorruption.test b/tests/trackingcorruption.test index de62d62e..6110e26c 100644 --- a/tests/trackingcorruption.test +++ b/tests/trackingcorruption.test @@ -43,7 +43,7 @@ EOF testrun - --keepunreferenced remove breakme aa aa-addons 3<realchecksums); free(f->section); free(f->priority); - if (f->type == ft_DEB || f->type == ft_UDEB) { + if (f->type == ft_DEB || f->type == ft_DDEB || f->type == ft_UDEB) { binaryfile_free(f->deb); } else if (f->type == ft_DSC) { dscfile_free(f->dsc); @@ -947,7 +948,7 @@ static retvalue processfiles(const char *changesfilename, struct changes *change if (RET_IS_OK(r)) { if (file->type == ft_DSC) r = parse_dsc(file, changes); - else if (file->type == ft_DEB || file->type == ft_UDEB) + else if (file->type == ft_DEB || file->type == ft_DDEB || file->type == ft_UDEB) r = parse_deb(file, changes); if (RET_WAS_ERROR(r)) { free(dir); @@ -958,7 +959,7 @@ static retvalue processfiles(const char *changesfilename, struct changes *change if (r == RET_NOTHING) { /* apply heuristics when not readable */ if (file->type == ft_DSC) { - } else if (file->type == ft_DEB || file->type == ft_UDEB) { + } else if (file->type == ft_DEB || file->type == ft_DDEB || file->type == ft_UDEB) { struct binary *b; size_t len; len = 0; @@ -1819,7 +1820,7 @@ static retvalue verify(const char *changesfilename, struct changes *changes) { const struct binary *b; const struct binaryfile *deb; - if (file->type != ft_DEB && file->type != ft_UDEB) + if (file->type != ft_DEB && file->type != ft_DDEB && file->type != ft_UDEB) continue; if (file->fullfilename == NULL) { fprintf(stderr, @@ -2504,6 +2505,10 @@ static retvalue adddeb(struct changes *c, const char *debfilename, const struct strcmp(fullfilename+strlen(fullfilename)-5, ".udeb") == 0) { packagetype = "udeb"; type = ft_UDEB; + } else if (strlen(fullfilename) > 5 && + strcmp(fullfilename+strlen(fullfilename)-5, ".ddeb") == 0) { + packagetype = "ddeb"; + type = ft_DDEB; } else { packagetype = "deb"; type = ft_DEB; @@ -2767,6 +2772,7 @@ static retvalue addfiles(const char *changesfilename, struct changes *c, int arg size_t l = strlen(filename); if ((l > 4 && strcmp(filename+l-4, ".deb") == 0) || + (l > 5 && strcmp(filename+l-5, ".ddeb") == 0) || (l > 5 && strcmp(filename+l-5, ".udeb") == 0)) r = adddeb(c, filename, searchpath); else if ((l > 4 && strcmp(filename+l-4, ".dsc") == 0)) diff --git a/tracking.c b/tracking.c index dc33292a..6fe55548 100644 --- a/tracking.c +++ b/tracking.c @@ -46,7 +46,7 @@ struct s_tracking { struct trackingoptions options; }; -retvalue tracking_done(trackingdb db) { +retvalue tracking_done(trackingdb db, struct distribution *distribution) { retvalue r; if (db == NULL) @@ -55,10 +55,17 @@ retvalue tracking_done(trackingdb db) { 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, const struct distribution *distribution, bool readonly) { +retvalue tracking_initialize(/*@out@*/trackingdb *db, struct distribution *distribution, bool readonly) { struct s_tracking *t; retvalue r; @@ -80,6 +87,7 @@ retvalue tracking_initialize(/*@out@*/trackingdb *db, const struct distribution return r; } *db = t; + distribution->trackingdb = t; return RET_OK; } @@ -522,7 +530,7 @@ static retvalue tracking_recreatereferences(trackingdb t) { const char *key, *value, *data; size_t datalen; - r = table_newglobalcursor(t->table, &cursor); + r = table_newglobalcursor(t->table, true, &cursor); if (!RET_IS_OK(r)) return r; @@ -568,7 +576,7 @@ retvalue tracking_rereference(struct distribution *distribution) { return result; r = tracking_recreatereferences(tracks); RET_UPDATE(result, r); - r = tracking_done(tracks); + r = tracking_done(tracks, distribution); RET_ENDUPDATE(result, r); return result; } @@ -639,7 +647,7 @@ retvalue tracking_printall(trackingdb t) { const char *key, *value, *data; size_t datalen; - r = table_newglobalcursor(t->table, &cursor); + r = table_newglobalcursor(t->table, true, &cursor); if (!RET_IS_OK(r)) return r; @@ -671,9 +679,9 @@ retvalue tracking_foreach_ro(struct distribution *d, tracking_foreach_ro_action if (!RET_IS_OK(r)) return r; - r = table_newglobalcursor(t->table, &cursor); + r = table_newglobalcursor(t->table, true, &cursor); if (!RET_IS_OK(r)) { - (void)tracking_done(t); + (void)tracking_done(t, d); return r; } @@ -691,7 +699,7 @@ retvalue tracking_foreach_ro(struct distribution *d, tracking_foreach_ro_action } r = cursor_close(t->table, cursor); RET_ENDUPDATE(result, r); - r = tracking_done(t); + r = tracking_done(t, d); RET_ENDUPDATE(result, r); return result; } @@ -870,7 +878,7 @@ retvalue trackingdata_insert(struct trackingdata *data, enum filetype filetype, } if (r == RET_NOTHING) { fprintf(stderr, -"Could not found tracking data for %s_%s in %s to remove old files from it.\n", +"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; @@ -889,6 +897,9 @@ retvalue trackingdata_remove(struct trackingdata *data, const char* oldsource, c 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 && @@ -904,7 +915,7 @@ retvalue trackingdata_remove(struct trackingdata *data, const char* oldsource, c } if (result == RET_NOTHING) { fprintf(stderr, -"Could not found tracking data for %s_%s in %s to remove old files from it.\n", +"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; } @@ -1069,7 +1080,7 @@ retvalue tracking_tidyall(trackingdb t) { const char *key, *value, *data; size_t datalen; - r = table_newglobalcursor(t->table, &cursor); + r = table_newglobalcursor(t->table, true, &cursor); if (!RET_IS_OK(r)) return r; @@ -1102,7 +1113,7 @@ retvalue tracking_reset(trackingdb t) { size_t datalen, newdatalen; int i; - r = table_newglobalcursor(t->table, &cursor); + r = table_newglobalcursor(t->table, true, &cursor); if (!RET_IS_OK(r)) return r; @@ -1139,7 +1150,7 @@ static retvalue tracking_foreachversion(trackingdb t, struct distribution *distr const char *value, *data; size_t datalen; - r = table_newduplicatecursor(t->table, sourcename, &cursor, + r = table_newduplicatepairedcursor(t->table, sourcename, &cursor, &value, &data, &datalen); if (!RET_IS_OK(r)) return r; @@ -1183,13 +1194,17 @@ static retvalue targetremovesourcepackage(trackingdb t, struct trackedpackage *p 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, *basefilename, *filekey = pkg->filekeys.values[i]; - char *packagename; + const char *s, *s2, *basefilename, *filekey = pkg->filekeys.values[i]; + char *packagename, *packageversion; struct package package; struct strlist filekeys; bool savedstaletracking; @@ -1244,9 +1259,14 @@ static retvalue targetremovesourcepackage(trackingdb t, struct trackedpackage *p packagename = strndup(basefilename, s - basefilename); if (FAILEDTOALLOC(packagename)) return RET_ERROR_OOM; - r = package_get(target, packagename, NULL, &package); + 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) { @@ -1254,16 +1274,18 @@ static retvalue targetremovesourcepackage(trackingdb t, struct trackedpackage *p && verbose >= -1) { fprintf(stderr, "Warning: tracking data might be inconsistent:\n" -"cannot find '%s' in '%s', but '%s' should be there.\n", - packagename, target->identifier, +"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); @@ -1330,6 +1352,10 @@ static retvalue removesourcepackage(trackingdb t, struct trackedpackage *pkg, st 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) { @@ -1370,6 +1396,10 @@ retvalue tracking_removepackages(trackingdb t, struct distribution *distribution 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); @@ -1432,7 +1462,7 @@ retvalue tracking_retrack(struct distribution *d, bool needsretrack) { /* now remove everything no longer needed */ r = tracking_tidyall(tracks); } - rr = tracking_done(tracks); + rr = tracking_done(tracks, d); RET_ENDUPDATE(r, rr); return r; } diff --git a/tracking.h b/tracking.h index 082ab620..e44383ef 100644 --- a/tracking.h +++ b/tracking.h @@ -17,8 +17,8 @@ retvalue tracking_parse(struct distribution *, struct configiterator *); /* high-level retrack of the whole distribution */ retvalue tracking_retrack(struct distribution *, bool /*evenifnotstale*/); -retvalue tracking_initialize(/*@out@*/trackingdb *, const struct distribution *, bool readonly); -retvalue tracking_done(trackingdb); +retvalue tracking_initialize(/*@out@*/trackingdb *, struct distribution *, bool readonly); +retvalue tracking_done(trackingdb, struct distribution *distribution); retvalue tracking_listdistributions(/*@out@*/struct strlist *); retvalue tracking_drop(const char *); diff --git a/updates.c b/updates.c index f6d7ada3..8d81ddfb 100644 --- a/updates.c +++ b/updates.c @@ -155,6 +155,9 @@ struct update_pattern { // (empty means all) struct strlist udebcomponents_from; struct strlist udebcomponents_into; + // There's no ddeb support here yet, since we don't know what the + // Debian archive layout is going to look like. + // NULL means no condition /*@null@*/term *includecondition; struct filterlist filterlist; diff --git a/upgradelist.c b/upgradelist.c index f3f0d7a3..96344235 100644 --- a/upgradelist.c +++ b/upgradelist.c @@ -99,6 +99,10 @@ static retvalue save_package_version(struct upgradelist *upgrade, struct package if (RET_WAS_ERROR(r)) return r; + if (verbose >= 15) + fprintf(stderr, "trace: save_package_version(upgrade.target={identifier: %s}, pkg={name: %s, version: %s, pkgname: %s}) called.\n", + upgrade->target == NULL ? NULL : upgrade->target->identifier, pkg->name, pkg->version, pkg->pkgname); + package = zNEW(struct package_data); if (FAILEDTOALLOC(package)) return RET_ERROR_OOM; @@ -143,6 +147,10 @@ retvalue upgradelist_initialize(struct upgradelist **ul, struct target *t) { retvalue r, r2; struct package_cursor iterator; + if (verbose >= 15) + fprintf(stderr, "trace: upgradelist_initialize(target={identifier: %s}) called.\n", + t == NULL ? NULL : t->identifier); + upgrade = zNEW(struct upgradelist); if (FAILEDTOALLOC(upgrade)) return RET_ERROR_OOM; @@ -151,7 +159,7 @@ retvalue upgradelist_initialize(struct upgradelist **ul, struct target *t) { /* Beginn with the packages currently in the archive */ - r = package_openiterator(t, READONLY, &iterator); + r = package_openiterator(t, READONLY, false, &iterator); if (RET_WAS_ERROR(r)) { upgradelist_free(upgrade); return r; @@ -519,7 +527,7 @@ retvalue upgradelist_pull(struct upgradelist *upgrade, struct target *source, up struct package_cursor iterator; upgrade->last = NULL; - r = package_openiterator(source, READONLY, &iterator); + r = package_openiterator(source, READONLY, true, &iterator); if (RET_WAS_ERROR(r)) return r; result = RET_NOTHING; @@ -609,7 +617,7 @@ retvalue upgradelist_predelete(struct upgradelist *upgrade, struct logger *logge r = RET_ERROR_INTERRUPTED; else r = target_removepackage(upgrade->target, - logger, pkg->name, NULL); + logger, pkg->name, NULL, NULL); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) break; @@ -713,7 +721,7 @@ retvalue upgradelist_install(struct upgradelist *upgrade, struct logger *logger, r = RET_ERROR_INTERRUPTED; else r = target_removepackage(upgrade->target, - logger, pkg->name, NULL); + logger, pkg->name, NULL, NULL); RET_UPDATE(result, r); if (RET_WAS_ERROR(r)) break;