Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
reprepro (5.4.2-1deepin6) unstable; urgency=medium

* feat: verify exported indices consistency after update/pull

-- lichenggang <lichenggang@deepin.org> Tue, 14 Apr 2026 10:00:00 +0800

reprepro (5.4.2-1deepin5) unstable; urgency=medium

* fix: import source file checksum error
Expand Down
305 changes: 305 additions & 0 deletions debian/patches/0005-feat-verify-exported-indices-consistency.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
diff --git a/distribution.c b/distribution.c
index 8b24365..e450dd4 100644
--- a/distribution.c
+++ b/distribution.c
@@ -40,6 +40,8 @@
#include "configparser.h"
#include "byhandhook.h"
#include "package.h"
+#include "indexfile.h"
+#include "filecntl.h"
#include "distribution.h"

static retvalue distribution_free(struct distribution *distribution) {
@@ -1074,6 +1076,241 @@ retvalue distribution_exportlist(enum exportwhen when, struct distribution *dist
return result;
}

+static int compare_string_pointers(const void *a, const void *b) {
+ const char * const *sa = a;
+ const char * const *sb = b;
+ return strcmp(*sa, *sb);
+}
+
+retvalue distribution_verify_exported_indices(struct distribution *distributions) {
+ struct distribution *d;
+ retvalue result = RET_OK;
+
+ if (verbose >= 5)
+ fprintf(stderr, "trace: verifying exported indices consistency...\n");
+
+ for (d = distributions ; d != NULL ; d = d->next) {
+ struct target *t;
+
+ if (d->omitted || !d->selected ||
+ d->exportoptions[deo_noexport])
+ continue;
+
+ for (t = d->targets ; t != NULL ; t = t->next) {
+ struct package_cursor cursor;
+ struct indexfile *idx = NULL;
+ struct package pkg;
+ struct strlist db_packages, idx_packages;
+ char *basepath = NULL, *filepath = NULL, *indexpath = NULL;
+ retvalue r;
+ enum compression comp = c_none;
+ int i, j;
+ bool mismatch = false;
+
+ if (t->noexport)
+ continue;
+
+ if (verbose >= 10)
+ fprintf(stderr,
+"trace: verifying exported index for target '%s'...\n",
+ t->identifier);
+
+ strlist_init(&db_packages);
+ strlist_init(&idx_packages);
+
+ r = package_openiterator(t, READONLY, true, &cursor);
+ if (RET_WAS_ERROR(r)) {
+ RET_UPDATE(result, r);
+ strlist_done(&db_packages);
+ strlist_done(&idx_packages);
+ continue;
+ }
+ while (package_next(&cursor)) {
+ char *entry;
+ r = package_getversion(&cursor.current);
+ if (RET_WAS_ERROR(r)) {
+ RET_UPDATE(result, r);
+ mismatch = true;
+ break;
+ }
+ entry = mprintf("%s|%s",
+ cursor.current.name,
+ cursor.current.version);
+ if (FAILEDTOALLOC(entry)) {
+ RET_UPDATE(result, RET_ERROR_OOM);
+ mismatch = true;
+ break;
+ }
+ r = strlist_add(&db_packages, entry);
+ if (RET_WAS_ERROR(r)) {
+ free(entry);
+ RET_UPDATE(result, r);
+ mismatch = true;
+ break;
+ }
+ }
+ r = package_closeiterator(&cursor);
+ if (RET_WAS_ERROR(r)) {
+ RET_UPDATE(result, r);
+ strlist_done(&db_packages);
+ strlist_done(&idx_packages);
+ continue;
+ }
+ if (mismatch) {
+ strlist_done(&db_packages);
+ strlist_done(&idx_packages);
+ continue;
+ }
+
+ if (verbose >= 10)
+ fprintf(stderr,
+"trace: target '%s' has %d packages in database.\n",
+ t->identifier, db_packages.count);
+
+ basepath = calc_dirconcat(global.distdir, d->codename);
+ filepath = calc_dirconcat(basepath, t->relativedirectory);
+ free(basepath);
+ basepath = calc_dirconcat(filepath, t->exportmode->filename);
+ free(filepath);
+
+ if (isregularfile(basepath)) {
+ indexpath = basepath;
+ basepath = NULL;
+ comp = c_none;
+ } else {
+ indexpath = mprintf("%s.gz", basepath);
+ if (!isregularfile(indexpath)) {
+ if (db_packages.count > 0) {
+ fprintf(stderr,
+"Error: exported index file for %s not found (expected %s or %s.gz)!\n",
+ t->identifier, basepath, basepath);
+ RET_UPDATE(result, RET_ERROR_MISSING);
+ }
+ free(indexpath);
+ free(basepath);
+ strlist_done(&db_packages);
+ strlist_done(&idx_packages);
+ continue;
+ }
+ comp = c_gzip;
+ free(basepath);
+ basepath = NULL;
+ }
+
+ r = indexfile_open(&idx, indexpath, comp);
+ if (RET_WAS_ERROR(r)) {
+ fprintf(stderr,
+"Error: cannot open exported index file %s for %s!\n",
+ indexpath, t->identifier);
+ RET_UPDATE(result, r);
+ goto target_done;
+ }
+
+ memset(&pkg, 0, sizeof(pkg));
+ while (indexfile_getnext(idx, &pkg, t, true)) {
+ char *entry;
+ entry = mprintf("%s|%s", pkg.name, pkg.version);
+ if (FAILEDTOALLOC(entry)) {
+ RET_UPDATE(result, RET_ERROR_OOM);
+ mismatch = true;
+ break;
+ }
+ r = strlist_add(&idx_packages, entry);
+ if (RET_WAS_ERROR(r)) {
+ free(entry);
+ RET_UPDATE(result, r);
+ mismatch = true;
+ break;
+ }
+ package_done(&pkg);
+ }
+ package_done(&pkg);
+
+ r = indexfile_close(idx);
+ idx = NULL;
+ if (RET_WAS_ERROR(r)) {
+ fprintf(stderr,
+"Error: cannot close exported index file %s for %s!\n",
+ indexpath, t->identifier);
+ RET_UPDATE(result, r);
+ goto target_done;
+ }
+ if (mismatch)
+ goto target_done;
+
+ if (verbose >= 10)
+ fprintf(stderr,
+"trace: target '%s' has %d packages in exported index.\n",
+ t->identifier, idx_packages.count);
+
+ if (db_packages.count > 0)
+ qsort(db_packages.values, db_packages.count,
+ sizeof(char *),
+ compare_string_pointers);
+ if (idx_packages.count > 0)
+ qsort(idx_packages.values, idx_packages.count,
+ sizeof(char *),
+ compare_string_pointers);
+
+ i = 0; j = 0;
+ while (i < db_packages.count && j < idx_packages.count) {
+ int cmp = strcmp(db_packages.values[i],
+ idx_packages.values[j]);
+ if (cmp == 0) {
+ i++; j++;
+ } else if (cmp < 0) {
+ fprintf(stderr,
+"Error: package missing in exported index %s: %s\n",
+ t->identifier, db_packages.values[i]);
+ mismatch = true;
+ i++;
+ } else {
+ fprintf(stderr,
+"Error: unexpected package in exported index %s: %s\n",
+ t->identifier, idx_packages.values[j]);
+ mismatch = true;
+ j++;
+ }
+ }
+ while (i < db_packages.count) {
+ fprintf(stderr,
+"Error: package missing in exported index %s: %s\n",
+ t->identifier, db_packages.values[i]);
+ mismatch = true;
+ i++;
+ }
+ while (j < idx_packages.count) {
+ fprintf(stderr,
+"Error: unexpected package in exported index %s: %s\n",
+ t->identifier, idx_packages.values[j]);
+ mismatch = true;
+ j++;
+ }
+
+ if (mismatch) {
+ fprintf(stderr,
+"Fatal error: exported index %s does not match database!\n",
+ t->identifier);
+ RET_UPDATE(result, RET_ERROR);
+ } else if (verbose >= 10) {
+ fprintf(stderr,
+"trace: target '%s' exported index matches database.\n",
+ t->identifier);
+ }
+
+ target_done:
+ free(indexpath);
+ free(basepath);
+ strlist_done(&db_packages);
+ strlist_done(&idx_packages);
+ }
+ }
+
+ if (verbose >= 5)
+ fprintf(stderr, "trace: finished verifying exported indices.\n");
+
+ return result;
+}

/* get a pointer to the apropiate part of the linked list */
struct distribution *distribution_find(struct distribution *distributions, const char *name) {
diff --git a/distribution.h b/distribution.h
index 1078084..9123764 100644
--- a/distribution.h
+++ b/distribution.h
@@ -153,6 +153,7 @@ struct distribution *distribution_find(struct distribution *, const char *);
retvalue distribution_freelist(/*@only@*/struct distribution *distributions);
enum exportwhen {EXPORT_NEVER, EXPORT_SILENT_NEVER, EXPORT_CHANGED, EXPORT_NORMAL, EXPORT_FORCE };
retvalue distribution_exportlist(enum exportwhen when, /*@only@*/struct distribution *);
+retvalue distribution_verify_exported_indices(struct distribution *distributions);

retvalue distribution_loadalloverrides(struct distribution *);
void distribution_unloadoverrides(struct distribution *distribution);
diff --git a/main.c b/main.c
index ad3f61a..c8570b8 100644
--- a/main.c
+++ b/main.c
@@ -4266,6 +4266,16 @@ static retvalue callaction(command_t command, const struct action *action, int a
if (!RET_WAS_ERROR(result)) {
r = distribution_exportlist(export, alldistributions);
RET_ENDUPDATE(result, r);
+ /* verify exported index files match database
+ * to catch inconsistencies caused by partial exports
+ * or secondary index corruption (e.g. bug #1095493) */
+ if (!RET_WAS_ERROR(result) &&
+ export != EXPORT_NEVER &&
+ export != EXPORT_SILENT_NEVER) {
+ r = distribution_verify_exported_indices(
+ alldistributions);
+ RET_ENDUPDATE(result, r);
+ }
}

r = distribution_freelist(alldistributions);
@@ -4402,6 +4412,16 @@ static retvalue callaction(command_t command, const struct action *action, int a
r = distribution_exportlist(export,
alldistributions);
RET_ENDUPDATE(result, r);
+ /* verify exported index files match database
+ * to catch inconsistencies caused by partial exports
+ * or secondary index corruption (e.g. bug #1095493) */
+ if (!RET_WAS_ERROR(result) &&
+ export != EXPORT_NEVER &&
+ export != EXPORT_SILENT_NEVER) {
+ r = distribution_verify_exported_indices(
+ alldistributions);
+ RET_ENDUPDATE(result, r);
+ }
}

/* delete files losing references, or
1 change: 1 addition & 0 deletions debian/patches/series
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
0002-feat-add-ignore-source-checksum-option-support.patch
0003-chore-Change-sha512-implement.patch
0004-fix-import-source-file-checksum-error.patch
0005-feat-verify-exported-indices-consistency.patch
Loading