From 98f36517251b5bccdb80d90d698c173c18ee4a29 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Aug 2003 00:26:05 +0000 Subject: implemented merging an patch making git-svn-id: file:///home/lennart/svn/public/syrep/trunk@9 07ea20a6-d2c5-0310-9e02-9ef735347d72 --- doc/package.txt | 15 ++++++ doc/todo.txt | 12 +++-- src/Makefile | 2 +- src/context.c | 26 +++++----- src/extract.c | 93 ++-------------------------------- src/makepatch.c | 72 +++++++++++++++++++++++++++ src/makepatch.h | 8 +++ src/merge.c | 69 +++++++++++++++++++------- src/merge.h | 2 +- src/package.c | 60 ++++++++++++++++------ src/package.h | 3 +- src/syrep.c | 74 ++++++++++++++++++++++++--- src/syrep.ggo | 11 +++-- src/util.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 4 ++ test/Makefile | 10 ++-- 16 files changed, 460 insertions(+), 152 deletions(-) create mode 100644 doc/package.txt create mode 100644 src/makepatch.c create mode 100644 src/makepatch.h diff --git a/doc/package.txt b/doc/package.txt new file mode 100644 index 0000000..e20274c --- /dev/null +++ b/doc/package.txt @@ -0,0 +1,15 @@ + + +File format for patches and snapshots: + + +8 bytes: id SYREPLP0 + +Multiple items of: + + 4 bytes: flags + 32 bytes: name as asciiz string + 4 bytes: length of item + + +$Id$ diff --git a/doc/todo.txt b/doc/todo.txt index a9da376..0bd0835 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -1,10 +1,16 @@ -1. merge/makepatch +1. merge/makepatch DONE 2. gzip 3. url merges -4. better usage info +4. better usage info on --help 5. documentation/man pages 6. stat() before and after md5 as consistency check 7. md5 before delete/replace/link -8. mmap usage package.c +8. mmap usage in package.c +9. Warn if hostname != origin +10. Recursive directory creation +11. Locking/Atomicity +12. Prune empty directories +13. Handle conflicts +14. -q mode $Id$ diff --git a/src/Makefile b/src/Makefile index addd2e8..a7e55ec 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,7 @@ CC=gcc all: syrep -syrep: cache.o update.o util.o syrep.o md5.o md5util.o context.o package.o dbutil.o cmdline.o info.o history.o dump.o list.o diff.o merge.o extract.o +syrep: cache.o update.o util.o syrep.o md5.o md5util.o context.o package.o dbutil.o cmdline.o info.o history.o dump.o list.o diff.o merge.o extract.o makepatch.o $(CC) -g $^ -o $@ -ldb cmdline.c cmdline.h: syrep.ggo Makefile diff --git a/src/context.c b/src/context.c index 00455ea..6512f0d 100644 --- a/src/context.c +++ b/src/context.c @@ -80,7 +80,7 @@ struct syrep_db_context* db_context_open(const char *filename) { if (!(c->package = package_open(filename))) goto fail; - path = package_get_item(c->package, "timestamp"); + path = package_get_item(c->package, "timestamp", 1); if ((f = fopen(path, "r"))) { if (fscanf(f, "%i", &c->timestamp) != 1) c->timestamp = 0; @@ -90,14 +90,14 @@ struct syrep_db_context* db_context_open(const char *filename) { if (!c->timestamp) c->timestamp = time(NULL); - path = package_get_item(c->package, "version"); + path = package_get_item(c->package, "version", 1); if ((f = fopen(path, "r"))) { if (fscanf(f, "%u", &c->version) != 1) c->version = 0; fclose(f); } - path = package_get_item(c->package, "origin"); + path = package_get_item(c->package, "origin", 1); if ((f = fopen(path, "r"))) { char hn[256]; if (fgets(hn, sizeof(hn), f)) { @@ -123,27 +123,27 @@ struct syrep_db_context* db_context_open(const char *filename) { } /* Creating database id_meta */ - if (!(c->db_id_meta = open_db(package_get_item(c->package, "id_meta"), 0))) + if (!(c->db_id_meta = open_db(package_get_item(c->package, "id_meta", 1), 0))) goto fail; /* Creating database md_name */ - if (!(c->db_md_name = open_db(package_get_item(c->package, "md_name"), 1))) + if (!(c->db_md_name = open_db(package_get_item(c->package, "md_name", 1), 1))) goto fail; /* Creating database name_md */ - if (!(c->db_name_md = open_db(package_get_item(c->package, "name_md"), 1))) + if (!(c->db_name_md = open_db(package_get_item(c->package, "name_md", 1), 1))) goto fail; /* Creating database name_lastmd */ - if (!(c->db_name_lastmd = open_db(package_get_item(c->package, "name_lastmd"), 0))) + if (!(c->db_name_lastmd = open_db(package_get_item(c->package, "name_lastmd", 1), 0))) goto fail; /* Creating database md_lastname */ - if (!(c->db_md_lastname = open_db(package_get_item(c->package, "md_lastname"), 0))) + if (!(c->db_md_lastname = open_db(package_get_item(c->package, "md_lastname", 1), 0))) goto fail; /* Creating database version_timestamp */ - if (!(c->db_version_timestamp = open_db(package_get_item(c->package, "version_timestamp"), 0))) + if (!(c->db_version_timestamp = open_db(package_get_item(c->package, "version_timestamp", 1), 0))) goto fail; return c; @@ -157,7 +157,7 @@ fail: int db_context_save(struct syrep_db_context *c, const char *filename) { FILE *f; - assert(c && c->package && filename); + assert(c && c->package); if (c->db_id_meta) c->db_id_meta->sync(c->db_id_meta, 0); @@ -177,19 +177,19 @@ int db_context_save(struct syrep_db_context *c, const char *filename) { if (c->db_version_timestamp) c->db_version_timestamp->sync(c->db_version_timestamp, 0); - if (!(f = fopen(package_get_item(c->package, "timestamp"), "w+"))) + if (!(f = fopen(package_get_item(c->package, "timestamp", 1), "w+"))) return -1; fprintf(f, "%i\n", c->timestamp); fclose(f); - if (!(f = fopen(package_get_item(c->package, "version"), "w+"))) + if (!(f = fopen(package_get_item(c->package, "version", 1), "w+"))) return -1; fprintf(f, "%u\n", c->version); fclose(f); - if (!(f = fopen(package_get_item(c->package, "origin"), "w+"))) + if (!(f = fopen(package_get_item(c->package, "origin", 1), "w+"))) return -1; fprintf(f, "%s\n", c->origin); diff --git a/src/extract.c b/src/extract.c index 06d95df..616b8d1 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -10,91 +9,7 @@ #include "package.h" #include "extract.h" - -#define MMAPSIZE (100*1024*1024) -#define BUFSIZE (1024*1024) - -int copy_file(const char *src, const char *dst, uint32_t l) { - int sfd = -1, dfd = -1, r = -1; - uint32_t m; - off_t o; - void *src_p, *dst_p; - void *buf = NULL; - - if ((sfd = open(src, O_RDONLY)) < 0) { - fprintf(stderr, "open(%s, O_RDONLY): %s\n", src, strerror(errno)); - goto finish; - } - - if ((dfd = open(dst, O_RDWR|O_TRUNC|O_CREAT, 0666)) < 0) { - fprintf(stderr, "open(%s, O_WRONLY): %s\n", dst, strerror(errno)); - goto finish; - } - - if (ftruncate(dfd, l) < 0) { - fprintf(stderr, "ftruncate(): %s\n", strerror(errno)); - goto finish; - } - - o = 0; - m = l < MMAPSIZE ? l : MMAPSIZE; - - while (l > 0) { - if ((src_p = mmap(NULL, m, PROT_READ, MAP_SHARED, sfd, o)) == MAP_FAILED) - break; - - if ((dst_p = mmap(NULL, m, PROT_READ|PROT_WRITE, MAP_SHARED, dfd, o)) == MAP_FAILED) { - munmap(src_p, m); - break; - } - - memcpy(dst_p, src_p, m); - - munmap(src_p, m); - munmap(dst_p, m); - - o += m; - l -= m; - m = l < MMAPSIZE ? l : MMAPSIZE; - } - - if (l > 0) { - fprintf(stderr, "mmap() failed: %s\n", strerror(errno)); - - if (!(buf = malloc(BUFSIZE))) - goto finish; - - for (;;) { - ssize_t r; - - if ((m = read(sfd, buf, BUFSIZE)) < 0) { - fprintf(stderr, "read(): %s\n", strerror(errno)); - goto finish; - } - - if (!m) - break; - - if ((r = write(dfd, buf, m)) < 0) { - fprintf(stderr, "write(): %s\n", strerror(errno)); - goto finish; - } - } - } - - - r = 0; - -finish: - - if (sfd >= 0) - close(sfd); - - if (dfd >= 0) - close(dfd); - - return r; -} +#include "util.h" static int cb(struct package *p, const char *name, const char *path, void *u) { struct stat st; @@ -110,8 +25,10 @@ static int cb(struct package *p, const char *name, const char *path, void *u) { } else size = (uint32_t) st.st_size; - if (size) - return copy_file(path, name, size); + if (size) { + fprintf(stderr, "Extracting %s ...\n", name); + return copy_file(path, name); + } return 0; } diff --git a/src/makepatch.c b/src/makepatch.c new file mode 100644 index 0000000..1ddeb93 --- /dev/null +++ b/src/makepatch.c @@ -0,0 +1,72 @@ +#include +#include +#include + +#include "package.h" +#include "makepatch.h" +#include "diff.h" +#include "dbutil.h" +#include "md5util.h" + +struct cb_info { + struct syrep_db_context *c1; + struct syrep_db_context *c2; + const char *root; +}; + +static int cb(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) { + struct cb_info *cb_info = p; + struct syrep_md md; + char path[PATH_MAX+1]; + char d[SYREP_DIGESTLENGTH*2+1]; + int f; + + assert(ddb && name && de && p); + + if (de->action != DIFF_COPY && de->action != DIFF_CONFLICT) + return 0; + + if ((f = get_current_md_by_name(cb_info->c1, name, &md)) < 0) + return -1; + + if (!f) + return 0; + + fhex(md.digest, SYREP_DIGESTLENGTH, d); + d[SYREP_DIGESTLENGTH*2] = 0; + + snprintf(path, sizeof(path), "%s/%s", cb_info->root, name->path); + + fprintf(stderr, "Adding %s (%s) to patch.\n", name->path, d); + + if (!package_get_item(cb_info->c1->package, d, 0)) + if (package_add_file(cb_info->c1->package, d, path) < 0) + return -1; + + return 0; +} + +int makepatch(struct syrep_db_context *c1, struct syrep_db_context *c2, const char *root) { + struct cb_info cb_info; + DB *ddb = NULL; + int r = -1; + + memset(&cb_info, 0, sizeof(cb_info)); + cb_info.c1 = c1; + cb_info.c2 = c2; + cb_info.root = root; + + if (!(ddb = make_diff(c1, c2))) + goto finish; + + if (diff_foreach(ddb, cb, &cb_info) < 0) + goto finish; + + r = 0; + +finish: + if (ddb) + ddb->close(ddb, 0); + + return r; +} diff --git a/src/makepatch.h b/src/makepatch.h new file mode 100644 index 0000000..f6836ae --- /dev/null +++ b/src/makepatch.h @@ -0,0 +1,8 @@ +#ifndef foomakepatchhfoo +#define foomakepatchhfoo + +#include "context.h" + +int makepatch(struct syrep_db_context *c1, struct syrep_db_context *c2, const char *dir); + +#endif diff --git a/src/merge.c b/src/merge.c index aef3f8f..059523e 100644 --- a/src/merge.c +++ b/src/merge.c @@ -7,6 +7,8 @@ #include "md5util.h" #include "diff.h" #include "dbutil.h" +#include "package.h" +#include "util.h" struct cb_info { struct syrep_db_context *c1; @@ -14,8 +16,10 @@ struct cb_info { const char *root; }; -int conflict_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) { +static int conflict_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) { struct cb_info *cb_info = p; + struct syrep_md md1, md2; + int f1, f2; char path[PATH_MAX+1]; assert(ddb && name && de && p); @@ -23,13 +27,34 @@ int conflict_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void if (de->action != DIFF_CONFLICT) return 0; + if ((f1 = get_current_md_by_name(cb_info->c1, name, &md1)) < 0) + return -1; + + if ((f2 = get_current_md_by_name(cb_info->c2, name, &md2)) < 0) + return -1; + snprintf(path, sizeof(path), "%s/%s", cb_info->root, name->path); - fprintf(stderr, "QUERY %s\n", path); + if (f1 && f2) { + char d1[SYREP_DIGESTLENGTH*2+1], d2[SYREP_DIGESTLENGTH*2+1]; + + fhex(md1.digest, SYREP_DIGESTLENGTH, d1); + fhex(md2.digest, SYREP_DIGESTLENGTH, d2); + d1[SYREP_DIGESTLENGTH*2] = d2[SYREP_DIGESTLENGTH*2] = 0; + + fprintf(stderr, "CONFLICT: New file '%s' apparead: %s in A vs. %s in B\n", path, d1, d2); + } else if (f1 || f2) { + char d[SYREP_DIGESTLENGTH*2+1]; + fhex(f1 ? md1.digest : md2.digest, SYREP_DIGESTLENGTH, d); + d[SYREP_DIGESTLENGTH*2] = 0; + + fprintf(stderr, "CONFLICT: File '%s' (%s) appeared in %c, removed in %c\n", path, d, f1 ? 'A' : 'B', f2 ? 'A' : 'B'); + } + return 0; } -int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) { +static int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) { struct cb_info *cb_info = p; struct syrep_name name2; struct syrep_md md; @@ -50,7 +75,6 @@ int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) if ((f = get_current_md_by_name(cb_info->c1, name, &md)) < 0) return -1; - if (!f) { fprintf(stderr, "Diff invalid!\n"); return -1; @@ -59,26 +83,33 @@ int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) fhex(md.digest, SYREP_DIGESTLENGTH, d); d[SYREP_DIGESTLENGTH*2] = 0; - fprintf(stderr, "Searching for %s\n", d); - if ((f = get_current_name_by_md(cb_info->c2, &md, &name2)) < 0) return -1; if (f) { char path2[PATH_MAX+1]; snprintf(path2, sizeof(path2), "%s/%s", cb_info->root, name2.path); - fprintf(stderr, "Linking file <%s> to <%s>\n", path2, path); - if (link(path2, path) < 0) { - fprintf(stderr, "Failed to link: %s\n", strerror(errno)); + fprintf(stderr, "COPY: Linking existing file <%s> to <%s>.\n", path2, path); + + if (copy_or_link_file(path2, path) < 0) return -1; - } - } else - fprintf(stderr, "MISSING FILE %s\n", path); + + } else { + const char* a; + + if ((a = package_get_item(cb_info->c1->package, d, 0))) { + fprintf(stderr, "COPY: Copying file <%s> from patch.\n", path); + + if (copy_or_link_file(a, path) < 0) + return -1; + } else + fprintf(stderr, "COPY: File <%s> is missing.\n", path); + } return 0; } -int delete_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) { +static int delete_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) { struct cb_info *cb_info = p; char path[PATH_MAX+1]; @@ -91,10 +122,11 @@ int delete_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void * return 0; snprintf(path, sizeof(path), "%s/%s", cb_info->root, name->path); - fprintf(stderr, "Deleting file <%s>\n", path); + + fprintf(stderr, "DELETE: Deleting file <%s>\n", path); if (unlink(path) < 0) { - fprintf(stderr, "Failed to unlink: %s\n", strerror(errno)); + fprintf(stderr, "unlink(%s): %s\n", path, strerror(errno)); return -1; } @@ -102,9 +134,10 @@ int delete_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void * } /* Merges c1 into c2 in directory "root" */ -int merge_snapshot(struct syrep_db_context *c1, struct syrep_db_context *c2, const char* root) { +int merge(struct syrep_db_context *c1, struct syrep_db_context *c2, const char* root) { struct cb_info cb_info; DB *ddb = NULL; + int r = -1; memset(&cb_info, 0, sizeof(cb_info)); cb_info.c1 = c1; @@ -122,11 +155,13 @@ int merge_snapshot(struct syrep_db_context *c1, struct syrep_db_context *c2, con if (diff_foreach(ddb, delete_phase, &cb_info) < 0) goto finish; + + r = 0; finish: if (ddb) ddb->close(ddb, 0); - return 0; + return r; } diff --git a/src/merge.h b/src/merge.h index 2b88712..17d3fc0 100644 --- a/src/merge.h +++ b/src/merge.h @@ -3,6 +3,6 @@ #include "context.h" -int merge_snapshot(struct syrep_db_context *c1, struct syrep_db_context *c2, const char* root); +int merge(struct syrep_db_context *c1, struct syrep_db_context *c2, const char* root); #endif diff --git a/src/package.c b/src/package.c index 5a741eb..8f8bd23 100644 --- a/src/package.c +++ b/src/package.c @@ -16,6 +16,7 @@ struct package_item; struct package_item { char *name; char *path; + int remove; struct package_item *next; }; @@ -61,19 +62,27 @@ static char *tmp(char *fn, int l) { return fn; } -static struct package_item *item_new(const char *name, const char *path) { +static struct package_item *item_new(const char *name, const char *path, int r) { struct package_item *i = NULL; - if (!(i = malloc(sizeof(struct package_item)))) - return NULL; + if (!(i = malloc(sizeof(struct package_item)))) { + fprintf(stderr, "malloc() failed: %s\n", strerror(errno)); + goto fail; + } memset(i, 0, sizeof(struct package_item)); - if (!(i->name = strdup(name))) + if (!(i->name = strdup(name))) { + fprintf(stderr, "strdup() failed: %s\n", strerror(errno)); goto fail; + } - if (!(i->path = strdup(path))) + if (!(i->path = strdup(path))) { + fprintf(stderr, "strdup() failed: %s\n", strerror(errno)); goto fail; + } + + i->remove = r; return i; @@ -148,7 +157,7 @@ struct package* package_open(const char *fn) { } fclose(d); - if (!(pi = item_new(name, path))) { + if (!(pi = item_new(name, path, 1))) { unlink(path); fprintf(stderr, "Failed to allocate memory.\n"); goto finish; @@ -183,9 +192,11 @@ int package_save(struct package *p, const char *fn) { int r = 0; struct package_item *i; - assert(p && fn); + assert(p); - if (!(f = fopen(fn, "w+"))) + if (!fn) + f = stdout; + else if (!(f = fopen(fn, "w+"))) return -1; for (i = p->items; i; i = i->next) { @@ -234,8 +245,9 @@ int package_save(struct package *p, const char *fn) { if (s) fclose(s); - - fclose(f); + + if (fn) + fclose(f); if (r != 0) unlink(fn); @@ -243,19 +255,21 @@ int package_save(struct package *p, const char *fn) { return r; } -const char *package_get_item(struct package* p, const char *name) { +const char *package_get_item(struct package* p, const char *name, int c) { struct package_item *i; char path[PATH_MAX]; assert(p && name); - for (i = p->items; i; i = i->next) { + for (i = p->items; i; i = i->next) if (strncmp(name, i->name, PACKAGE_ITEM_NAME_LEN) == 0) return i->path; - } + if (!c) + return NULL; + snprintf(path, sizeof(path), "%s/%i", p->base, p->count++); - if (!(i = item_new(name, path))) { + if (!(i = item_new(name, path, 1))) { unlink(path); return NULL; } @@ -271,6 +285,22 @@ const char *package_get_item(struct package* p, const char *name) { return i->path; } +int package_add_file(struct package *p, const char *name, const char *fn) { + struct package_item *i; + + if (!(i = item_new(name, fn, 0))) + return -1; + + if (p->last) { + p->last->next = i; + p->last = i; + } else + p->items = p->last = i; + + return 0; +} + + void package_remove(struct package *p) { struct package_item *i, *n; assert(p); @@ -278,7 +308,7 @@ void package_remove(struct package *p) { for (i = p->items; i; i = n) { n = i->next; - if (i->path) + if (i->remove && i->path) if (unlink(i->path)) if (errno != ENOENT) fprintf(stderr, "Failed to remove <%s>: %s\n", i->path, strerror(errno)); diff --git a/src/package.h b/src/package.h index 56a3d34..da9b571 100644 --- a/src/package.h +++ b/src/package.h @@ -8,7 +8,8 @@ struct package; struct package* package_open(const char *fn); void package_remove(struct package *p); int package_save(struct package *p, const char *fn); -const char *package_get_item(struct package* p, const char *name); +const char *package_get_item(struct package* p, const char *name, int c); +int package_add_file(struct package *p, const char *name, const char *fn); int package_foreach(struct package *p, int (*cb) (struct package *p, const char *name, const char *path, void *u), void *u); #endif diff --git a/src/syrep.c b/src/syrep.c index aaf98f4..ad7619a 100644 --- a/src/syrep.c +++ b/src/syrep.c @@ -27,10 +27,11 @@ #include "history.h" #include "dump.h" #include "extract.h" +#include "makepatch.h" struct gengetopt_args_info args; -int do_diff(void) { +static int do_diff(void) { struct syrep_db_context *c1 = NULL, *c2 = NULL; char *path1 = NULL, *path2 = NULL; DB *ddb = NULL; @@ -85,7 +86,7 @@ finish: return r; } -int do_merge(void) { +static int do_merge(void) { struct syrep_db_context *c1 = NULL, *c2 = NULL; char *path1 = NULL, *path2 = NULL; int r = 1; @@ -107,7 +108,60 @@ int do_merge(void) { goto finish; if (!strcmp(path1, path2)) { - fprintf(stderr, "ERROR: diff command requires two distinct snapshots as arguments\n"); + fprintf(stderr, "ERROR: merge command requires two distinct snapshots as arguments\n"); + goto finish; + } + + if (!(c1 = db_context_open(path1))) + goto finish; + + if (!(c2 = db_context_open(path2))) + goto finish; + + if (merge(c1, c2, args.inputs[1]) < 0) + goto finish; + + r = 0; + +finish: + if (c1) + db_context_free(c1); + + if (c2) + db_context_free(c2); + + if (path1) + free(path1); + + if (path2) + free(path2); + + return r; +} + +static int do_makepatch(void) { + struct syrep_db_context *c1 = NULL, *c2 = NULL; + char *path1 = NULL, *path2 = NULL; + int r = 1; + + if (args.inputs_num != 2) { + fprintf(stderr, "ERROR: Need exactly one repository and one repository snapshot for makepatch command\n"); + goto finish; + } + + if (isdirectory(args.inputs[0]) <= 0) { + fprintf(stderr, "ERROR: %s is not a directory\n", args.inputs[1]); + goto finish; + } + + if (!(path1 = strdup(get_attached_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME)))) + goto finish; + + if (!(path2 = strdup(get_attached_filename(args.inputs[1], SYREP_SNAPSHOTFILENAME)))) + goto finish; + + if (!strcmp(path1, path2)) { + fprintf(stderr, "ERROR: makepatch command requires two distinct snapshots as arguments\n"); goto finish; } @@ -117,7 +171,13 @@ int do_merge(void) { if (!(c2 = db_context_open(path2))) goto finish; - if (merge_snapshot(c1, c2, args.inputs[1]) < 0) + if (makepatch(c1, c2, args.inputs[0]) < 0) + goto finish; + + + if (!args.output_file_given && isatty(fileno(stdout))) + fprintf(stderr, "Sorry, I am not going to write the patch data to a tty.\n"); + else if (db_context_save(c1, args.output_file_given ? args.output_file_arg : NULL) < 0) goto finish; r = 0; @@ -138,7 +198,7 @@ finish: return r; } -int do_foreach(int (*func) (struct syrep_db_context *c), int m) { +static int do_foreach(int (*func) (struct syrep_db_context *c), int m) { char *path = NULL; int r = 1, i; struct syrep_db_context *c = NULL; @@ -206,7 +266,7 @@ finish: return r; } -int do_update(void) { +static int do_update(void) { char *path = NULL; int r = 1, i; struct syrep_db_context *c = NULL; @@ -321,6 +381,8 @@ int main(int argc, char *argv[]) { return do_foreach(dump, 0); else if (args.extract_flag) return do_foreach(extract, 1); + else if (args.makepatch_flag) + return do_makepatch(); cmdline_parser_print_help(); diff --git a/src/syrep.ggo b/src/syrep.ggo index 216a43d..91f9b2d 100644 --- a/src/syrep.ggo +++ b/src/syrep.ggo @@ -2,7 +2,7 @@ purpose "Synchronize Repositories" package "syrep" version "0.1" -option "verbose" - "Enable verbose operation" flag off +option "verbose" v "Enable verbose operation" flag off option "list" - "List a repository snapshot" flag off option "show-deleted" - "list: Show deleted entries of repository snapshot" flag off @@ -19,12 +19,15 @@ option "update" - "Update a repository snapshot" flag off option "no-cache" - "update: Don't use a message digest cache" flag off option "no-purge" - "update: Don't pruge obsolete entries from cache after update run" flag off option "ro-cache" - "update: Use read only cache" flag off - option "progress" - "update: Show progress" flag off + option "progress" p "update: Show progress" flag off option "diff" - "Show difference between two repositories or snapshots" flag off option "merge" - "Merge a snapshot or a repository into a repository" flag off - option "question" - "merge: Ask a question before each action" flag off + option "question" q "merge: Ask a question before each action" flag off + +option "makepatch" - "Make a patch against the specified repository" flag off + option "output-file" o "makepatch: Write output to specified file instead of STDOUT" string no option "extract" - "Extract the context of a snapshot or patch" flag off - option "output-directory" - "extract: Write output to specified directory" string no + option "output-directory" D "extract: Write output to specified directory" string no diff --git a/src/util.c b/src/util.c index 91954dd..ed93e6b 100644 --- a/src/util.c +++ b/src/util.c @@ -6,6 +6,9 @@ #include #include #include +#include +#include + #include "util.h" void statistics(DB *db) { @@ -104,3 +107,151 @@ int isdirectory(const char *path) { return !!S_ISDIR(st.st_mode); } + + +#define MMAPSIZE (100*1024*1024) +#define BUFSIZE (1024*1024) + +int copy_fd(int sfd, int dfd, uint32_t l) { + off_t src_o, dst_o; + void *src_p, *dst_p; + void *buf = NULL; + int r = -1; + struct stat st; + + if ((src_o = lseek(sfd, 0, SEEK_CUR)) == (off_t) -1) { + fprintf(stderr, "lseek(): %s\n", strerror(errno)); + goto finish; + } + + if (fstat(sfd, &st) < 0) { + fprintf(stderr, "fstat(): %s\n", strerror(errno)); + goto finish; + } + + if (l == (uint32_t) -1 || l >= st.st_size - src_o) + l = st.st_size - src_o; + + if ((dst_o = lseek(dfd, 0, SEEK_CUR)) == (off_t) -1) { + fprintf(stderr, "lseek(): %s\n", strerror(errno)); + goto finish; + } + + if (fstat(dfd, &st) < 0) { + fprintf(stderr, "fstat(): %s\n", strerror(errno)); + goto finish; + } + + if (dst_o+l > st.st_size) + if (ftruncate(dfd, dst_o+l) < 0) { + fprintf(stderr, "ftruncate(): %s\n", strerror(errno)); + goto finish; + } + + if (l > BUFSIZE) { + uint32_t m = l < MMAPSIZE ? l : MMAPSIZE; + + while (l > 0) { + if ((src_p = mmap(NULL, m, PROT_READ, MAP_SHARED, sfd, src_o)) == MAP_FAILED) + break; + + if ((dst_p = mmap(NULL, m, PROT_READ|PROT_WRITE, MAP_SHARED, dfd, dst_o)) == MAP_FAILED) { + munmap(src_p, m); + break; + } + + memcpy(dst_p, src_p, m); + + munmap(src_p, m); + munmap(dst_p, m); + + src_o += m; + dst_o += m; + l -= m; + + m = l < MMAPSIZE ? l : MMAPSIZE; + } + + + if (lseek(sfd, src_o, SEEK_SET) == (off_t) -1) { + fprintf(stderr, "lseek(): %s\n", strerror(errno)); + goto finish; + } + + if (lseek(dfd, dst_o, SEEK_SET) == (off_t) -1) { + fprintf(stderr, "lseek(): %s\n", strerror(errno)); + goto finish; + } + } + + if (l > 0) { + if (!(buf = malloc(BUFSIZE))) + goto finish; + + for (;;) { + ssize_t m; + + if ((m = read(sfd, buf, BUFSIZE)) < 0) { + fprintf(stderr, "read(): %s\n", strerror(errno)); + goto finish; + } + + if (!m) + break; + + if (write(dfd, buf, m) < 0) { + fprintf(stderr, "write(): %s\n", strerror(errno)); + goto finish; + } + } + } + + r = 0; + +finish: + + return r; +} + +int copy_file(const char *src, const char *dst) { + int sfd = -1, dfd = -1, r = -1; + + if ((sfd = open(src, O_RDONLY)) < 0) { + fprintf(stderr, "open(%s, O_RDONLY): %s\n", src, strerror(errno)); + goto finish; + } + + if ((dfd = open(dst, O_RDWR|O_TRUNC|O_CREAT, 0666)) < 0) { + fprintf(stderr, "open(%s, O_RDWR|O_TRUNC|O_CREAT): %s\n", dst, strerror(errno)); + goto finish; + } + + if (copy_fd(sfd, dfd, (uint32_t) -1) < 0) + goto finish; + + r = 0; + +finish: + + if (sfd >= 0) + close(sfd); + + if (dfd >= 0) + close(dfd); + + return r; +} + +int copy_or_link_file(const char *src, const char *dst) { + + if (link(src, dst) < 0) { + + if (errno == EXDEV || errno == EPERM) + return copy_file(src, dst); + + fprintf(stderr, "link(%s, %s): %s\n", src, dst, strerror(errno)); + return -1; + } + + return 0; +} diff --git a/src/util.h b/src/util.h index dbdb165..2460ab9 100644 --- a/src/util.h +++ b/src/util.h @@ -2,12 +2,16 @@ #define fooutilhfoo #include +#include void statistics(DB *db); char* normalize_path(char *s); void rotdash(void); const char* get_attached_filename(const char *path, const char *fn); int isdirectory(const char *path); +int copy_fd(int sfd, int dfd, uint32_t l); +int copy_file(const char *src, const char *dst); +int copy_or_link_file(const char *src, const char *dst); #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) diff --git a/test/Makefile b/test/Makefile index f5d2ae0..344a559 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,8 +1,12 @@ -SYREP=../syrep +SYREP=../src/syrep + +ifdef VERBOSE +VERBOSE=-v +endif %: - mkdir -p rep1 rep2 - $(SYREP) --show-deleted --show-times --$@ rep1 rep2 + @mkdir -p rep1 rep2 + $(SYREP) $(VERBOSE) --output-directory=extract -opatch --show-deleted --show-times --$@ rep1 rep2 all: update -- cgit