From db3b96fc0014a5e860df1efc729b38d629662826 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 29 Aug 2003 01:21:11 +0000 Subject: many fixes --cleanup implemented git-svn-id: file:///home/lennart/svn/public/syrep/trunk@13 07ea20a6-d2c5-0310-9e02-9ef735347d72 --- doc/todo.txt | 25 +++++++--- src/Makefile | 5 +- src/cache.c | 1 - src/cleanup.c | 92 +++++++++++++++++++++++++++++++++++ src/cleanup.h | 26 ++++++++++ src/context.c | 26 +++++++++- src/context.h | 4 +- src/diff.c | 12 ++++- src/diff.h | 7 ++- src/md5util.c | 10 +++- src/merge.c | 131 +++++++++++++++++++++++++++++-------------------- src/merge.h | 1 + src/package.c | 95 +++++++++++++++++++----------------- src/package.h | 2 +- src/syrep.c | 104 +++++++++++++++++++++++++++++---------- src/syrep.ggo | 5 ++ src/syrep.h | 2 + src/update.c | 26 +++++++--- src/util.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/util.h | 16 ++++-- test/Makefile | 2 +- 21 files changed, 590 insertions(+), 155 deletions(-) create mode 100644 src/cleanup.c create mode 100644 src/cleanup.h diff --git a/doc/todo.txt b/doc/todo.txt index 622ea0b..78e828e 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -1,20 +1,29 @@ +pre-0.1: - merge/makepatch DONE - Mode -T DONE - Recursive directory creation DONE - Prune empty directories DONE -- Handle conflicts +- Handle conflicts DONE - stat() before and after md5 as consistency check DONE +- -q mode DONE +- check of non-static symbols DONE +- warn if hostname != origin DONE +- signal handling DONE +- add --cleanup DONE +- package.c rewrite: add progressive, mmap, sendfile, gzip support, atomicity - better usage info on --help - documentation/man pages -- package.c rewrite: add progressive, mmap, sendfile, gzip support, atomicity -- -q mode -- url merges +- autoconf +- testing + +post-0.1: +- continue merge on copy failure +- url/ssh merges - bi-directory merges -- md5 before delete/replace/link -- Warn if hostname != origin +- digest check before delete/replace/link - some more asserts -- check of non-static symbols - PATH_MAX or PATH_MAX+1? -- explicit link or copy +- add --always-copy +- add --forget $Id$ diff --git a/src/Makefile b/src/Makefile index a7e55ec..2032e76 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,10 +1,9 @@ -CFLAGS=-Wall -pipe -O0 -g +CFLAGS=-Wall -pipe -O0 -g -DARCH_IS_BIG_ENDIAN=0 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 makepatch.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 cleanup.o $(CC) -g $^ -o $@ -ldb cmdline.c cmdline.h: syrep.ggo Makefile diff --git a/src/cache.c b/src/cache.c index 2e893f2..925cd69 100644 --- a/src/cache.c +++ b/src/cache.c @@ -183,7 +183,6 @@ int md_cache_get(struct syrep_md_cache *c, const char *path, uint8_t digest[16]) if ((j = get(c, &k, digest)) < 0) goto finish; - if (!j) if (fdmd5(fd, st.st_size, digest) < 0) goto finish; diff --git a/src/cleanup.c b/src/cleanup.c new file mode 100644 index 0000000..6f096f9 --- /dev/null +++ b/src/cleanup.c @@ -0,0 +1,92 @@ +/* $Id$ */ + +/*** + This file is part of syrep. + + syrep is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + syrep 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 syrep; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***/ + +#include +#include +#include +#include + +#include "syrep.h" +#include "util.h" + +int cleanup(const char *root) { + char p[PATH_MAX]; + + if (args.cleanup_level_arg >= 1) { + + if (args.verbose_flag) + fprintf(stderr, "Emptying trash ...\n"); + + snprintf(p, sizeof(p), "%s/.syrep/" SYREP_TRASHDIR, root); + + if (rm_rf(p, 0) < 0) + return -1; + + if (args.verbose_flag) + fprintf(stderr, "Removing temporary directories ...\n"); + + snprintf(p, sizeof(p), "%s/.syrep/" SYREP_TEMPDIR, root); + + if (rm_rf(p, 1) < 0) + return -1; + } + + if (args.cleanup_level_arg >= 2) { + + if (args.verbose_flag) + fprintf(stderr, "Removing digest cache ...\n"); + + snprintf(p, sizeof(p), "%s/.syrep/" SYREP_MDCACHEFILENAME, root); + + if (unlink(p) < 0) { + if (errno != ENOENT) { + fprintf(stderr, "unlink(\"%s\"): %s\n", p, strerror(errno)); + return -1; + } + } + } + + if (args.cleanup_level_arg >= 3) { + + if (args.verbose_flag) + fprintf(stderr, "Removing status data ...\n"); + + snprintf(p, sizeof(p), "%s/.syrep/" SYREP_SNAPSHOTFILENAME, root); + + if (unlink(p) < 0) { + if (errno != ENOENT) { + fprintf(stderr, "unlink(\"%s\"): %s\n", p, strerror(errno)); + return -1; + } + } + + snprintf(p, sizeof(p), "%s/.syrep", root); + + if (rmdir(p) < 0) { + if (errno != ENOENT) { + fprintf(stderr, "rmdir(\"%s\"): %s\n", p, strerror(errno)); + return -1; + } + } + } + + + return 0; +} diff --git a/src/cleanup.h b/src/cleanup.h new file mode 100644 index 0000000..51fd15b --- /dev/null +++ b/src/cleanup.h @@ -0,0 +1,26 @@ +#ifndef foocleanuphfoo +#define foocleanuphfoo + +/* $Id$ */ + +/*** + This file is part of syrep. + + syrep is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + syrep 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 syrep; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***/ + +int cleanup(const char *root); + +#endif diff --git a/src/context.c b/src/context.c index f64040a..7052bad 100644 --- a/src/context.c +++ b/src/context.c @@ -29,6 +29,7 @@ #include "context.h" #include "package.h" +#include "util.h" int db_context_free(struct syrep_db_context* c) { if (c) { @@ -87,7 +88,7 @@ static DB* open_db(const char*path, int dup) { return db; } -struct syrep_db_context* db_context_open(const char *filename) { +struct syrep_db_context* db_context_open(const char *filename, int force) { struct syrep_db_context *c = NULL; const char* path; FILE *f; @@ -97,7 +98,7 @@ struct syrep_db_context* db_context_open(const char *filename) { memset(c, 0, sizeof(struct syrep_db_context)); - if (!(c->package = package_open(filename))) + if (!(c->package = package_open(filename, force))) goto fail; path = package_get_item(c->package, "timestamp", 1); @@ -217,3 +218,24 @@ int db_context_save(struct syrep_db_context *c, const char *filename) { return package_save(c->package, filename); } + +int db_context_origin_warn(struct syrep_db_context *c) { + char hn[256]; + + assert(c); + + if (gethostname(hn, sizeof(hn)) < 0) + return -1; + + if (strcmp(hn, c->origin)) { + int q; + + if (!(q = question("WARNING: Snapshot is not from local host! Continue?", "ny"))) + return -1; + + if (q != 'y') + return 1; + } + + return 0; +} diff --git a/src/context.h b/src/context.h index e4617c9..96f4dcf 100644 --- a/src/context.h +++ b/src/context.h @@ -41,8 +41,10 @@ struct syrep_db_context { char* origin; }; -struct syrep_db_context* db_context_open(const char *path); +struct syrep_db_context* db_context_open(const char *path, int force); int db_context_save(struct syrep_db_context *c, const char *path); int db_context_free(struct syrep_db_context* c); +int db_context_origin_warn(struct syrep_db_context *c); + #endif diff --git a/src/diff.c b/src/diff.c index a9a20bd..54185b2 100644 --- a/src/diff.c +++ b/src/diff.c @@ -182,10 +182,13 @@ static int enumerate(DB *ddb, struct syrep_db_context *c1, struct syrep_db_conte struct syrep_id *id = (struct syrep_id*) key.data; struct syrep_meta *meta = (struct syrep_meta*) data.data; - - assert(id && meta); + if (interrupted) { + fprintf(stderr, "Canceled.\n"); + goto finish; + } + if (meta->last_seen != c1->version) continue; @@ -344,6 +347,11 @@ int diff_foreach(DB *ddb, int (*cb)(DB *ddb, struct syrep_name *name, struct dif struct syrep_name *name = (struct syrep_name*) key.data; struct diff_entry *de = (struct diff_entry*) data.data; + if (interrupted) { + fprintf(stderr, "Canceled.\n"); + goto finish; + } + if ((t = cb(ddb, name, de, p)) < 0) { r = t; goto finish; diff --git a/src/diff.h b/src/diff.h index a746092..2c1c134 100644 --- a/src/diff.h +++ b/src/diff.h @@ -24,7 +24,12 @@ #include "context.h" #include "dbstruct.h" -enum { DIFF_COPY, DIFF_DELETE, DIFF_CONFLICT, DIFF_IGNORE }; +/* + * Please note that DIFF_REPLACE is never set by make_diff(). Instead + * it is used for conflict resolution in merge.c + */ + +enum { DIFF_COPY, DIFF_DELETE, DIFF_CONFLICT, DIFF_REPLACE }; struct diff_entry { int action; diff --git a/src/md5util.c b/src/md5util.c index e4c3269..6b9a1e2 100644 --- a/src/md5util.c +++ b/src/md5util.c @@ -28,6 +28,7 @@ #include "md5util.h" #include "md5.h" +#include "syrep.h" void fhex(const unsigned char *bin,int len, char *txt) { const static char hex[] = "0123456789abcdef"; @@ -46,7 +47,7 @@ int fdmd5(int fd, size_t l, char *md) { void *d; off_t o = 0; size_t m; - int r = 0; + int r = -1; md5_state_t s; struct stat pre, post; void *p = NULL; @@ -65,6 +66,11 @@ int fdmd5(int fd, size_t l, char *md) { while (l && ((d = mmap(NULL, m, PROT_READ, MAP_SHARED, fd, o)) != MAP_FAILED)) { md5_append(&s, d, m); munmap(d, m); + + if (interrupted) { + fprintf(stderr, "Canceled.\n"); + goto finish; + } o += m; l -= m; @@ -112,6 +118,8 @@ int fdmd5(int fd, size_t l, char *md) { md5_finish(&s, md); + r = 0; + finish: if (p) diff --git a/src/merge.c b/src/merge.c index c37cb53..b281b06 100644 --- a/src/merge.c +++ b/src/merge.c @@ -44,6 +44,7 @@ struct cb_info { 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 writeback = 0; int f1, f2; char path[PATH_MAX+1]; @@ -62,18 +63,73 @@ static int conflict_phase(DB *ddb, struct syrep_name *name, struct diff_entry *d if (f1 && f2) { char d1[SYREP_DIGESTLENGTH*2+1], d2[SYREP_DIGESTLENGTH*2+1]; + int q; 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); + fprintf(stderr, "CONFLICT: New file '%s': %s in *local* vs. %s in *remote* repository.\n", path, d2, d1); + + if (!(q = question("Replace in local repository?", "ny"))) + return -1; + + if (q == 'y') { + de->action = DIFF_REPLACE; + de->repository = cb_info->c1; + writeback = 1; + } + } 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'); + fprintf(stderr, "CONFLICT: File '%s' (%s) appeared in *%s*, removed in *%s* repository.\n", path, d, f1 ? "remote" : "local", f2 ? "remote" : "local"); + + if (f1) { + int q; + + if (!(q = question("Add to local repository?", "yn"))) + return -1; + + if (q == 'y') { + de->action = DIFF_COPY; + de->repository = cb_info->c1; + writeback = 1; + } + } else { + int q; + + if (!(q = question("Remove from local repository?", "ny"))) + return -1; + + if (q == 'y') { + de->action = DIFF_DELETE; + de->repository = cb_info->c2; + writeback = 1; + } + } + } + + if (writeback) { + DBT key, data; + int ret; + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + key.data = (void*) name; + key.size = sizeof(struct syrep_name); + + data.data = (void*) de; + data.size = sizeof(struct diff_entry); + + if ((ret = ddb->put(ddb, NULL, &key, &data, 0))) { + ddb->err(ddb, ret, "ddb::put()"); + return -1; + } } return 0; @@ -112,7 +168,7 @@ static int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, v assert(ddb && name && de && p); - if (de->action != DIFF_COPY) + if (de->action != DIFF_COPY && de->action != DIFF_REPLACE) return 0; if (de->repository == cb_info->c2) @@ -153,7 +209,7 @@ static int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, v escape_path(name2.path, path2+l, sizeof(path2)-l); if (!access(path2, R_OK)) { - if (copy_or_link_file(path2, path, 0) < 0) + if (copy_or_link_file(path2, path, de->action == DIFF_REPLACE) < 0) return -1; } @@ -169,7 +225,7 @@ static int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, v if (makeprefixpath(path, 0777) < 0) return -1; - if (copy_or_link_file(a, path, 0) < 0) + if (copy_or_link_file(a, path, de->action == DIFF_REPLACE) < 0) return -1; } else fprintf(stderr, "COPY: File <%s> is missing.\n", name->path); @@ -191,6 +247,19 @@ static int delete_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, if (de->repository == cb_info->c1) return 0; + if (args.question_flag) { + char text[256]; + int q; + + snprintf(text, sizeof(text), "Delete %s?", name->path); + + if ((q = question(text, "yn")) < 0) + return -1; + + if (q != 'y') + return 0; + } + snprintf(path, sizeof(path), "%s/%s", cb_info->root, name->path); snprintf(target, sizeof(target), "%s/", cb_info->trash_dir); @@ -209,47 +278,6 @@ static int delete_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, return 0; } -static int empty_trash(const char *trash) { - DIR *dir = NULL; - int r = -1; - struct dirent *de; - - if (!(dir = opendir(trash))) { - fprintf(stderr, "opendir(\"%s\"): %s", trash, strerror(errno)); - goto finish; - } - - while ((de = readdir(dir))) { - char path[PATH_MAX]; - - if (!strcmp(de->d_name, ".")) - continue; - - if (!strcmp(de->d_name, "..")) - continue; - - snprintf(path, sizeof(path), "%s/%s", trash, de->d_name); - - if (unlink(path) < 0) { - fprintf(stderr, "unlink(\"%s\"): %s\n", path, strerror(errno)); - goto finish; - } - } - - if (rmdir(trash) < 0) { - fprintf(stderr, "rmdir(\"%s\"): %s\n", trash, strerror(errno)); - goto finish; - } - - r = 0; - -finish: - if (dir) - closedir(dir); - - return r; -} - /* Merges c1 into c2 in directory "root" */ int merge(struct syrep_db_context *c1, struct syrep_db_context *c2, const char* root) { struct cb_info cb_info; @@ -260,9 +288,9 @@ int merge(struct syrep_db_context *c1, struct syrep_db_context *c2, const char* cb_info.c1 = c1; cb_info.c2 = c2; cb_info.root = root; - - snprintf(cb_info.trash_dir, sizeof(cb_info.trash_dir), "%s/.syrep-trash", root); - mkdir(cb_info.trash_dir, 0777); + + snprintf(cb_info.trash_dir, sizeof(cb_info.trash_dir), "%s/.syrep/trash", root); + mkdir_p(cb_info.trash_dir, 0777); if (!(ddb = make_diff(c1, c2))) goto finish; @@ -276,8 +304,9 @@ int merge(struct syrep_db_context *c1, struct syrep_db_context *c2, const char* if (diff_foreach(ddb, copy_phase, &cb_info) < 0) goto finish; - if (empty_trash(cb_info.trash_dir) < 0) - goto finish; + if (!args.keep_trash_flag) + if (rm_rf(cb_info.trash_dir, 0) < 0) + goto finish; r = 0; diff --git a/src/merge.h b/src/merge.h index 4e5b766..02c1a9e 100644 --- a/src/merge.h +++ b/src/merge.h @@ -24,5 +24,6 @@ #include "context.h" int merge(struct syrep_db_context *c1, struct syrep_db_context *c2, const char* root); +int empty_trash(const char *trash); #endif diff --git a/src/package.c b/src/package.c index 464918c..2c396be 100644 --- a/src/package.c +++ b/src/package.c @@ -118,7 +118,7 @@ fail: return NULL; } -struct package* package_open(const char *fn) { +struct package* package_open(const char *fn, int force) { struct package *p; FILE *f = NULL; char path[PATH_MAX]; @@ -143,56 +143,63 @@ struct package* package_open(const char *fn) { if (fn) { if (!(f = fopen(fn, "r"))) { - fprintf(stderr, "Failed to open <%s>: %s\n", fn, strerror(errno)); - goto finish; - } - - for (;;) { - uint32_t len; - char name[PACKAGE_ITEM_NAME_LEN+1]; - FILE *d = NULL; - struct package_item *pi; - - if (!fread(name, sizeof(name)-1, 1, f)) - break; - name[sizeof(name)-1] = 0; - - if (fread(&len, sizeof(len), 1, f) != 1) { - fprintf(stderr, "Short read while reading length: %s\n", strerror(errno)); + if (errno != ENOENT || !force) { + fprintf(stderr, "Failed to open <%s>: %s\n", fn, strerror(errno)); goto finish; } + } - snprintf(path, sizeof(path), "%s/%i", p->base, p->count++); - if (!(d = fopen(path, "w+"))) { - fprintf(stderr, "Failed to open target file: %s\n", strerror(errno)); - goto finish; - } + if (f) { + + for (;;) { + uint32_t len; + char name[PACKAGE_ITEM_NAME_LEN+1]; + FILE *d = NULL; + struct package_item *pi; + + if (!fread(name, sizeof(name)-1, 1, f)) + break; + + name[sizeof(name)-1] = 0; + + if (fread(&len, sizeof(len), 1, f) != 1) { + fprintf(stderr, "Short read while reading length: %s\n", strerror(errno)); + goto finish; + } + + snprintf(path, sizeof(path), "%s/%i", p->base, p->count++); + if (!(d = fopen(path, "w+"))) { + fprintf(stderr, "Failed to open target file: %s\n", strerror(errno)); + goto finish; + } + + if (copy(f, d, len) < 0) { + fprintf(stderr, "Copy failed: %s\n", strerror(errno)); + fclose(d); + unlink(path); + goto finish; + } - if (copy(f, d, len) < 0) { - fprintf(stderr, "Copy failed: %s\n", strerror(errno)); fclose(d); - unlink(path); - goto finish; - } + if (!(pi = item_new(name, path, 1))) { + unlink(path); + fprintf(stderr, "Failed to allocate memory.\n"); + goto finish; + } - fclose(d); - if (!(pi = item_new(name, path, 1))) { - unlink(path); - fprintf(stderr, "Failed to allocate memory.\n"); - goto finish; + assert(!!p->last == !!p->items); + + if (p->last) { + p->last->next = pi; + p->last = pi; + } else + p->items = p->last = pi; } - assert(!!p->last == !!p->items); - - if (p->last) { - p->last->next = pi; - p->last = pi; - } else - p->items = p->last = pi; - } - fclose(f); + fclose(f); + } } return p; @@ -339,8 +346,10 @@ void package_remove(struct package *p) { } if (p->base) { - if (rmdir(p->base)) - fprintf(stderr, "Failed to remove <%s>: %s\n", p->base, strerror(errno)); + if (rmdir(p->base)) { + if (errno != ENOENT) + fprintf(stderr, "Failed to remove <%s>: %s\n", p->base, strerror(errno)); + } free(p->base); } diff --git a/src/package.h b/src/package.h index 57b2daf..9a7bfd2 100644 --- a/src/package.h +++ b/src/package.h @@ -25,7 +25,7 @@ struct package; -struct package* package_open(const char *fn); +struct package* package_open(const char *fn, int force); 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, int c); diff --git a/src/syrep.c b/src/syrep.c index fadb7e5..f95f086 100644 --- a/src/syrep.c +++ b/src/syrep.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "md5util.h" #include "md5.h" @@ -48,6 +49,9 @@ #include "dump.h" #include "extract.h" #include "makepatch.h" +#include "cleanup.h" + +volatile int interrupted = 0; struct gengetopt_args_info args; @@ -63,15 +67,15 @@ static int do_diff(void) { } if (args.local_temp_flag && isdirectory(args.inputs[0]) >= 1) { - const char *p = get_attached_filename(args.inputs[0], SYREP_TEMPDIR); + const char *p = get_snapshot_filename(args.inputs[0], SYREP_TEMPDIR); mkdir(p, 0777); setenv("TMPDIR", p, 1); } - if (!(path1 = strdup(get_attached_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME)))) + if (!(path1 = strdup(get_snapshot_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME)))) goto finish; - if (!(path2 = strdup(get_attached_filename(args.inputs[1], SYREP_SNAPSHOTFILENAME)))) + if (!(path2 = strdup(get_snapshot_filename(args.inputs[1], SYREP_SNAPSHOTFILENAME)))) goto finish; if (!strcmp(path1, path2)) { @@ -79,10 +83,10 @@ static int do_diff(void) { goto finish; } - if (!(c1 = db_context_open(path1))) + if (!(c1 = db_context_open(path1, 0))) goto finish; - if (!(c2 = db_context_open(path2))) + if (!(c2 = db_context_open(path2, 0))) goto finish; if (!(ddb = make_diff(c1, c2))) @@ -128,15 +132,15 @@ static int do_merge(void) { } if (args.local_temp_flag) { - const char *p = get_attached_filename(args.inputs[1], SYREP_TEMPDIR); + const char *p = get_snapshot_filename(args.inputs[1], SYREP_TEMPDIR); mkdir(p, 0777); setenv("TMPDIR", p, 1); } - if (!(path1 = strdup(get_attached_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME)))) + if (!(path1 = strdup(get_snapshot_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME)))) goto finish; - if (!(path2 = strdup(get_attached_filename(args.inputs[1], SYREP_SNAPSHOTFILENAME)))) + if (!(path2 = strdup(get_snapshot_filename(args.inputs[1], SYREP_SNAPSHOTFILENAME)))) goto finish; if (!strcmp(path1, path2)) { @@ -144,10 +148,13 @@ static int do_merge(void) { goto finish; } - if (!(c1 = db_context_open(path1))) + if (!(c1 = db_context_open(path1, 0))) goto finish; - if (!(c2 = db_context_open(path2))) + if (!(c2 = db_context_open(path2, 0))) + goto finish; + + if (db_context_origin_warn(c2)) goto finish; if (merge(c1, c2, args.inputs[1]) < 0) @@ -187,15 +194,15 @@ static int do_makepatch(void) { } if (args.local_temp_flag) { - const char *p = get_attached_filename(args.inputs[0], SYREP_TEMPDIR); + const char *p = get_snapshot_filename(args.inputs[0], SYREP_TEMPDIR); mkdir(p, 0777); setenv("TMPDIR", p, 1); } - if (!(path1 = strdup(get_attached_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME)))) + if (!(path1 = strdup(get_snapshot_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME)))) goto finish; - if (!(path2 = strdup(get_attached_filename(args.inputs[1], SYREP_SNAPSHOTFILENAME)))) + if (!(path2 = strdup(get_snapshot_filename(args.inputs[1], SYREP_SNAPSHOTFILENAME)))) goto finish; if (!strcmp(path1, path2)) { @@ -203,12 +210,15 @@ static int do_makepatch(void) { goto finish; } - if (!(c1 = db_context_open(path1))) + if (!(c1 = db_context_open(path1, 0))) goto finish; - if (!(c2 = db_context_open(path2))) + if (db_context_origin_warn(c1)) goto finish; + if (!(c2 = db_context_open(path2, 0))) + goto finish; + if (makepatch(c1, c2, args.inputs[0]) < 0) goto finish; @@ -248,15 +258,15 @@ static int do_foreach(int (*func) (struct syrep_db_context *c), int m) { static char saved_cwd[PATH_MAX]; if (args.local_temp_flag && isdirectory(args.inputs[i]) >= 1) { - const char *p = get_attached_filename(args.inputs[i], SYREP_TEMPDIR); + const char *p = get_snapshot_filename(args.inputs[i], SYREP_TEMPDIR); mkdir(p, 0777); setenv("TMPDIR", p, 1); } - if (!(path = strdup(get_attached_filename(args.inputs[i], SYREP_SNAPSHOTFILENAME)))) + if (!(path = strdup(get_snapshot_filename(args.inputs[i], SYREP_SNAPSHOTFILENAME)))) goto finish; - if (!(c = db_context_open(path))) + if (!(c = db_context_open(path, 0))) goto finish; if (args.inputs_num > 1) @@ -333,26 +343,26 @@ static int do_update(void) { } if (args.local_temp_flag) { - const char *p = get_attached_filename(args.inputs[i], SYREP_TEMPDIR); + const char *p = get_snapshot_filename(args.inputs[i], SYREP_TEMPDIR); mkdir(p, 0777); setenv("TMPDIR", p, 1); } - if (!(path = strdup(get_attached_filename(args.inputs[i], SYREP_SNAPSHOTFILENAME)))) + if (!(path = strdup(get_snapshot_filename(args.inputs[i], SYREP_SNAPSHOTFILENAME)))) goto finish; - if (!(c = db_context_open(path))) { - fprintf(stderr, "Initializing repository.\n"); - if (!(c = db_context_open(NULL))) - goto finish; - } + if (!(c = db_context_open(path, 1))) + goto finish; + + if (db_context_origin_warn(c)) + goto finish; if (!args.no_cache_flag) { const char *p; if (args.cache_given) cache = md_cache_open(args.cache_arg, args.ro_cache_flag); - else if ((p = get_attached_filename(args.inputs[i], SYREP_MDCACHEFILENAME))) + else if ((p = get_snapshot_filename(args.inputs[i], SYREP_MDCACHEFILENAME))) cache = md_cache_open(p, args.ro_cache_flag); } @@ -407,8 +417,48 @@ finish: return r; } +static int do_cleanup(void) { + int r = 1, i; + + if (args.inputs_num < 1) + fprintf(stderr, "WARNING: No repository specified!\n"); + + for (i = 0; i < args.inputs_num; i++) { + + if (isdirectory(args.inputs[i]) <= 0) { + fprintf(stderr, "%s is not a directory\n", args.inputs[i]); + goto finish; + } + + if (args.inputs_num > 1) + fprintf(stderr, "*** %s ***\n", args.inputs[i]); + + if (cleanup(args.inputs[i]) < 0) + goto finish; + + if (args.inputs_num > 1 && i < args.inputs_num-1) + fprintf(stderr, "\n"); + } + + r = 0; + +finish: + + return r; + +} + +static void sigint(int s) { + interrupted = 1; +} + int main(int argc, char *argv[]) { char *b; + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigint; + sigaction(SIGINT, &sa, NULL); if ((b = strrchr(argv[0], '/'))) argv[0] = b+1; @@ -433,6 +483,8 @@ int main(int argc, char *argv[]) { return do_foreach(extract, 1); else if (args.makepatch_flag) return do_makepatch(); + else if (args.cleanup_flag) + return do_cleanup(); cmdline_parser_print_help(); diff --git a/src/syrep.ggo b/src/syrep.ggo index e0a5f02..b3e16ef 100644 --- a/src/syrep.ggo +++ b/src/syrep.ggo @@ -22,6 +22,7 @@ version "0.1" option "verbose" v "Enable verbose operation" flag off option "local-temp" T "Use temporary directory inside repository" flag off +option "ignore-origin" - "update: Don't warn if snapshot not local in update, merge, makepatch" flag off option "list" - "List a repository snapshot" flag off option "show-deleted" - "list: Show deleted entries of repository snapshot" flag off @@ -45,9 +46,13 @@ 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" q "merge: Ask a question before each action" flag off option "prune-empty" - "merge: Prune empty directories" flag off + option "keep-trash" - "merge: Don't empty trash" 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" D "extract: Write output to specified directory" string no + +option "cleanup" - "Remove syrep info from repository" flag off + option "cleanup-level" - "cleanup: 1 - just remove temporary data and trash (default); 2 - remove MD cache as well; 3 - remove all syrep data" int default="1" no diff --git a/src/syrep.h b/src/syrep.h index 29b709d..f3f4d5e 100644 --- a/src/syrep.h +++ b/src/syrep.h @@ -32,4 +32,6 @@ extern struct gengetopt_args_info args; +extern volatile int interrupted; + #endif diff --git a/src/update.c b/src/update.c index f063686..e0e7f9c 100644 --- a/src/update.c +++ b/src/update.c @@ -126,6 +126,7 @@ static int handle_file(struct syrep_db_context *c, uint32_t version, const char } static int iterate_dir(struct syrep_db_context *c, struct syrep_md_cache *cache, uint32_t version, const char *root) { + int r = -1; DIR *dir; struct dirent *de; char p[PATH_MAX]; @@ -143,6 +144,11 @@ static int iterate_dir(struct syrep_db_context *c, struct syrep_md_cache *cache, if (!strncmp(de->d_name, ".syrep", 6)) continue; + if (interrupted) { + fprintf(stderr, "Canceled.\n"); + goto finish; + } + if (args.progress_flag) rotdash(); @@ -157,21 +163,24 @@ static int iterate_dir(struct syrep_db_context *c, struct syrep_md_cache *cache, if (S_ISDIR(st.st_mode)) { if (iterate_dir(c, cache, version, p) < 0) - fprintf(stderr, "iterate_dir(%s) failed: %s\n", p, strerror(errno)); + goto finish; + } else if (S_ISREG(st.st_mode)) { if (md_cache_get(cache, p, md.digest) < 0) - continue; + goto finish; - if ((handle_file(c, version, p, &md)) < 0) { - fprintf(stderr, "handle_file(%s) failed.\n", p); - return -1; - } + if ((handle_file(c, version, p, &md)) < 0) + goto finish; } } + + r = 0; + +finish: closedir(dir); - return 0; + return r; } static int new_version(struct syrep_db_context *c, uint32_t v, uint32_t t) { @@ -217,6 +226,9 @@ int update(struct syrep_db_context *c, struct syrep_md_cache *cache) { if (iterate_dir(c, cache, version, ".") < 0) return -1; + if (args.progress_flag) + rotdash_hide(); + if (new_version(c, version, now) < 0) return -1; diff --git a/src/util.c b/src/util.c index 2a4eb59..f0d66d1 100644 --- a/src/util.c +++ b/src/util.c @@ -30,12 +30,17 @@ #include #include #include +#include +#include #ifdef USE_SENDFILE #include #endif #include "util.h" +#include "syrep.h" + +static int stderr_tty = -1; void statistics(DB *db) { DB_BTREE_STAT *statp; @@ -90,7 +95,10 @@ void rotdash(void) { static const char dashes[] = /* ".oOo"; */ "|/-\\"; const static char *d = dashes; - if (isatty(fileno(stderr))) { + if (stderr_tty < 0) + stderr_tty = isatty(fileno(stderr)); + + if (stderr_tty) { fprintf(stderr, "%c\b", *d); d++; @@ -99,7 +107,15 @@ void rotdash(void) { } } -const char* get_attached_filename(const char *path, const char *fn) { +void rotdash_hide(void) { + if (stderr_tty < 0) + stderr_tty = isatty(fileno(stderr)); + + if (stderr_tty) + fputs(" \b", stderr); +} + +const char* get_snapshot_filename(const char *path, const char *fn) { static char npath[PATH_MAX]; struct stat st; @@ -213,6 +229,11 @@ int copy_fd(int sfd, int dfd, uint32_t l) { munmap(src_p, m); munmap(dst_p, m); + + if (interrupted) { + fprintf(stderr, "Canceled.\n"); + goto finish; + } src_o += m; dst_o += m; @@ -243,6 +264,11 @@ int copy_fd(int sfd, int dfd, uint32_t l) { for (;;) { ssize_t m; + if (interrupted) { + fprintf(stderr, "Canceled.\n"); + goto finish; + } + if ((m = read(sfd, buf, BUFSIZE)) < 0) { fprintf(stderr, "read(): %s\n", strerror(errno)); goto finish; @@ -421,3 +447,126 @@ int makeprefixpath(const char *path, mode_t m) { return mkdir_p(tmp, m); } + +int question(const char *text, const char *replies) { + int r = 0; + FILE *f; + + assert(text && replies && *replies); + + if (!(f = fopen("/dev/tty", "r"))) + f = stdin; + + for (;;) { + const char *q; + char reply[256]; + + if (interrupted) { + fprintf(stderr, "Canceled.\n"); + goto finish; + } + + fprintf(stderr, "\r%s [", text); + + for (q = replies; *q; q++) { + fputc(q == replies ? toupper(*q) : tolower(*q), stderr); + + if (*(q+1)) + fputc('|', stderr); + } + + fprintf(stderr, "] "); + + if (!fgets(reply, sizeof(reply), f)) + goto finish; + + if (reply[0] == '\r' || reply[0] == '\n') + reply[0] = *replies; + + reply[0] = tolower(reply[0]); + + for (q = replies; *q; q++) + + if (tolower(*q) == reply[0]) { + r = *q; + goto finish; + } + } + + +finish: + + fputc('\r', stderr); + + if (f != stdin) + fclose(f); + + return r; + +} + + +int rm_rf(const char *root, int rec) { + DIR *dir = NULL; + int r = -1; + struct dirent *de; + + if (!(dir = opendir(root))) { + + if (errno == ENOENT) { + r = 0; + goto finish; + } + + fprintf(stderr, "opendir(\"%s\"): %s", root, strerror(errno)); + goto finish; + } + + while ((de = readdir(dir))) { + char path[PATH_MAX]; + + if (!strcmp(de->d_name, ".")) + continue; + + if (!strcmp(de->d_name, "..")) + continue; + + snprintf(path, sizeof(path), "%s/%s", root, de->d_name); + + if (rec) { + struct stat st; + + if (stat(path, &st) < 0) { + fprintf(stderr, "stat(\"%s\"): %s\n", path, strerror(errno)); + goto finish; + } + + if (S_ISDIR(st.st_mode)) { + + if (rm_rf(path, rec) < 0) + return -1; + + continue; + } + } + + if (unlink(path) < 0) { + fprintf(stderr, "unlink(\"%s\"): %s\n", path, strerror(errno)); + goto finish; + } + } + + if (rmdir(root) < 0) { + fprintf(stderr, "rmdir(\"%s\"): %s\n", root, strerror(errno)); + goto finish; + } + + r = 0; + +finish: + if (dir) + closedir(dir); + + return r; +} + diff --git a/src/util.h b/src/util.h index 2aa7215..a44c598 100644 --- a/src/util.h +++ b/src/util.h @@ -24,13 +24,19 @@ #include #include +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + void statistics(DB *db); char* normalize_path(char *s); void rotdash(void); -const char* get_attached_filename(const char *path, const char *fn); +void rotdash_hide(void); +const char* get_snapshot_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 c); +int move_file(const char *src, const char *dst, int c); int copy_or_link_file(const char *src, const char *dst, int c); /* Remove all directories between path and root if they are empty. */ @@ -42,10 +48,10 @@ int mkdir_p(const char *path, mode_t m); /* Create all leading directories in path */ int makeprefixpath(const char *path, mode_t m); -int move_file(const char *src, const char *dst, int c); +int question(const char *q, const char *resp); + +/* Same as /bin/rm -rf in the shell */ +int rm_rf(const char *root, int rec); -#ifndef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) -#endif #endif diff --git a/test/Makefile b/test/Makefile index 491c59a..fc92e0e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -31,7 +31,7 @@ mergepatch: clean: mrproper: - rm -rf rep?/.syrep* + $(SYREP) -v --cleanup-level=3 --cleanup rep1 rep2 .PHONY: all merge clean mrproper mergepatch -- cgit