diff options
| -rw-r--r-- | doc/todo.txt | 25 | ||||
| -rw-r--r-- | src/Makefile | 5 | ||||
| -rw-r--r-- | src/cache.c | 1 | ||||
| -rw-r--r-- | src/cleanup.c | 92 | ||||
| -rw-r--r-- | src/cleanup.h | 26 | ||||
| -rw-r--r-- | src/context.c | 26 | ||||
| -rw-r--r-- | src/context.h | 4 | ||||
| -rw-r--r-- | src/diff.c | 12 | ||||
| -rw-r--r-- | src/diff.h | 7 | ||||
| -rw-r--r-- | src/md5util.c | 10 | ||||
| -rw-r--r-- | src/merge.c | 131 | ||||
| -rw-r--r-- | src/merge.h | 1 | ||||
| -rw-r--r-- | src/package.c | 95 | ||||
| -rw-r--r-- | src/package.h | 2 | ||||
| -rw-r--r-- | src/syrep.c | 104 | ||||
| -rw-r--r-- | src/syrep.ggo | 5 | ||||
| -rw-r--r-- | src/syrep.h | 2 | ||||
| -rw-r--r-- | src/update.c | 26 | ||||
| -rw-r--r-- | src/util.c | 153 | ||||
| -rw-r--r-- | src/util.h | 16 | ||||
| -rw-r--r-- | test/Makefile | 2 | 
21 files changed, 590 insertions, 155 deletions
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 <unistd.h> +#include <string.h> +#include <limits.h> +#include <errno.h> + +#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 @@ -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; @@ -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 <unistd.h>  #include <stdlib.h>  #include <errno.h> +#include <signal.h>  #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; @@ -30,12 +30,17 @@  #include <unistd.h>  #include <sys/mman.h>  #include <fcntl.h> +#include <ctype.h> +#include <dirent.h>  #ifdef USE_SENDFILE  #include <sys/sendfile.h>  #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; +} + @@ -24,13 +24,19 @@  #include <db.h>  #include <stdint.h> +#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  | 
