diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:12:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:12:14 +0000 |
commit | 4b8a0f3f3dcf60dac2ce308ea08d413a535af29f (patch) | |
tree | 0f09c0ad2a4d0f535d89040a63dc3a866a6606e6 /binaries.c | |
parent | Initial commit. (diff) | |
download | reprepro-4b8a0f3f3dcf60dac2ce308ea08d413a535af29f.tar.xz reprepro-4b8a0f3f3dcf60dac2ce308ea08d413a535af29f.zip |
Adding upstream version 5.4.4.upstream/5.4.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | binaries.c | 784 |
1 files changed, 784 insertions, 0 deletions
diff --git a/binaries.c b/binaries.c new file mode 100644 index 0000000..9465dfc --- /dev/null +++ b/binaries.c @@ -0,0 +1,784 @@ +/* This file is part of "reprepro" + * Copyright (C) 2003,2004,2005,2006,2007,2009,2010 Bernhard R. Link + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301 USA + */ +#include <config.h> + +#include <errno.h> +#include <assert.h> +#include <string.h> +#include <strings.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include "error.h" +#include "mprintf.h" +#include "strlist.h" +#include "names.h" +#include "chunks.h" +#include "sources.h" +#include "binaries.h" +#include "names.h" +#include "dpkgversions.h" +#include "log.h" +#include "override.h" +#include "tracking.h" +#include "debfile.h" +#include "package.h" + +static const char * const deb_checksum_headers[cs_COUNT] = { + "MD5sum", "SHA1", "SHA256", "SHA512", "Size"}; + +static char *calc_binary_basename(const char *name, const char *version, architecture_t arch, packagetype_t packagetype) { + const char *v; + assert (name != NULL); + assert (version != NULL); + assert (atom_defined(arch)); + assert (atom_defined(packagetype)); + v = strchr(version, ':'); + if (v != NULL) + v++; + else + v = version; + return mprintf("%s_%s_%s.%s", name, v, atoms_architectures[arch], + atoms_packagetypes[packagetype]); +} + + +/* get checksums out of a "Packages"-chunk. */ +static retvalue binaries_parse_checksums(const char *chunk, /*@out@*/struct checksums **checksums_p) { + retvalue result, r; + char *checksums[cs_COUNT]; + enum checksumtype type; + bool gothash = false; + + result = RET_NOTHING; + + for (type = 0 ; type < cs_COUNT ; type++) { + checksums[type] = NULL; + r = chunk_getvalue(chunk, deb_checksum_headers[type], + &checksums[type]); + if (type != cs_length && RET_IS_OK(r)) + gothash = true; + RET_UPDATE(result, r); + } + if (!gothash) { + fprintf(stderr, +"No checksums found in binary control chunk:\n '%s'\n", + chunk); + RET_UPDATE(result, RET_ERROR_MISSING); + } + if (checksums[cs_length] == NULL) { + fprintf(stderr, +"Missing 'Size' line in binary control chunk:\n '%s'\n", + chunk); + RET_UPDATE(result, RET_ERROR_MISSING); + } + if (RET_WAS_ERROR(result)) { + for (type = 0 ; type < cs_COUNT ; type++) + free(checksums[type]); + return result; + } + return checksums_init(checksums_p, checksums); +} + +retvalue binaries_getarchitecture(const char *chunk, architecture_t *architecture_p) { + char *parch; + retvalue r; + + r = chunk_getvalue(chunk, "Architecture", &parch); + if (r == RET_NOTHING) { + fprintf(stderr, +"Internal Error: Missing Architecture: header in '%s'!\n", + chunk); + return RET_ERROR; + } + if (RET_WAS_ERROR(r)) + return r; + *architecture_p = architecture_find(parch); + free(parch); + + if (!atom_defined(*architecture_p)) { + fprintf(stderr, +"Internal Error: Unexpected Architecture: header in '%s'!\n", + chunk); + return RET_ERROR; + } + return RET_OK; +} + +/* get somefields out of a "Packages.gz"-chunk. + * returns RET_OK on success, RET_NOTHING if incomplete, error otherwise */ +static retvalue binaries_calc_basename(struct package *package, /*@out@*/char **basename_p) { + retvalue r; + char *mybasename; + + assert (package->name != NULL); + assert (package->version != NULL); + + r = properpackagename(package->name); + if (!RET_WAS_ERROR(r)) + r = properversion(package->version); + if (RET_WAS_ERROR(r)) { + return r; + } + mybasename = calc_binary_basename(package->name, + package->version, + package->architecture, + package->target->packagetype); + if (FAILEDTOALLOC(mybasename)) { + return RET_ERROR_OOM; + } + + *basename_p = mybasename; + return RET_OK; +} + +/* get files out of a "Packages.gz"-chunk. */ +retvalue binaries_getfilekeys(const char *chunk, struct strlist *files) { + retvalue r; + char *filename; + + /* Read the filename given there */ + r = chunk_getvalue(chunk, "Filename", &filename); + if (!RET_IS_OK(r)) { + if (r == RET_NOTHING) { + fprintf(stderr, +"Data does not look like binary control: '%s'\n", + chunk); + r = RET_ERROR; + } + return r; + } + r = strlist_init_singleton(filename, files); + return r; +} + +static retvalue calcfilekeys(component_t component, const char *sourcename, const char *basefilename, struct strlist *filekeys) { + char *filekey; + retvalue r; + + r = propersourcename(sourcename); + if (RET_WAS_ERROR(r)) { + return r; + } + filekey = calc_filekey(component, sourcename, basefilename); + if (FAILEDTOALLOC(filekey)) + return RET_ERROR_OOM; + r = strlist_init_singleton(filekey, filekeys); + return r; +} + +static inline retvalue calcnewcontrol(const char *chunk, const char *packagename, const char *sourcename, const char *basefilename, component_t component, struct strlist *filekeys, char **newchunk) { + retvalue r; + char *n; + + + n = chunk_normalize(chunk, "Package", packagename); + if (FAILEDTOALLOC(n)) + return RET_ERROR_OOM; + r = calcfilekeys(component, sourcename, basefilename, filekeys); + if (RET_WAS_ERROR(r)) { + free(n); + return r; + } + + assert (filekeys->count == 1); + *newchunk = chunk_replacefield(n, "Filename", + filekeys->values[0], false); + free(n); + if (FAILEDTOALLOC(*newchunk)) { + strlist_done(filekeys); + return RET_ERROR_OOM; + } + return RET_OK; +} + +retvalue binaries_getversion(const char *control, char **version) { + retvalue r; + + r = chunk_getvalue(control, "Version", version); + if (RET_WAS_ERROR(r)) + return r; + if (r == RET_NOTHING) { + fprintf(stderr, +"Missing 'Version' field in chunk:'%s'\n", + control); + return RET_ERROR; + } + return r; +} + +retvalue binaries_getinstalldata(const struct target *t, struct package *package, char **control, struct strlist *filekeys, struct checksumsarray *origfiles) { + char *basefilename; + struct checksumsarray origfilekeys; + retvalue r; + const char *chunk = package->control; + + assert (t->packagetype == package->target->packagetype); + + r = package_getsource(package); + if (RET_WAS_ERROR(r)) + return r; + + r = binaries_calc_basename(package, &basefilename); + if (RET_WAS_ERROR(r)) + return RET_ERROR; + r = binaries_getchecksums(chunk, &origfilekeys); + if (RET_WAS_ERROR(r)) { + free(basefilename); + return r; + } + + r = calcnewcontrol(chunk, package->name, package->source, + basefilename, + t->component, filekeys, control); + if (RET_WAS_ERROR(r)) { + checksumsarray_done(&origfilekeys); + } else { + assert (r != RET_NOTHING); + checksumsarray_move(origfiles, &origfilekeys); + } + free(basefilename); + return r; +} + +retvalue binaries_getchecksums(const char *chunk, struct checksumsarray *filekeys) { + retvalue r; + struct checksumsarray a; + + r = binaries_getfilekeys(chunk, &a.names); + if (RET_WAS_ERROR(r)) + return r; + assert (a.names.count == 1); + a.checksums = NEW(struct checksums *); + if (FAILEDTOALLOC(a.checksums)) { + strlist_done(&a.names); + return RET_ERROR_OOM; + } + r = binaries_parse_checksums(chunk, a.checksums); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) { + free(a.checksums); + strlist_done(&a.names); + return r; + } + checksumsarray_move(filekeys, &a); + return RET_OK; +} + +retvalue binaries_doreoverride(const struct target *target, const char *packagename, const char *controlchunk, /*@out@*/char **newcontrolchunk) { + const struct overridedata *o; + struct fieldtoadd *fields; + char *newchunk; + retvalue r; + + if (interrupted()) + return RET_ERROR_INTERRUPTED; + + o = override_search(target->distribution->overrides.deb, packagename); + if (o == NULL) + return RET_NOTHING; + + r = override_allreplacefields(o, &fields); + if (!RET_IS_OK(r)) + return r; + newchunk = chunk_replacefields(controlchunk, fields, "Filename", false); + addfield_free(fields); + if (FAILEDTOALLOC(newchunk)) + return RET_ERROR_OOM; + *newcontrolchunk = newchunk; + return RET_OK; +} + +retvalue ubinaries_doreoverride(const struct target *target, const char *packagename, const char *controlchunk, /*@out@*/char **newcontrolchunk) { + const struct overridedata *o; + struct fieldtoadd *fields; + char *newchunk; + retvalue r; + + if (interrupted()) + return RET_ERROR_INTERRUPTED; + + o = override_search(target->distribution->overrides.udeb, packagename); + if (o == NULL) + return RET_NOTHING; + + r = override_allreplacefields(o, &fields); + if (!RET_IS_OK(r)) + return r; + newchunk = chunk_replacefields(controlchunk, fields, "Filename", true); + addfield_free(fields); + if (FAILEDTOALLOC(newchunk)) + return RET_ERROR_OOM; + *newcontrolchunk = newchunk; + return RET_OK; +} + +retvalue binaries_retrack(const char *packagename, const char *chunk, trackingdb tracks) { + retvalue r; + const char *sourcename; + char *fsourcename, *sourceversion, *arch, *filekey; + enum filetype filetype; + struct trackedpackage *pkg; + + //TODO: eliminate duplicate code! + assert(packagename!=NULL); + + if (interrupted()) + return RET_ERROR_INTERRUPTED; + + /* is there a sourcename */ + r = chunk_getnameandversion(chunk, "Source", + &fsourcename, &sourceversion); + if (RET_WAS_ERROR(r)) + return r; + if (r == RET_NOTHING) { + sourceversion = NULL; + sourcename = packagename; + fsourcename = NULL; + } else { + sourcename = fsourcename; + } + if (sourceversion == NULL) { + // Think about binNMUs, can something be done here? + r = chunk_getvalue(chunk, "Version", &sourceversion); + if (RET_WAS_ERROR(r)) { + free(fsourcename); + return r; + } + if (r == RET_NOTHING) { + free(fsourcename); + fprintf(stderr, +"Missing 'Version' field in chunk:'%s'\n", + chunk); + return RET_ERROR; + } + } + + r = chunk_getvalue(chunk, "Architecture", &arch); + if (r == RET_NOTHING) { + fprintf(stderr, "No Architecture field in chunk:'%s'\n", + chunk); + r = RET_ERROR; + } + if (RET_WAS_ERROR(r)) { + free(sourceversion); + free(fsourcename); + return r; + } + if (strcmp(arch, "all") == 0) { + filetype = ft_ALL_BINARY; + } else { + filetype = ft_ARCH_BINARY; + } + free(arch); + + r = chunk_getvalue(chunk, "Filename", &filekey); + if (!RET_IS_OK(r)) { + if (r == RET_NOTHING) { + fprintf(stderr, "No Filename field in chunk: '%s'\n", + chunk); + r = RET_ERROR; + } + free(sourceversion); + free(fsourcename); + return r; + } + r = tracking_getornew(tracks, sourcename, sourceversion, &pkg); + free(fsourcename); + free(sourceversion); + if (RET_WAS_ERROR(r)) { + free(filekey); + return r; + } + assert (r != RET_NOTHING); + r = trackedpackage_addfilekey(tracks, pkg, filetype, filekey, true); + if (RET_WAS_ERROR(r)) { + trackedpackage_free(pkg); + return r; + } + return tracking_save(tracks, pkg); +} + +retvalue binaries_getsourceandversion(const char *chunk, const char *packagename, char **source, char **version) { + retvalue r; + char *sourcename, *sourceversion; + + //TODO: eliminate duplicate code! + assert(packagename!=NULL); + + /* is there a sourcename */ + r = chunk_getnameandversion(chunk, "Source", + &sourcename, &sourceversion); + if (RET_WAS_ERROR(r)) + return r; + if (r == RET_NOTHING) { + sourceversion = NULL; + sourcename = strdup(packagename); + if (FAILEDTOALLOC(sourcename)) + return RET_ERROR_OOM; + } + if (sourceversion == NULL) { + r = chunk_getvalue(chunk, "Version", &sourceversion); + if (RET_WAS_ERROR(r)) { + free(sourcename); + return r; + } + if (r == RET_NOTHING) { + free(sourcename); + fprintf(stderr, "No Version field in chunk:'%s'\n", + chunk); + return RET_ERROR; + } + } + *source = sourcename; + *version = sourceversion; + return RET_OK; +} + +static inline retvalue getvalue(const char *filename, const char *chunk, const char *field, char **value) { + retvalue r; + + r = chunk_getvalue(chunk, field, value); + if (r == RET_NOTHING) { + fprintf(stderr, "No %s field in %s's control file!\n", + field, filename); + r = RET_ERROR; + } + return r; +} + +static inline retvalue checkvalue(const char *filename, const char *chunk, const char *field) { + retvalue r; + + r = chunk_checkfield(chunk, field); + if (r == RET_NOTHING) { + fprintf(stderr, "No %s field in %s's control file!\n", + field, filename); + r = RET_ERROR; + } + return r; +} + +static inline retvalue getvalue_n(const char *chunk, const char *field, char **value) { + retvalue r; + + r = chunk_getvalue(chunk, field, value); + if (r == RET_NOTHING) { + *value = NULL; + } + return r; +} + +void binaries_debdone(struct deb_headers *pkg) { + free(pkg->name);free(pkg->version); + free(pkg->source);free(pkg->sourceversion); + free(pkg->control); + free(pkg->section); + free(pkg->priority); +} + +retvalue binaries_readdeb(struct deb_headers *deb, const char *filename) { + retvalue r; + char *architecture; + + r = extractcontrol(&deb->control, filename); + if (RET_WAS_ERROR(r)) + return r; + /* first look for fields that should be there */ + + r = chunk_getname(deb->control, "Package", &deb->name, false); + if (r == RET_NOTHING) { + fprintf(stderr, "Missing 'Package' field in %s!\n", filename); + r = RET_ERROR; + } + if (RET_WAS_ERROR(r)) + return r; + r = checkvalue(filename, deb->control, "Maintainer"); + if (RET_WAS_ERROR(r)) + return r; + r = checkvalue(filename, deb->control, "Description"); + if (RET_WAS_ERROR(r)) + return r; + r = getvalue(filename, deb->control, "Version", &deb->version); + if (RET_WAS_ERROR(r)) + return r; + r = getvalue(filename, deb->control, "Architecture", &architecture); + if (RET_WAS_ERROR(r)) + return r; + r = properfilenamepart(architecture); + if (RET_WAS_ERROR(r)) { + free(architecture); + return r; + } + r = architecture_intern(architecture, &deb->architecture); + free(architecture); + if (RET_WAS_ERROR(r)) + return r; + /* can be there, otherwise we also know what it is */ + r = chunk_getnameandversion(deb->control, "Source", + &deb->source, &deb->sourceversion); + if (r == RET_NOTHING) { + deb->source = strdup(deb->name); + if (FAILEDTOALLOC(deb->source)) + r = RET_ERROR_OOM; + } + if (RET_WAS_ERROR(r)) + return r; + if (deb->sourceversion == NULL) { + deb->sourceversion = strdup(deb->version); + if (FAILEDTOALLOC(deb->sourceversion)) + return RET_ERROR_OOM; + } + + /* normally there, but optional: */ + + r = getvalue_n(deb->control, PRIORITY_FIELDNAME, &deb->priority); + if (RET_WAS_ERROR(r)) + return r; + r = getvalue_n(deb->control, SECTION_FIELDNAME, &deb->section); + if (RET_WAS_ERROR(r)) + return r; + return RET_OK; +} + +/* do overwrites, add Filename and Checksums to the control-item */ +retvalue binaries_complete(const struct deb_headers *pkg, const char *filekey, const struct checksums *checksums, const struct overridedata *override, const char *section, const char *priority, char **newcontrol) { + struct fieldtoadd *replace; + char *normalchunk, *newchunk; + enum checksumtype type; + + assert (section != NULL && priority != NULL); + assert (filekey != NULL && checksums != NULL); + + replace = NULL; + for (type = 0 ; type < cs_COUNT ; type++) { + const char *start; + size_t len; + if (checksums_getpart(checksums, type, &start, &len)) { + replace = addfield_newn(deb_checksum_headers[type], + start, len, replace); + if (FAILEDTOALLOC(replace)) + return RET_ERROR_OOM; + } + } + replace = addfield_new("Filename", filekey, replace); + if (FAILEDTOALLOC(replace)) + return RET_ERROR_OOM; + replace = addfield_new(SECTION_FIELDNAME, section, replace); + if (FAILEDTOALLOC(replace)) + return RET_ERROR_OOM; + replace = addfield_new(PRIORITY_FIELDNAME, priority, replace); + if (FAILEDTOALLOC(replace)) + return RET_ERROR_OOM; + + replace = override_addreplacefields(override, replace); + if (FAILEDTOALLOC(replace)) + return RET_ERROR_OOM; + + normalchunk = chunk_normalize(pkg->control, + "Package", pkg->name); + if (FAILEDTOALLOC(normalchunk)) + newchunk = NULL; + else + newchunk = chunk_replacefields(normalchunk, replace, + "Description", true); + free(normalchunk); + addfield_free(replace); + if (FAILEDTOALLOC(newchunk)) { + return RET_ERROR_OOM; + } + + *newcontrol = newchunk; + + return RET_OK; +} + +/* update Checksums */ +retvalue binaries_complete_checksums(const char *chunk, const struct strlist *filekeys, struct checksums **c, char **out) { + struct fieldtoadd *replace; + char *newchunk; + enum checksumtype type; + const struct checksums *checksums; + + assert (filekeys->count == 1); + checksums = c[0]; + + replace = NULL; + for (type = 0 ; type < cs_COUNT ; type++) { + const char *start; + size_t len; + if (checksums_getpart(checksums, type, &start, &len)) { + replace = addfield_newn(deb_checksum_headers[type], + start, len, replace); + if (FAILEDTOALLOC(replace)) + return RET_ERROR_OOM; + } + } + newchunk = chunk_replacefields(chunk, replace, + "Description", true); + addfield_free(replace); + if (FAILEDTOALLOC(newchunk)) + return RET_ERROR_OOM; + *out = newchunk; + return RET_OK; +} + +retvalue binaries_adddeb(const struct deb_headers *deb, const struct atomlist *forcearchitectures, packagetype_t packagetype, struct distribution *distribution, struct trackingdata *trackingdata, component_t component, const struct strlist *filekeys, const char *control) { + retvalue r, result; + int i; + + assert (logger_isprepared(distribution->logger)); + + /* finally put it into one or more architectures of the distribution */ + + result = RET_NOTHING; + + if (deb->architecture != architecture_all) { + struct target *t = distribution_getpart(distribution, + component, deb->architecture, + packagetype); + r = target_initpackagesdb(t, READWRITE); + if (!RET_WAS_ERROR(r)) { + retvalue r2; + if (interrupted()) + r = RET_ERROR_INTERRUPTED; + else + r = target_addpackage(t, distribution->logger, + deb->name, deb->version, + control, + filekeys, + false, + trackingdata, + deb->architecture, + NULL, NULL); + r2 = target_closepackagesdb(t); + RET_ENDUPDATE(r, r2); + } + RET_UPDATE(result, r); + RET_UPDATE(distribution->status, result); + return result; + } + /* It's an architecture all package */ + + /* if -A includes all, it is added everywhere, as if no -A was + * given. (as it behaved this way when there was only one -A possible, + * and to allow incoming to force it into architecture 'all' ) + * */ + if (forcearchitectures != NULL && + atomlist_in(forcearchitectures, architecture_all)) + forcearchitectures = NULL; + + for (i = 0 ; i < distribution->architectures.count ; i++) { + /*@dependent@*/struct target *t; + architecture_t a = distribution->architectures.atoms[i]; + + if (a == architecture_source) + continue; + if (forcearchitectures != NULL && + !atomlist_in(forcearchitectures, a)) + continue; + t = distribution_getpart(distribution, + component, a, packagetype); + r = target_initpackagesdb(t, READWRITE); + if (!RET_WAS_ERROR(r)) { + retvalue r2; + if (interrupted()) + r = RET_ERROR_INTERRUPTED; + else + r = target_addpackage(t, distribution->logger, + deb->name, deb->version, + control, + filekeys, + false, + trackingdata, + deb->architecture, + NULL, NULL); + r2 = target_closepackagesdb(t); + RET_ENDUPDATE(r, r2); + } + RET_UPDATE(result, r); + } + RET_UPDATE(distribution->status, result); + return result; +} + +static inline retvalue checkadddeb(struct distribution *distribution, component_t component, architecture_t architecture, packagetype_t packagetype, bool tracking, const struct deb_headers *deb, bool permitnewerold) { + retvalue r; + struct target *t; + + t = distribution_getpart(distribution, + component, architecture, packagetype); + assert (t != NULL); + r = target_initpackagesdb(t, READONLY); + if (!RET_WAS_ERROR(r)) { + retvalue r2; + if (interrupted()) + r = RET_ERROR_INTERRUPTED; + else + r = target_checkaddpackage(t, + deb->name, deb->version, + tracking, + permitnewerold); + r2 = target_closepackagesdb(t); + RET_ENDUPDATE(r, r2); + } + return r; +} + +retvalue binaries_checkadddeb(const struct deb_headers *deb, architecture_t forcearchitecture, packagetype_t packagetype, struct distribution *distribution, bool tracking, component_t component, bool permitnewerold) { + retvalue r, result; + int i; + + /* finally put it into one or more architectures of the distribution */ + result = RET_NOTHING; + + if (deb->architecture != architecture_all) { + r = checkadddeb(distribution, + component, deb->architecture, packagetype, + tracking, deb, permitnewerold); + RET_UPDATE(result, r); + } else if (atom_defined(forcearchitecture) + && forcearchitecture != architecture_all) { + r = checkadddeb(distribution, + component, forcearchitecture, packagetype, + tracking, deb, permitnewerold); + RET_UPDATE(result, r); + } else for (i = 0 ; i < distribution->architectures.count ; i++) { + architecture_t a = distribution->architectures.atoms[i]; + if (a == architecture_source) + continue; + r = checkadddeb(distribution, + component, a, packagetype, + tracking, deb, permitnewerold); + RET_UPDATE(result, r); + } + return result; +} + +retvalue binaries_calcfilekeys(component_t component, const struct deb_headers *deb, packagetype_t packagetype, struct strlist *filekeys) { + retvalue r; + char *basefilename; + + basefilename = calc_binary_basename(deb->name, deb->version, + deb->architecture, packagetype); + if (FAILEDTOALLOC(basefilename)) + return RET_ERROR_OOM; + + r = calcfilekeys(component, deb->source, basefilename, filekeys); + free(basefilename); + return r; +} |