summaryrefslogtreecommitdiffstats
path: root/src/diff.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2003-08-26 21:43:31 +0000
committerLennart Poettering <lennart@poettering.net>2003-08-26 21:43:31 +0000
commit3bc3ad24a2c089b4ee80fc1765fab3d2af378d00 (patch)
tree632a002b1c9a507ab49c11875eb373a6642902fb /src/diff.c
parentd4a8a10792c7f9b777487b2d15ab56c737e7e35c (diff)
Initial commit
git-svn-id: file:///home/lennart/svn/public/syrep/trunk@2 07ea20a6-d2c5-0310-9e02-9ef735347d72
Diffstat (limited to 'src/diff.c')
-rw-r--r--src/diff.c345
1 files changed, 345 insertions, 0 deletions
diff --git a/src/diff.c b/src/diff.c
new file mode 100644
index 0000000..100d4f9
--- /dev/null
+++ b/src/diff.c
@@ -0,0 +1,345 @@
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "diff.h"
+#include "dbstruct.h"
+#include "util.h"
+#include "md5util.h"
+#include "dbutil.h"
+
+static int add_diff_entry(DB *ddb, struct syrep_name *name, int action, struct syrep_db_context *repository) {
+ DBT key, data;
+ int ret;
+ struct diff_entry de;
+
+ memset(&de, 0, sizeof(de));
+ de.action = action;
+ de.repository = repository;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = name;
+ key.size = sizeof(struct syrep_name);
+
+ data.data = &de;
+ data.size = sizeof(struct diff_entry);
+
+ if ((ret = ddb->put(ddb, NULL, &key, &data, DB_NOOVERWRITE)) != 0) {
+ DBT data2;
+
+ if (ret != DB_KEYEXIST) {
+ ddb->err(ddb, ret, "ddb::put()");
+ return -1;
+ }
+
+ memset(&data2, 0, sizeof(data2));
+
+ if ((ret = ddb->get(ddb, NULL, &key, &data2, 0)) != 0) {
+ ddb->err(ddb, ret, "ddb::get()");
+ return -1;
+ }
+
+ if (data.size != data2.size || memcmp(data.data, data2.data, data.size)) {
+ fprintf(stderr, "Snapshot inconsistency\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int foreach(DB *ddb, struct syrep_db_context *c1, struct syrep_db_context *c2, struct syrep_name *name) {
+ struct syrep_md md1, md2;
+ int md1_valid, md2_valid;
+
+
+ if ((md1_valid = get_current_md_by_name(c1, name, &md1)) < 0)
+ return -1;
+
+ if ((md2_valid = get_current_md_by_name(c2, name, &md2)) < 0)
+ return -1;
+
+
+ //fprintf(stderr, "FOREACH %i %i %s\n", md1_valid, md2_valid, name->path);
+
+ if (md1_valid && md2_valid) {
+ int f1, f2;
+
+ /* Same file? */
+ if (!memcmp(&md1, &md2, sizeof(struct syrep_md)))
+ return 0;
+
+ if ((f1 = get_meta_by_name_md(c1, name, &md2, NULL)) < 0)
+ return -1;
+
+ if ((f2 = get_meta_by_name_md(c2, name, &md1, NULL)) < 0)
+ return -1;
+
+ /* The version in c1 is a newer version of that in c2 */
+ if (f1 && !f2)
+ return add_diff_entry(ddb, name, DIFF_COPY, c1);
+
+ /* Vice versa */
+ if (!f1 && f2)
+ return add_diff_entry(ddb, name, DIFF_COPY, c2);
+
+ /* Completely different file */
+ return add_diff_entry(ddb, name, DIFF_CONFLICT, NULL);
+
+ } else if (md1_valid) {
+ struct syrep_meta meta1, meta2;
+ int f1, f2;
+ uint32_t t1, t2;
+
+ if ((md2_valid = get_last_md_by_name(c2, name, &md2)) < 0)
+ return -1;
+
+ if (!md2_valid)
+ return add_diff_entry(ddb, name, DIFF_COPY, c1);
+
+ if (memcmp(&md1, &md2, sizeof(struct syrep_md)))
+ return add_diff_entry(ddb, name, DIFF_COPY, c1);
+
+ if ((f1 = get_meta_by_name_md(c1, name, &md1, &meta1)) < 0)
+ return -1;
+
+ if ((f2 = get_meta_by_name_md(c2, name, &md2, &meta2)) < 0)
+ return -1;
+
+ if (!f1 || !f2) {
+ fprintf(stderr, "Database inconsistency\n");
+ return -1;
+ }
+
+ /* Check whether file reappeared in c1 */
+ if ((t1 = get_version_timestamp(c1, meta1.first_seen-1)) == (uint32_t) -1)
+ return -1;
+
+ if ((t2 = get_version_timestamp(c2, meta2.last_seen+1)) == (uint32_t) -1)
+ return -1;
+
+ if (t1 >= t2)
+ return add_diff_entry(ddb, name, DIFF_COPY, c1);
+
+
+ /* Check whether file was deleted in c2 */
+ if ((t1 = get_version_timestamp(c1, meta1.first_seen)) == (uint32_t) -1)
+ return -1;
+
+ if ((t2 = get_version_timestamp(c2, meta2.last_seen)) == (uint32_t) -1)
+ return -1;
+
+ if (t1 < t2)
+ return add_diff_entry(ddb, name, DIFF_DELETE, c1);
+
+ return add_diff_entry(ddb, name, DIFF_CONFLICT, NULL);
+
+ } else if (md2_valid) {
+ fprintf(stderr, "This should be impossible!\n");
+ abort();
+ }
+
+ return 0;
+}
+
+
+static int enumerate(DB *ddb, struct syrep_db_context *c1, struct syrep_db_context *c2) {
+ int r = -1, ret;
+ DBC *cursor = NULL;
+ DBT key, data;
+
+ if ((ret = c1->db_id_meta->cursor(c1->db_id_meta, NULL, &cursor, 0)) != 0) {
+ c1->db_id_meta->err(c1->db_id_meta, ret, "id_meta::cursor()");
+ goto finish;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ while ((ret = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
+ struct syrep_id *id = (struct syrep_id*) key.data;
+ struct syrep_meta *meta = (struct syrep_meta*) data.data;
+
+
+
+ assert(id && meta);
+
+ if (meta->last_seen != c1->version)
+ continue;
+
+ if (foreach(ddb, c1, c2, &id->name) < 0) {
+ fprintf(stderr, "foreach() failed\n");
+ goto finish;
+ }
+ }
+
+
+ if (ret != DB_NOTFOUND) {
+ c1->db_id_meta->err(c1->db_id_meta, ret, "id_meta::c_get()");
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+
+ if (cursor)
+ cursor->c_close(cursor);
+
+ return r;
+}
+
+DB* make_diff(struct syrep_db_context *c1, struct syrep_db_context *c2) {
+ int ret;
+ DB *ddb = NULL;
+
+ if ((ret = db_create(&ddb, NULL, 0))) {
+ fprintf(stderr, "ddb::create(): %s\n", db_strerror(ret));
+ goto finish;
+ }
+
+ if ((ret = ddb->open(ddb, NULL, NULL, NULL, DB_BTREE, DB_CREATE, 0664))) {
+ ddb->err(ddb, ret, "ddb::open()");
+ goto finish;
+ }
+
+ if (enumerate(ddb, c1, c2) < 0)
+ goto finish;
+
+ if (enumerate(ddb, c2, c1) < 0)
+ goto finish;
+
+ return ddb;
+
+finish:
+
+ if (ddb)
+ ddb->close(ddb, 0);
+
+ return NULL;
+}
+
+
+struct cb_info {
+ struct syrep_db_context *c1, *c2;
+};
+
+static int list_cb(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) {
+ struct syrep_md md1, md2;
+ int f1, f2;
+ struct cb_info *cb_info = p;
+
+ assert(name && de);
+
+ if ((f1 = get_last_md_by_name(cb_info->c1, name, &md1)) < 0)
+ return -1;
+
+ if ((f2 = get_last_md_by_name(cb_info->c2, name, &md2)) < 0)
+ return -1;
+
+ if (!(f1 || f2)) {
+ fprintf(stderr, "Diff inconsicteny\n");
+ return -1;
+ }
+
+ switch (de->action) {
+ case DIFF_COPY: {
+ char d[33];
+ char src, dst;
+
+ if (de->repository == cb_info->c1) {
+ src = 'A'; dst = 'B';
+ fhex_md5(md1.digest, d);
+ } else {
+ src = 'B'; dst = 'A';
+ fhex_md5(md2.digest, d);
+ }
+
+ d[32] = 0;
+
+ fprintf(stderr, "COPY <%s|%s> FROM %c TO %c\n", d, name->path, src, dst);
+ break;
+ }
+
+ case DIFF_DELETE: {
+ char d[33];
+ int rep;
+
+ if (de->repository == cb_info->c1) {
+ rep = 'A';
+ fhex_md5(md1.digest, d);
+ } else {
+ rep = 'B';
+ fhex_md5(md2.digest, d);
+ }
+
+ d[32] = 0;
+
+ fprintf(stderr, "DELETE <%s|%s> FROM %c\n", d, name->path, rep);
+ break;
+
+ }
+
+ case DIFF_CONFLICT: {
+ char d1[33], d2[33];
+
+ fhex_md5(md1.digest, d1);
+ fhex_md5(md2.digest, d2);
+
+ d1[32] = d2[32] = 0;
+
+ fprintf(stderr, "CONFLICT <%s> BETWEEN <%s> IN A AND <%s> IN B\n", name->path, d1, d2);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int list_diff(struct syrep_db_context *c1, struct syrep_db_context *c2, DB *ddb) {
+ struct cb_info cb_info;
+ cb_info.c1 = c1;
+ cb_info.c2 = c2;
+
+ return diff_foreach(ddb, list_cb, &cb_info);
+}
+
+int diff_foreach(DB *ddb, int (*cb)(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p), void *p) {
+ DBC *cursor = NULL;
+ int r = -1, ret;
+ DBT key, data;
+
+ if ((ret = ddb->cursor(ddb, NULL, &cursor, 0)) != 0) {
+ ddb->err(ddb, ret, "ddb::cursor()");
+ goto finish;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ while ((ret = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
+ int t;
+ struct syrep_name *name = (struct syrep_name*) key.data;
+ struct diff_entry *de = (struct diff_entry*) data.data;
+
+ if ((t = cb(ddb, name, de, p)) < 0) {
+ r = t;
+ goto finish;
+ }
+ }
+
+ if (ret != DB_NOTFOUND) {
+ ddb->err(ddb, ret, "ddb::c_get() failed");
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (cursor)
+ cursor->c_close(cursor);
+
+ return r;
+}