summaryrefslogtreecommitdiffstats
path: root/files.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:12:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:12:14 +0000
commit4b8a0f3f3dcf60dac2ce308ea08d413a535af29f (patch)
tree0f09c0ad2a4d0f535d89040a63dc3a866a6606e6 /files.c
parentInitial commit. (diff)
downloadreprepro-4b8a0f3f3dcf60dac2ce308ea08d413a535af29f.tar.xz
reprepro-4b8a0f3f3dcf60dac2ce308ea08d413a535af29f.zip
Adding upstream version 5.4.4.upstream/5.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'files.c')
-rw-r--r--files.c817
1 files changed, 817 insertions, 0 deletions
diff --git a/files.c b/files.c
new file mode 100644
index 0000000..0f3b618
--- /dev/null
+++ b/files.c
@@ -0,0 +1,817 @@
+/* This file is part of "reprepro"
+ * Copyright (C) 2003,2004,2005,2006,2007,2008 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 <sys/stat.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "error.h"
+#include "strlist.h"
+#include "filecntl.h"
+#include "names.h"
+#include "checksums.h"
+#include "dirs.h"
+#include "names.h"
+#include "files.h"
+#include "ignore.h"
+#include "filelist.h"
+#include "debfile.h"
+#include "pool.h"
+#include "database_p.h"
+
+static retvalue files_get_checksums(const char *filekey, /*@out@*/struct checksums **checksums_p) {
+ const char *checksums;
+ size_t checksumslen;
+ retvalue r;
+
+ r = table_gettemprecord(rdb_checksums, filekey,
+ &checksums, &checksumslen);
+ if (!RET_IS_OK(r))
+ return r;
+ return checksums_setall(checksums_p, checksums, checksumslen);
+}
+
+retvalue files_add_checksums(const char *filekey, const struct checksums *checksums) {
+ retvalue r;
+ const char *combined;
+ size_t combinedlen;
+
+ assert (rdb_checksums != NULL);
+ r = checksums_getcombined(checksums, &combined, &combinedlen);
+ if (!RET_IS_OK(r))
+ return r;
+ r = table_adduniqsizedrecord(rdb_checksums, filekey,
+ combined, combinedlen + 1, true, false);
+ if (!RET_IS_OK(r))
+ return r;
+ return pool_markadded(filekey);
+}
+
+static retvalue files_replace_checksums(const char *filekey, const struct checksums *checksums) {
+ retvalue r;
+ const char *combined;
+ size_t combinedlen;
+
+ assert (rdb_checksums != NULL);
+ r = checksums_getcombined(checksums, &combined, &combinedlen);
+ if (!RET_IS_OK(r))
+ return r;
+ return table_adduniqsizedrecord(rdb_checksums, filekey,
+ combined, combinedlen + 1, true, false);
+}
+
+/* remove file's md5sum from database */
+retvalue files_removesilent(const char *filekey) {
+ retvalue r;
+
+ if (rdb_contents != NULL)
+ (void)table_deleterecord(rdb_contents, filekey, true);
+ r = table_deleterecord(rdb_checksums, filekey, true);
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Unable to forget unknown filekey '%s'.\n",
+ filekey);
+ return RET_ERROR_MISSING;
+ }
+ return r;
+}
+
+retvalue files_remove(const char *filekey) {
+ retvalue r;
+
+ r = files_removesilent(filekey);
+ if (RET_IS_OK(r))
+ return pool_markdeleted(filekey);
+ return r;
+}
+
+/* hardlink file with known checksums and add it to database */
+retvalue files_hardlinkandadd(const char *tempfile, const char *filekey, const struct checksums *checksums) {
+ retvalue r;
+
+ /* an additional check to make sure nothing tricks us into
+ * overwriting it by another file */
+ r = files_canadd(filekey, checksums);
+ if (!RET_IS_OK(r))
+ return r;
+ r = checksums_hardlink(global.outdir, filekey, tempfile, checksums);
+ if (RET_WAS_ERROR(r))
+ return r;
+
+ return files_add_checksums(filekey, checksums);
+}
+
+/* check if file is already there (RET_NOTHING) or could be added (RET_OK)
+ * or RET_ERROR_WRONG_MD5SUM if filekey already has different md5sum */
+retvalue files_canadd(const char *filekey, const struct checksums *checksums) {
+ retvalue r;
+ struct checksums *indatabase;
+ bool improves;
+
+ r = files_get_checksums(filekey, &indatabase);
+ if (r == RET_NOTHING)
+ return RET_OK;
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (!checksums_check(indatabase, checksums, &improves)) {
+ fprintf(stderr,
+"File \"%s\" is already registered with different checksums!\n",
+ filekey);
+ checksums_printdifferences(stderr, indatabase, checksums);
+ checksums_free(indatabase);
+ return RET_ERROR_WRONG_MD5;
+
+ }
+ // TODO: sometimes the caller might want to have additional
+ // checksums from the database already, think about ways to
+ // make them available...
+ checksums_free(indatabase);
+ return RET_NOTHING;
+}
+
+
+/* check for file in the database and if not found there, if it can be detected */
+retvalue files_expect(const char *filekey, const struct checksums *checksums, bool warnifadded) {
+ retvalue r;
+ char *filename;
+ struct checksums *improvedchecksums = NULL;
+
+ r = files_canadd(filekey, checksums);
+ if (r == RET_NOTHING)
+ return RET_OK;
+ if (RET_WAS_ERROR(r))
+ return r;
+
+ /* ready to add means missing, so have to look for the file itself: */
+ filename = files_calcfullfilename(filekey);
+ if (FAILEDTOALLOC(filename))
+ return RET_ERROR_OOM;
+
+ /* first check if a possible manually put (or left over from previous
+ * downloads attepts) file is there and is correct */
+ r = checksums_test(filename, checksums, &improvedchecksums);
+ if (r == RET_ERROR_WRONG_MD5) {
+ fprintf(stderr,
+"Deleting unexpected file '%s'!\n"
+"(not in database and wrong in pool)\n ",
+ filename);
+ if (unlink(filename) == 0)
+ r = RET_NOTHING;
+ else {
+ int e = errno;
+ fprintf(stderr,
+"Error %d deleting '%s': %s!\n", e, filename, strerror(e));
+ }
+ }
+ free(filename);
+ if (!RET_IS_OK(r))
+ return r;
+
+ if (warnifadded)
+ fprintf(stderr,
+"Warning: readded existing file '%s' mysteriously missing from the checksum database.\n",
+ filekey);
+
+ // TODO: some callers might want the updated checksum when
+ // improves is true, how to get them there?
+
+ /* add found file to database */
+ if (improvedchecksums != NULL) {
+ r = files_add_checksums(filekey, improvedchecksums);
+ checksums_free(improvedchecksums);
+ } else
+ r = files_add_checksums(filekey, checksums);
+ assert (r != RET_NOTHING);
+ return r;
+}
+
+/* check for several files in the database and in the pool if missing */
+retvalue files_expectfiles(const struct strlist *filekeys, struct checksums *checksumsarray[]) {
+ int i;
+ retvalue r;
+
+ for (i = 0 ; i < filekeys->count ; i++) {
+ const char *filekey = filekeys->values[i];
+ const struct checksums *checksums = checksumsarray[i];
+
+ r = files_expect(filekey, checksums, verbose >= 0);
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (r == RET_NOTHING) {
+ /* File missing */
+ fprintf(stderr, "Missing file %s\n", filekey);
+ return RET_ERROR_MISSING;
+ }
+ }
+ return RET_OK;
+}
+
+static inline retvalue checkorimprove(const char *filekey, struct checksums **checksums_p) {
+ const struct checksums *checksums = *checksums_p;
+ struct checksums *indatabase;
+ bool improves;
+ retvalue r;
+
+ r = files_get_checksums(filekey, &indatabase);
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Missing file %s\n", filekey);
+ return RET_ERROR_MISSING;
+ }
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (!checksums_check(checksums, indatabase, &improves)) {
+ fprintf(stderr,
+"File \"%s\" is already registered with different checksums!\n",
+ filekey);
+ checksums_printdifferences(stderr, indatabase, checksums);
+ r = RET_ERROR_WRONG_MD5;
+ } else if (improves) {
+ r = checksums_combine(checksums_p, indatabase, NULL);
+ } else
+ r = RET_NOTHING;
+ checksums_free(indatabase);
+ return r;
+}
+
+
+/* check for several files in the database and update information,
+ * return RET_NOTHING if everything is OK and nothing needs improving */
+retvalue files_checkorimprove(const struct strlist *filekeys, struct checksums *checksumsarray[]) {
+ int i;
+ retvalue result, r;
+
+ result = RET_NOTHING;
+ for (i = 0 ; i < filekeys->count ; i++) {
+ r = checkorimprove(filekeys->values[i],
+ &checksumsarray[i]);
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (RET_IS_OK(r))
+ result = RET_OK;
+ }
+ return result;
+}
+
+/* dump out all information */
+retvalue files_printmd5sums(void) {
+ retvalue result, r;
+ struct cursor *cursor;
+ const char *filekey, *checksum;
+
+ r = table_newglobalcursor(rdb_checksums, true, &cursor);
+ if (!RET_IS_OK(r))
+ return r;
+ result = RET_NOTHING;
+ while (cursor_nexttempdata(rdb_checksums, cursor, &filekey, &checksum, NULL)) {
+ result = RET_OK;
+ (void)fputs(filekey, stdout);
+ (void)putchar(' ');
+ while (*checksum == ':') {
+ while (*checksum != ' ' && *checksum != '\0')
+ checksum++;
+ if (*checksum == ' ')
+ checksum++;
+ }
+ (void)fputs(checksum, stdout);
+ (void)putchar('\n');
+ }
+ r = cursor_close(rdb_checksums, cursor);
+ RET_ENDUPDATE(result, r);
+ return result;
+}
+
+retvalue files_printchecksums(void) {
+ retvalue result, r;
+ struct cursor *cursor;
+ const char *filekey, *checksum;
+
+ r = table_newglobalcursor(rdb_checksums, true, &cursor);
+ if (!RET_IS_OK(r))
+ return r;
+ result = RET_NOTHING;
+ while (cursor_nexttempdata(rdb_checksums, cursor, &filekey, &checksum, NULL)) {
+ result = RET_OK;
+ (void)fputs(filekey, stdout);
+ (void)putchar(' ');
+ (void)fputs(checksum, stdout);
+ (void)putchar('\n');
+ if (interrupted()) {
+ result = RET_ERROR_INTERRUPTED;
+ break;
+ }
+ }
+ r = cursor_close(rdb_checksums, cursor);
+ RET_ENDUPDATE(result, r);
+ return result;
+}
+
+/* callback for each registered file */
+retvalue files_foreach(per_file_action action, void *privdata) {
+ retvalue result, r;
+ struct cursor *cursor;
+ const char *filekey, *checksum;
+
+ r = table_newglobalcursor(rdb_checksums, true, &cursor);
+ if (!RET_IS_OK(r))
+ return r;
+ result = RET_NOTHING;
+ while (cursor_nexttempdata(rdb_checksums, cursor, &filekey, &checksum, NULL)) {
+ if (interrupted()) {
+ RET_UPDATE(result, RET_ERROR_INTERRUPTED);
+ break;
+ }
+ r = action(privdata, filekey);
+ RET_UPDATE(result, r);
+ }
+ r = cursor_close(rdb_checksums, cursor);
+ RET_ENDUPDATE(result, r);
+ return result;
+}
+
+static retvalue checkpoolfile(const char *fullfilename, const struct checksums *expected, bool *improveable) {
+ struct checksums *actual;
+ retvalue r;
+ bool improves;
+
+ r = checksums_read(fullfilename, &actual);
+ if (RET_IS_OK(r)) {
+ if (!checksums_check(expected, actual, &improves)) {
+ fprintf(stderr, "WRONG CHECKSUMS of '%s':\n",
+ fullfilename);
+ checksums_printdifferences(stderr, expected, actual);
+ r = RET_ERROR_WRONG_MD5;
+ } else if (improves)
+ *improveable = true;
+ checksums_free(actual);
+ }
+ return r;
+}
+
+retvalue files_checkpool(bool fast) {
+ retvalue result, r;
+ struct cursor *cursor;
+ const char *filekey, *combined;
+ size_t combinedlen;
+ struct checksums *expected;
+ char *fullfilename;
+ bool improveable = false;
+
+ result = RET_NOTHING;
+ r = table_newglobalcursor(rdb_checksums, true, &cursor);
+ if (!RET_IS_OK(r))
+ return r;
+ while (cursor_nexttempdata(rdb_checksums, cursor,
+ &filekey, &combined, &combinedlen)) {
+ r = checksums_setall(&expected, combined, combinedlen);
+ if (RET_WAS_ERROR(r)) {
+ RET_UPDATE(result, r);
+ continue;
+ }
+ fullfilename = files_calcfullfilename(filekey);
+ if (FAILEDTOALLOC(fullfilename)) {
+ result = RET_ERROR_OOM;
+ checksums_free(expected);
+ break;
+ }
+ if (fast)
+ r = checksums_cheaptest(fullfilename, expected, true);
+ else
+ r = checkpoolfile(fullfilename, expected, &improveable);
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Missing file '%s'!\n", fullfilename);
+ r = RET_ERROR_MISSING;
+ }
+ free(fullfilename);
+ checksums_free(expected);
+ RET_UPDATE(result, r);
+ }
+ r = cursor_close(rdb_checksums, cursor);
+ RET_ENDUPDATE(result, r);
+ if (improveable && verbose >= 0)
+ printf(
+"There were files with only some of the checksums this version of reprepro\n"
+"can compute recorded. To add those run reprepro collectnewchecksums.\n");
+ return result;
+}
+
+retvalue files_collectnewchecksums(void) {
+ retvalue result, r;
+ struct cursor *cursor;
+ const char *filekey, *all;
+ size_t alllen;
+ struct checksums *expected;
+ char *fullfilename;
+
+ result = RET_NOTHING;
+ r = table_newglobalcursor(rdb_checksums, true, &cursor);
+ if (!RET_IS_OK(r))
+ return r;
+ while (cursor_nexttempdata(rdb_checksums, cursor,
+ &filekey, &all, &alllen)) {
+ r = checksums_setall(&expected, all, alllen);
+ if (!RET_IS_OK(r)) {
+ RET_UPDATE(result, r);
+ continue;
+ }
+ if (checksums_iscomplete(expected)) {
+ checksums_free(expected);
+ continue;
+ }
+
+ fullfilename = files_calcfullfilename(filekey);
+ if (FAILEDTOALLOC(fullfilename)) {
+ result = RET_ERROR_OOM;
+ checksums_free(expected);
+ break;
+ }
+ r = checksums_complete(&expected, fullfilename);
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Missing file '%s'!\n", fullfilename);
+ r = RET_ERROR_MISSING;
+ }
+ if (r == RET_ERROR_WRONG_MD5) {
+ fprintf(stderr,
+"ERROR: Cannot collect missing checksums for '%s'\n"
+"as the file in the pool does not match the already recorded checksums\n",
+ filekey);
+ }
+ free(fullfilename);
+ if (RET_IS_OK(r))
+ r = files_replace_checksums(filekey, expected);
+ checksums_free(expected);
+ RET_UPDATE(result, r);
+ }
+ r = cursor_close(rdb_checksums, cursor);
+ RET_ENDUPDATE(result, r);
+ return result;
+}
+
+retvalue files_detect(const char *filekey) {
+ struct checksums *checksums;
+ char *fullfilename;
+ retvalue r;
+
+ fullfilename = files_calcfullfilename(filekey);
+ if (FAILEDTOALLOC(fullfilename))
+ return RET_ERROR_OOM;
+ r = checksums_read(fullfilename, &checksums);
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Error opening '%s'!\n", fullfilename);
+ r = RET_ERROR_MISSING;
+ }
+ if (RET_WAS_ERROR(r)) {
+ free(fullfilename);
+ return r;
+ }
+ free(fullfilename);
+ r = files_add_checksums(filekey, checksums);
+ checksums_free(checksums);
+ return r;
+}
+
+struct rfd { bool reread; };
+
+static retvalue regenerate_filelist(void *data, const char *filekey) {
+ bool reread = ((struct rfd*)data)->reread;
+ size_t l = strlen(filekey);
+ char *debfilename;
+ char *filelist;
+ size_t fls;
+ retvalue r;
+
+ if (l <= 4 || memcmp(filekey+l-4, ".deb", 4) != 0)
+ return RET_NOTHING;
+
+ if (!reread && !table_recordexists(rdb_contents, filekey))
+ return RET_NOTHING;
+
+ debfilename = files_calcfullfilename(filekey);
+ if (FAILEDTOALLOC(debfilename))
+ return RET_ERROR_OOM;
+
+ r = getfilelist(&filelist, &fls, debfilename);
+ free(debfilename);
+ if (RET_IS_OK(r)) {
+ if (verbose > 0)
+ (void)puts(filekey);
+ if (verbose > 6) {
+ const char *p = filelist;
+ while (*p != '\0') {
+ (void)putchar(' ');
+ (void)puts(p);
+ p += strlen(p)+1;
+ }
+ }
+ r = table_adduniqsizedrecord(rdb_contents,
+ filekey, filelist, fls, true, true);
+ free(filelist);
+ }
+ return r;
+}
+
+retvalue files_regenerate_filelist(bool reread) {
+ struct rfd d;
+
+ d.reread = reread;
+ return files_foreach(regenerate_filelist, &d);
+}
+
+/* Include a yet unknown file into the pool */
+retvalue files_preinclude(const char *sourcefilename, const char *filekey, struct checksums **checksums_p) {
+ retvalue r;
+ struct checksums *checksums, *realchecksums;
+ bool improves;
+ char *fullfilename;
+
+ r = files_get_checksums(filekey, &checksums);
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (RET_IS_OK(r)) {
+ r = checksums_read(sourcefilename, &realchecksums);
+ if (r == RET_NOTHING)
+ r = RET_ERROR_MISSING;
+ if (RET_WAS_ERROR(r)) {
+ checksums_free(checksums);
+ return r;
+ }
+ if (!checksums_check(checksums, realchecksums, &improves)) {
+ fprintf(stderr,
+"ERROR: '%s' cannot be included as '%s'.\n"
+"Already existing files can only be included again, if they are the same, but:\n",
+ sourcefilename, filekey);
+ checksums_printdifferences(stderr, checksums,
+ realchecksums);
+ checksums_free(checksums);
+ checksums_free(realchecksums);
+ return RET_ERROR_WRONG_MD5;
+ }
+ if (improves) {
+ r = checksums_combine(&checksums, realchecksums, NULL);
+ if (RET_WAS_ERROR(r)) {
+ checksums_free(realchecksums);
+ checksums_free(checksums);
+ return r;
+ }
+ r = files_replace_checksums(filekey, checksums);
+ if (RET_WAS_ERROR(r)) {
+ checksums_free(realchecksums);
+ checksums_free(checksums);
+ return r;
+ }
+ }
+ checksums_free(realchecksums);
+ // args, this breaks retvalue semantics!
+ if (checksums_p != NULL)
+ *checksums_p = checksums;
+ else
+ checksums_free(checksums);
+ return RET_NOTHING;
+ }
+ assert (sourcefilename != NULL);
+ fullfilename = files_calcfullfilename(filekey);
+ if (FAILEDTOALLOC(fullfilename))
+ return RET_ERROR_OOM;
+ (void)dirs_make_parent(fullfilename);
+ r = checksums_copyfile(fullfilename, sourcefilename, true, &checksums);
+ if (r == RET_ERROR_EXIST) {
+ // TODO: deal with already existing files!
+ fprintf(stderr, "File '%s' does already exist!\n",
+ fullfilename);
+ }
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Could not open '%s'!\n", sourcefilename);
+ r = RET_ERROR_MISSING;
+ }
+ if (RET_WAS_ERROR(r)) {
+ free(fullfilename);
+ return r;
+ }
+ free(fullfilename);
+
+ r = files_add_checksums(filekey, checksums);
+ if (RET_WAS_ERROR(r)) {
+ checksums_free(checksums);
+ return r;
+ }
+ if (checksums_p != NULL)
+ *checksums_p = checksums;
+ else
+ checksums_free(checksums);
+ return RET_OK;
+}
+
+static retvalue checkimproveorinclude(const char *sourcedir, const char *basefilename, const char *filekey, struct checksums **checksums_p, bool *improving) {
+ retvalue r;
+ struct checksums *checksums = NULL;
+ bool improves, copied = false;
+ char *fullfilename = files_calcfullfilename(filekey);
+
+ if (FAILEDTOALLOC(fullfilename))
+ return RET_ERROR_OOM;
+
+ if (checksums_iscomplete(*checksums_p)) {
+ r = checksums_cheaptest(fullfilename, *checksums_p, true);
+ if (r != RET_NOTHING) {
+ free(fullfilename);
+ return r;
+ }
+ } else {
+ r = checksums_read(fullfilename, &checksums);
+ if (RET_WAS_ERROR(r)) {
+ free(fullfilename);
+ return r;
+ }
+ }
+ if (r == RET_NOTHING) {
+ char *sourcefilename = calc_dirconcat(sourcedir, basefilename);
+
+ if (FAILEDTOALLOC(sourcefilename)) {
+ free(fullfilename);
+ return RET_ERROR_OOM;
+ }
+
+ fprintf(stderr,
+"WARNING: file %s was lost!\n"
+"(i.e. found in the database, but not in the pool)\n"
+"trying to compensate...\n",
+ filekey);
+ (void)dirs_make_parent(fullfilename);
+ r = checksums_copyfile(fullfilename, sourcefilename, false,
+ &checksums);
+ if (r == RET_ERROR_EXIST) {
+ fprintf(stderr,
+"File '%s' seems to be missing and existing at the same time!\n"
+"To confused to continue...\n",
+ fullfilename);
+ }
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Could not open '%s'!\n",
+ sourcefilename);
+ r = RET_ERROR_MISSING;
+ }
+ free(sourcefilename);
+ if (RET_WAS_ERROR(r)) {
+ free(fullfilename);
+ return r;
+ }
+ copied = true;
+ }
+
+ assert (checksums != NULL);
+
+ if (!checksums_check(*checksums_p, checksums, &improves)) {
+ if (copied) {
+ deletefile(fullfilename);
+ fprintf(stderr,
+"ERROR: Unexpected content of file '%s/%s'!\n", sourcedir, basefilename);
+ } else
+// TODO: if the database only listed some of the currently supported checksums,
+// and the caller of checkincludefile supplied some (which none yet does), but
+// not all (which needs at least three checksums, i.e. not applicaple before
+// sha256 get added), then this might also be called if the file in the pool
+// just has the same checksums as previously recorded (e.g. a md5sum collision)
+// but the new file was listed with another secondary hash than the original.
+// In that situation it might be a bit misleading...
+ fprintf(stderr,
+"ERROR: file %s is damaged!\n"
+"(i.e. found in the database, but with different checksums in the pool)\n",
+ filekey);
+ checksums_printdifferences(stderr, *checksums_p, checksums);
+ r = RET_ERROR_WRONG_MD5;
+ }
+ if (improves) {
+ r = checksums_combine(checksums_p, checksums, NULL);
+ if (RET_IS_OK(r))
+ *improving = true;
+ }
+ checksums_free(checksums);
+ free(fullfilename);
+ return r;
+}
+
+retvalue files_checkincludefile(const char *sourcedir, const char *basefilename, const char *filekey, struct checksums **checksums_p) {
+ char *sourcefilename, *fullfilename;
+ struct checksums *checksums;
+ retvalue r;
+ bool improves;
+
+ assert (*checksums_p != NULL);
+
+ r = files_get_checksums(filekey, &checksums);
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (RET_IS_OK(r)) {
+ /* there are three sources now:
+ * - the checksums from the database (may have some we
+ * do not even know about, and may miss some we can
+ * generate)
+ * - the checksums provided (typically only md5sum,
+ * as this comes from a .changes or .dsc)
+ * - the checksums of the file
+ *
+ * to make things more complicated, the file should only
+ * be read if needed, as this needs time.
+ * And it can happen the file got lost in the pool, then
+ * this is the best place to replace it.
+ */
+ if (!checksums_check(checksums, *checksums_p, &improves)) {
+ fprintf(stderr,
+"ERROR: '%s/%s' cannot be included as '%s'.\n"
+"Already existing files can only be included again, if they are the same, but:\n",
+ sourcedir, basefilename, filekey);
+ checksums_printdifferences(stderr, checksums,
+ *checksums_p);
+ checksums_free(checksums);
+ return RET_ERROR_WRONG_MD5;
+ }
+ r = RET_NOTHING;
+ if (improves)
+ r = checksums_combine(&checksums, *checksums_p, NULL);
+ if (!RET_WAS_ERROR(r))
+ r = checkimproveorinclude(sourcedir,
+ basefilename, filekey, &checksums, &improves);
+ if (!RET_WAS_ERROR(r) && improves)
+ r = files_replace_checksums(filekey, checksums);
+ if (RET_IS_OK(r))
+ r = RET_NOTHING;
+ /* return the combined checksum */
+ checksums_free(*checksums_p);
+ *checksums_p = checksums;
+ return r;
+ }
+
+ assert (sourcedir != NULL);
+ sourcefilename = calc_dirconcat(sourcedir, basefilename);
+ if (FAILEDTOALLOC(sourcefilename))
+ return RET_ERROR_OOM;
+
+ fullfilename = files_calcfullfilename(filekey);
+ if (FAILEDTOALLOC(fullfilename)) {
+ free(sourcefilename);
+ return RET_ERROR_OOM;
+ }
+
+ (void)dirs_make_parent(fullfilename);
+ r = checksums_copyfile(fullfilename, sourcefilename, true, &checksums);
+ if (r == RET_NOTHING) {
+ fprintf(stderr, "Could not open '%s'!\n", sourcefilename);
+ r = RET_ERROR_MISSING;
+ }
+ if (RET_WAS_ERROR(r)) {
+ free(fullfilename);
+ free(sourcefilename);
+ return r;
+ }
+ if (!checksums_check(*checksums_p, checksums, &improves)) {
+ deletefile(fullfilename);
+ fprintf(stderr, "ERROR: Unexpected content of file '%s'!\n",
+ sourcefilename);
+ checksums_printdifferences(stderr, *checksums_p, checksums);
+ r = RET_ERROR_WRONG_MD5;
+ }
+ free(sourcefilename);
+ free(fullfilename);
+ if (RET_WAS_ERROR(r)) {
+ return r;
+ }
+ if (improves) {
+ r = checksums_combine(checksums_p, checksums, NULL);
+ checksums_free(checksums);
+ if (RET_WAS_ERROR(r))
+ return r;
+ } else
+ checksums_free(checksums);
+
+ return files_add_checksums(filekey, *checksums_p);
+}
+
+off_t files_getsize(const char *filekey) {
+ retvalue r;
+ off_t s;
+ struct checksums *checksums;
+
+ r = files_get_checksums(filekey, &checksums);
+ if (!RET_IS_OK(r))
+ return -1;
+ s = checksums_getfilesize(checksums);
+ checksums_free(checksums);
+ return s;
+}