diff options
author | Lennart Poettering <lennart@poettering.net> | 2003-08-26 21:43:31 +0000 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2003-08-26 21:43:31 +0000 |
commit | 3bc3ad24a2c089b4ee80fc1765fab3d2af378d00 (patch) | |
tree | 632a002b1c9a507ab49c11875eb373a6642902fb /src/diff.c | |
parent | d4a8a10792c7f9b777487b2d15ab56c737e7e35c (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.c | 345 |
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; +} |