summaryrefslogtreecommitdiffstats
path: root/downloadcache.c
diff options
context:
space:
mode:
Diffstat (limited to 'downloadcache.c')
-rw-r--r--downloadcache.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/downloadcache.c b/downloadcache.c
new file mode 100644
index 0000000..a51f4e5
--- /dev/null
+++ b/downloadcache.c
@@ -0,0 +1,315 @@
+/* This file is part of "reprepro"
+ * Copyright (C) 2004,2005,2007,2009 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 <sys/types.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "error.h"
+#include "strlist.h"
+#include "names.h"
+#include "dirs.h"
+#include "files.h"
+#include "freespace.h"
+#include "downloadcache.h"
+
+
+struct downloaditem {
+ /*@dependent@*//*@null@*/struct downloaditem *parent;
+ /*@null@*/struct downloaditem *left, *right;
+ char *filekey;
+ struct checksums *checksums;
+ bool done;
+};
+
+/* Initialize a new download session */
+retvalue downloadcache_initialize(enum spacecheckmode mode, off_t reserveddb, off_t reservedother, struct downloadcache **download) {
+ struct downloadcache *cache;
+ retvalue r;
+
+ cache = zNEW(struct downloadcache);
+ if (FAILEDTOALLOC(cache))
+ return RET_ERROR_OOM;
+ r = space_prepare(&cache->devices, mode, reserveddb, reservedother);
+ if (RET_WAS_ERROR(r)) {
+ free(cache);
+ return r;
+ }
+ *download = cache;
+ return RET_OK;
+}
+
+/* free all memory */
+static void freeitem(/*@null@*//*@only@*/struct downloaditem *item) {
+ if (item == NULL)
+ return;
+ freeitem(item->left);
+ freeitem(item->right);
+ free(item->filekey);
+ checksums_free(item->checksums);
+ free(item);
+}
+
+retvalue downloadcache_free(struct downloadcache *download) {
+ if (download == NULL)
+ return RET_NOTHING;
+
+ freeitem(download->items);
+ space_free(download->devices);
+ free(download);
+ return RET_OK;
+}
+
+static retvalue downloaditem_callback(enum queue_action action, void *privdata, void *privdata2, const char *uri, const char *gotfilename, const char *wantedfilename, /*@null@*/const struct checksums *checksums, const char *method) {
+ struct downloaditem *d = privdata;
+ struct downloadcache *cache = privdata2;
+ struct checksums *read_checksums = NULL;
+ retvalue r;
+ bool improves;
+
+ if (action != qa_got)
+ // TODO: instead store in downloaditem?
+ return RET_ERROR;
+
+ /* if the file is somewhere else, copy it: */
+ if (strcmp(gotfilename, wantedfilename) != 0) {
+ if (verbose > 1)
+ fprintf(stderr,
+"Linking file '%s' to '%s'...\n", gotfilename, wantedfilename);
+ r = checksums_linkorcopyfile(wantedfilename, gotfilename,
+ &read_checksums);
+ if (r == RET_NOTHING) {
+ fprintf(stderr,
+"Cannot open '%s', obtained from '%s' method.\n",
+ gotfilename, method);
+ r = RET_ERROR_MISSING;
+ }
+ if (RET_WAS_ERROR(r)) {
+ // TODO: instead store in downloaditem?
+ return r;
+ }
+ if (read_checksums != NULL)
+ checksums = read_checksums;
+ }
+
+ if (checksums == NULL || !checksums_iscomplete(checksums)) {
+ assert(read_checksums == NULL);
+ r = checksums_read(wantedfilename, &read_checksums);
+ if (r == RET_NOTHING) {
+ fprintf(stderr,
+"Cannot open '%s', though '%s' method claims to have put it there!\n",
+ wantedfilename, method);
+ r = RET_ERROR_MISSING;
+ }
+ if (RET_WAS_ERROR(r)) {
+ // TODO: instead store in downloaditem?
+ return r;
+ }
+ checksums = read_checksums;
+ }
+ assert (checksums != NULL);
+
+ if (!checksums_check(d->checksums, checksums, &improves)) {
+ fprintf(stderr, "Wrong checksum during receive of '%s':\n",
+ uri);
+ checksums_printdifferences(stderr, d->checksums, checksums);
+ checksums_free(read_checksums);
+ (void)unlink(wantedfilename);
+ // TODO: instead store in downloaditem?
+ return RET_ERROR_WRONG_MD5;
+ }
+ if (improves) {
+ r = checksums_combine(&d->checksums, checksums, NULL);
+ checksums_free(read_checksums);
+ if (RET_WAS_ERROR(r))
+ return r;
+ } else
+ checksums_free(read_checksums);
+
+ if (global.showdownloadpercent > 0) {
+ unsigned int percent;
+
+ cache->size_done += checksums_getfilesize(d->checksums);
+
+ percent = (100 * cache->size_done) / cache->size_todo;
+ if (global.showdownloadpercent > 1
+ || percent > cache->last_percent) {
+ unsigned long long all = cache->size_done;
+ int kb, mb, gb, tb, b, groups = 0;
+
+ cache->last_percent = percent;
+
+ printf("Got %u%%: ", percent);
+ b = all & 1023;
+ all = all >> 10;
+ kb = all & 1023;
+ all = all >> 10;
+ mb = all & 1023;
+ all = all >> 10;
+ gb = all & 1023;
+ all = all >> 10;
+ tb = all;
+ if (tb != 0) {
+ printf("%dT ", tb);
+ groups++;
+ }
+ if (groups < 2 && (groups > 0 || gb != 0)) {
+ printf("%dG ", gb);
+ groups++;
+ }
+ if (groups < 2 && (groups > 0 || mb != 0)) {
+ printf("%dM ", mb);
+ groups++;
+ }
+ if (groups < 2 && (groups > 0 || kb != 0)) {
+ printf("%dK ", kb);
+ groups++;
+ }
+ if (groups < 2 && (groups > 0 || b != 0))
+ printf("%d ", b);
+ puts("bytes");
+ }
+ }
+ r = files_add_checksums(d->filekey, d->checksums);
+ if (RET_WAS_ERROR(r))
+ return r;
+ d->done = true;
+ return RET_OK;
+}
+
+/*@null@*//*@dependent@*/ static struct downloaditem *searchforitem(struct downloadcache *list,
+ const char *filekey,
+ /*@out@*/struct downloaditem **p,
+ /*@out@*/struct downloaditem ***h) {
+ struct downloaditem *item;
+ int c;
+
+ *h = &list->items;
+ *p = NULL;
+ item = list->items;
+ while (item != NULL) {
+ *p = item;
+ c = strcmp(filekey, item->filekey);
+ if (c == 0)
+ return item;
+ else if (c < 0) {
+ *h = &item->left;
+ item = item->left;
+ } else {
+ *h = &item->right;
+ item = item->right;
+ }
+ }
+ return NULL;
+}
+
+/* queue a new file to be downloaded:
+ * results in RET_ERROR_WRONG_MD5, if someone else already asked
+ * for the same destination with other md5sum created. */
+retvalue downloadcache_add(struct downloadcache *cache, struct aptmethod *method, const char *orig, const char *filekey, const struct checksums *checksums) {
+
+ struct downloaditem *i;
+ struct downloaditem *item, **h, *parent;
+ char *fullfilename;
+ retvalue r;
+
+ assert (cache != NULL && method != NULL);
+ r = files_expect(filekey, checksums, false);
+ if (r != RET_NOTHING)
+ return r;
+
+ i = searchforitem(cache, filekey, &parent, &h);
+ if (i != NULL) {
+ bool improves;
+
+ assert (i->filekey != NULL);
+ if (!checksums_check(i->checksums, checksums, &improves)) {
+ fprintf(stderr,
+"ERROR: Same file is requested with conflicting checksums:\n");
+ checksums_printdifferences(stderr,
+ i->checksums, checksums);
+ return RET_ERROR_WRONG_MD5;
+ }
+ if (improves) {
+ r = checksums_combine(&i->checksums,
+ checksums, NULL);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+ return RET_NOTHING;
+ }
+ item = zNEW(struct downloaditem);
+ if (FAILEDTOALLOC(item))
+ return RET_ERROR_OOM;
+
+ item->done = false;
+ item->filekey = strdup(filekey);
+ item->checksums = checksums_dup(checksums);
+ if (FAILEDTOALLOC(item->filekey) || FAILEDTOALLOC(item->checksums)) {
+ freeitem(item);
+ return RET_ERROR_OOM;
+ }
+
+ fullfilename = files_calcfullfilename(filekey);
+ if (FAILEDTOALLOC(fullfilename)) {
+ freeitem(item);
+ return RET_ERROR_OOM;
+ }
+ (void)dirs_make_parent(fullfilename);
+ r = space_needed(cache->devices, fullfilename, checksums);
+ if (RET_WAS_ERROR(r)) {
+ free(fullfilename);
+ freeitem(item);
+ return r;
+ }
+ r = aptmethod_enqueue(method, orig, fullfilename,
+ downloaditem_callback, item, cache);
+ if (RET_WAS_ERROR(r)) {
+ freeitem(item);
+ return r;
+ }
+ item->left = item->right = NULL;
+
+ item->parent = parent;
+ *h = item;
+
+ cache->size_todo += checksums_getfilesize(item->checksums);
+
+ return RET_OK;
+}
+
+/* some as above, only for more files... */
+retvalue downloadcache_addfiles(struct downloadcache *cache, struct aptmethod *method, const struct checksumsarray *origfiles, const struct strlist *filekeys) {
+ retvalue result, r;
+ int i;
+
+ assert (origfiles != NULL && filekeys != NULL
+ && origfiles->names.count == filekeys->count);
+
+ result = RET_NOTHING;
+
+ for (i = 0 ; i < filekeys->count ; i++) {
+ r = downloadcache_add(cache, method,
+ origfiles->names.values[i],
+ filekeys->values[i],
+ origfiles->checksums[i]);
+ RET_UPDATE(result, r);
+ }
+ return result;
+}