diff options
author | Lennart Poettering <lennart@poettering.net> | 2003-08-28 22:28:36 +0000 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2003-08-28 22:28:36 +0000 |
commit | 38cea8764bd4302bfa6f16b086b04dfa25be344b (patch) | |
tree | f3bc9c133bddbd905f1d7f7221ea70a5f0eb3b91 | |
parent | 98f36517251b5bccdb80d90d698c173c18ee4a29 (diff) |
directory merge fixes
git-svn-id: file:///home/lennart/svn/public/syrep/trunk@10 07ea20a6-d2c5-0310-9e02-9ef735347d72
-rw-r--r-- | src/extract.c | 23 | ||||
-rw-r--r-- | src/md5util.c | 104 | ||||
-rw-r--r-- | src/merge.c | 129 | ||||
-rw-r--r-- | src/syrep.c | 30 | ||||
-rw-r--r-- | src/syrep.ggo | 2 | ||||
-rw-r--r-- | src/syrep.h | 2 | ||||
-rw-r--r-- | src/util.c | 158 | ||||
-rw-r--r-- | src/util.h | 15 |
8 files changed, 391 insertions, 72 deletions
diff --git a/src/extract.c b/src/extract.c index 616b8d1..5ba7869 100644 --- a/src/extract.c +++ b/src/extract.c @@ -12,22 +12,19 @@ #include "util.h" static int cb(struct package *p, const char *name, const char *path, void *u) { - struct stat st; - uint32_t size; - - if (stat(path, &st) < 0) { + int r; + + if ((r = access(path, R_OK)) < 0) { if (errno == ENOENT) - size = 0; - else { - fprintf(stderr, "stat(%s) failed: %s\n", path, strerror(errno)); - return -1; - } - } else - size = (uint32_t) st.st_size; + return 0; + + fprintf(stderr, "stat(%s) failed: %s\n", path, strerror(errno)); + return -1; + } - if (size) { + if (r == 0) { fprintf(stderr, "Extracting %s ...\n", name); - return copy_file(path, name); + return copy_or_link_file(path, name, 1); } return 0; diff --git a/src/md5util.c b/src/md5util.c index a49e5d5..fcbf440 100644 --- a/src/md5util.c +++ b/src/md5util.c @@ -1,20 +1,19 @@ /*** - This file is part of pam_dotfile. + This file is part of syrep. - pam_dotfile is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by + 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. - pam_dotfile 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. + 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 pam_dotfile; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA + 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> @@ -23,6 +22,7 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <sys/stat.h> #include "md5util.h" #include "md5.h" @@ -46,48 +46,74 @@ int fdmd5(int fd, size_t l, char *md) { size_t m; int r = 0; md5_state_t s; + struct stat pre, post; + void *p = NULL; md5_init(&s); - m = l < MMAPSIZE ? l : MMAPSIZE; + if (fstat(fd, &pre) < 0) { + fprintf(stderr, "fstat(): %s\n", strerror(errno)); + goto finish; + } - while (l > 0 && ((d = mmap(NULL, m, PROT_READ, MAP_SHARED, fd, o)) != MAP_FAILED)) { - md5_append(&s, d, m); - munmap(d, m); - - o += m; - l -= m; + if (l > BUFSIZE) { + m = l < MMAPSIZE ? l : MMAPSIZE; - + + while (l && ((d = mmap(NULL, m, PROT_READ, MAP_SHARED, fd, o)) != MAP_FAILED)) { + md5_append(&s, d, m); + munmap(d, m); + + o += m; + l -= m; + m = l < MMAPSIZE ? l : MMAPSIZE; + + } + + + if (l > 0) + fprintf(stderr, "mmap() failed: %s\n", strerror(errno)); } if (l > 0) { - void *p; - fprintf(stderr, "mmap() failed: %s\n", strerror(errno)); - - if (!(p = malloc(BUFSIZE))) - r = -1; - else { + if (!(p = malloc(BUFSIZE))) { + fprintf(stderr, "malloc(): %s\n", strerror(errno)); + goto finish; + } + + while (l) { + ssize_t r; - for (;;) { - ssize_t r; - - if ((r = read(fd, p, BUFSIZE)) < 0) { - fprintf(stderr, "read(): %s\n", strerror(errno)); - free(p); - return -1; - } - - if (!r) - break; - - md5_append(&s, p, r); + if ((r = read(fd, p, BUFSIZE)) < 0) { + fprintf(stderr, "read(): %s\n", strerror(errno)); + goto finish; } - free(p); + if (!r) + break; + + md5_append(&s, p, r); + + l -= r; } } - + + if (fstat(fd, &post) < 0) { + fprintf(stderr, "fstat(): %s\n", strerror(errno)); + goto finish; + } + + if (pre.st_mtime != post.st_mtime) { + fprintf(stderr, "File modified while calculating digest.\n"); + goto finish; + } + md5_finish(&s, md); + +finish: + + if (p) + free(p); + return r; } diff --git a/src/merge.c b/src/merge.c index 059523e..a0d9f8d 100644 --- a/src/merge.c +++ b/src/merge.c @@ -3,6 +3,10 @@ #include <string.h> #include <unistd.h> #include <errno.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <stdlib.h> #include "md5util.h" #include "diff.h" @@ -14,6 +18,7 @@ struct cb_info { struct syrep_db_context *c1; struct syrep_db_context *c2; const char *root; + char trash_dir[PATH_MAX+1]; }; static int conflict_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) { @@ -54,6 +59,29 @@ static int conflict_phase(DB *ddb, struct syrep_name *name, struct diff_entry *d return 0; } +static char *escape_path(const char *path, char *dst, unsigned l) { + const char *p; + char *d; + + for (p = path, d = dst; *p && d-dst < l-1; p++) { + if (*p == '/') { + *(d++) = '%'; + *(d++) = '2'; + *(d++) = 'F'; + + } else if (*p == '%') { + *(d++) = '%'; + *(d++) = '2'; + *(d++) = '5'; + + } else + *(d++) = *p; + } + + *(d++) = 0; + return dst; +} + 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; @@ -89,21 +117,42 @@ static int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, v if (f) { char path2[PATH_MAX+1]; snprintf(path2, sizeof(path2), "%s/%s", cb_info->root, name2.path); - fprintf(stderr, "COPY: Linking existing file <%s> to <%s>.\n", path2, path); + fprintf(stderr, "COPY: Linking existing file <%s> to <%s>.\n", name2.path, name->path); - if (copy_or_link_file(path2, path) < 0) + if (makeprefixpath(path, 0777) < 0) return -1; + + if (!access(path2, R_OK)) { + if (copy_or_link_file(path2, path, 0) < 0) + return -1; + } else { + unsigned l; + + snprintf(path2, sizeof(path2), "%s/", cb_info->trash_dir); + l = strlen(path2); + escape_path(name2.path, path2+l, sizeof(path2)-l); + + if (!access(path2, R_OK)) { + if (copy_or_link_file(path2, path, 0) < 0) + return -1; + } + + fprintf(stderr, "COPY: Local file <%s> vanished. Snapshot not up to date.\n", name2.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); + fprintf(stderr, "COPY: Copying file <%s> from patch.\n", name->path); - if (copy_or_link_file(a, path) < 0) + if (makeprefixpath(path, 0777) < 0) + return -1; + + if (copy_or_link_file(a, path, 0) < 0) return -1; } else - fprintf(stderr, "COPY: File <%s> is missing.\n", path); + fprintf(stderr, "COPY: File <%s> is missing.\n", name->path); } return 0; @@ -111,7 +160,8 @@ static int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, v 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]; + char path[PATH_MAX+1], target[PATH_MAX+1]; + unsigned l; assert(ddb && name && de && p); @@ -123,14 +173,61 @@ static int delete_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, snprintf(path, sizeof(path), "%s/%s", cb_info->root, name->path); - fprintf(stderr, "DELETE: Deleting file <%s>\n", path); + snprintf(target, sizeof(target), "%s/", cb_info->trash_dir); + l = strlen(target); + escape_path(name->path, target+l, sizeof(target)-l); + + fprintf(stderr, "DELETE: Moving file <%s> into trash (%s)\n", path, target); - if (unlink(path) < 0) { - fprintf(stderr, "unlink(%s): %s\n", path, strerror(errno)); + if (move_file(path, target, 1) < 0) return -1; + + if (args.prune_empty_flag) + if (prune_empty_directories(path, cb_info->root) < 0) + return -1; + + 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; } - return 0; + r = 0; + +finish: + if (dir) + closedir(dir); + + return r; } /* Merges c1 into c2 in directory "root" */ @@ -143,6 +240,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); if (!(ddb = make_diff(c1, c2))) goto finish; @@ -150,17 +250,22 @@ int merge(struct syrep_db_context *c1, struct syrep_db_context *c2, const char* if (diff_foreach(ddb, conflict_phase, &cb_info) < 0) goto finish; - if (diff_foreach(ddb, copy_phase, &cb_info) < 0) + if (diff_foreach(ddb, delete_phase, &cb_info) < 0) goto finish; - if (diff_foreach(ddb, delete_phase, &cb_info) < 0) + if (diff_foreach(ddb, copy_phase, &cb_info) < 0) goto finish; + if (empty_trash(cb_info.trash_dir) < 0) + goto finish; + r = 0; finish: if (ddb) ddb->close(ddb, 0); + + rmdir(cb_info.trash_dir); return r; } diff --git a/src/syrep.c b/src/syrep.c index ad7619a..e68e2f1 100644 --- a/src/syrep.c +++ b/src/syrep.c @@ -42,6 +42,12 @@ static int do_diff(void) { goto finish; } + if (args.local_temp_flag && isdirectory(args.inputs[0]) >= 1) { + const char *p = get_attached_filename(args.inputs[0], SYREP_TEMPDIR); + mkdir(p, 0777); + setenv("TMPDIR", p, 1); + } + if (!(path1 = strdup(get_attached_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME)))) goto finish; @@ -100,6 +106,12 @@ static int do_merge(void) { fprintf(stderr, "ERROR: %s is not a directory\n", args.inputs[1]); goto finish; } + + if (args.local_temp_flag) { + const char *p = get_attached_filename(args.inputs[1], SYREP_TEMPDIR); + mkdir(p, 0777); + setenv("TMPDIR", p, 1); + } if (!(path1 = strdup(get_attached_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME)))) goto finish; @@ -154,6 +166,12 @@ static int do_makepatch(void) { goto finish; } + if (args.local_temp_flag) { + const char *p = get_attached_filename(args.inputs[0], SYREP_TEMPDIR); + mkdir(p, 0777); + setenv("TMPDIR", p, 1); + } + if (!(path1 = strdup(get_attached_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME)))) goto finish; @@ -209,6 +227,12 @@ static int do_foreach(int (*func) (struct syrep_db_context *c), int m) { for (i = 0; i < args.inputs_num; i++) { 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); + mkdir(p, 0777); + setenv("TMPDIR", p, 1); + } + if (!(path = strdup(get_attached_filename(args.inputs[i], SYREP_SNAPSHOTFILENAME)))) goto finish; @@ -288,6 +312,12 @@ static int do_update(void) { return 1; } + if (args.local_temp_flag) { + const char *p = get_attached_filename(args.inputs[i], SYREP_TEMPDIR); + mkdir(p, 0777); + setenv("TMPDIR", p, 1); + } + if (!(path = strdup(get_attached_filename(args.inputs[i], SYREP_SNAPSHOTFILENAME)))) goto finish; diff --git a/src/syrep.ggo b/src/syrep.ggo index 91f9b2d..970cc82 100644 --- a/src/syrep.ggo +++ b/src/syrep.ggo @@ -3,6 +3,7 @@ package "syrep" version "0.1" option "verbose" v "Enable verbose operation" flag off +option "local-temp" T "Use temporary directory inside repository" flag off option "list" - "List a repository snapshot" flag off option "show-deleted" - "list: Show deleted entries of repository snapshot" flag off @@ -25,6 +26,7 @@ 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 "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 diff --git a/src/syrep.h b/src/syrep.h index 66d0a4d..2011c8a 100644 --- a/src/syrep.h +++ b/src/syrep.h @@ -5,6 +5,8 @@ #define SYREP_SNAPSHOTFILENAME "current.syrep" #define SYREP_MDCACHEFILENAME "md-cache" +#define SYREP_TEMPDIR "tmp" +#define SYREP_TRASHDIR "trash" #define SYREP_DIGESTLENGTH 16 @@ -1,3 +1,5 @@ +#define USE_SENDFILE + #include <stdio.h> #include <assert.h> #include <stdlib.h> @@ -9,6 +11,10 @@ #include <sys/mman.h> #include <fcntl.h> +#ifdef USE_SENDFILE +#include <sys/sendfile.h> +#endif + #include "util.h" void statistics(DB *db) { @@ -118,6 +124,10 @@ int copy_fd(int sfd, int dfd, uint32_t l) { void *buf = NULL; int r = -1; struct stat st; +#ifdef USE_SENDFILE + ssize_t k; + off_t sfo; +#endif if ((src_o = lseek(sfd, 0, SEEK_CUR)) == (off_t) -1) { fprintf(stderr, "lseek(): %s\n", strerror(errno)); @@ -148,6 +158,25 @@ int copy_fd(int sfd, int dfd, uint32_t l) { goto finish; } + +#ifdef USE_SENDFILE + + sfo = src_o; + if ((k = sendfile(dfd, sfd, &sfo, l)) > 0) { + + l -= k; + dst_o += k; + src_o = sfo; + + if (lseek(sfd, src_o, SEEK_SET) == (off_t) -1) { + fprintf(stderr, "lseek(): %s\n", strerror(errno)); + goto finish; + } + } else if (k < 0) + fprintf(stderr, "sendfile() failed for file copy, trying mmap(): %s\n", strerror(errno)); + +#endif + if (l > BUFSIZE) { uint32_t m = l < MMAPSIZE ? l : MMAPSIZE; @@ -182,6 +211,9 @@ int copy_fd(int sfd, int dfd, uint32_t l) { fprintf(stderr, "lseek(): %s\n", strerror(errno)); goto finish; } + + if (l > 0) + fprintf(stderr, "mmap() failed for file copy, trying read()/write(): %s\n", strerror(errno)); } if (l > 0) { @@ -213,16 +245,16 @@ finish: return r; } -int copy_file(const char *src, const char *dst) { +int copy_file(const char *src, const char *dst, int c) { 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)); + 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)); + if ((dfd = open(dst, O_RDWR|O_TRUNC|O_CREAT|( c ? 0 : O_EXCL), 0666)) < 0) { + fprintf(stderr, "open(\"%s\", O_RDWR|O_TRUNC|O_CREAT%s): %s\n", dst, c ? "" : "|O_EXCL", strerror(errno)); goto finish; } @@ -242,12 +274,15 @@ finish: return r; } -int copy_or_link_file(const char *src, const char *dst) { +int copy_or_link_file(const char *src, const char *dst, int c) { + if (c) + unlink(dst); + if (link(src, dst) < 0) { if (errno == EXDEV || errno == EPERM) - return copy_file(src, dst); + return copy_file(src, dst, c); fprintf(stderr, "link(%s, %s): %s\n", src, dst, strerror(errno)); return -1; @@ -255,3 +290,114 @@ int copy_or_link_file(const char *src, const char *dst) { return 0; } + +int move_file(const char *src, const char *dst, int c) { + int r; + + if ((r = copy_or_link_file(src, dst, c)) < 0) + return -1; + + if (unlink(src) < 0) { + fprintf(stderr, "unlink(%s): %s\n", src, strerror(errno)); + unlink(dst); + return -1; + } + + return 0; +} + + +int prune_empty_directories(const char *path, const char *root) { + char rroot[PATH_MAX], + rpath[PATH_MAX]; + + strncpy(rroot, root, PATH_MAX); + rroot[PATH_MAX-1] = 0; + normalize_path(rroot); + + strncpy(rpath, path, PATH_MAX); + rpath[PATH_MAX-1] = 0; + normalize_path(rpath); + + for (;;) { + char *e; + + if (!rpath[0]) + break; + + if (!strcmp(rpath, "/")) + break; + + if (!strcmp(rpath, rroot)) + break; + + if (rmdir(rpath) < 0) { + + if (errno == ENOTEMPTY) + break; + + if (errno != ENOENT) { + fprintf(stderr, "rmdir(\"%s\"): %s\n", rpath, strerror(errno)); + return -1; + } + } + + if (!(e = strrchr(rpath, '/'))) + break; + + *e = 0; + + } + + return 0; +} + +int mkdir_p(const char *path, mode_t m) { + char tmp[PATH_MAX]; + char *e, *b; + int quit = 0; + + strncpy(tmp, path, PATH_MAX); + tmp[PATH_MAX-1] = 0; + + normalize_path(tmp); + + for (b = tmp, quit = 0; !quit;) { + + if (!(e = strchr(b, '/'))) { + e = strchr(b, 0); + quit = 1; + } + + *e = 0; + if (mkdir(tmp, m) < 0) { + if (errno != EEXIST) { + fprintf(stderr, "mkdir(\"%s\"): %s\n", tmp, strerror(errno)); + return -1; + } + } + *e = '/'; + + b = e+1; + } + + return 0; +} + +/* Create all leading directories in path */ +int makeprefixpath(const char *path, mode_t m) { + char tmp[PATH_MAX], *e; + + strncpy(tmp, path, PATH_MAX); + tmp[PATH_MAX-1] = 0; + + normalize_path(tmp); + + if (!(e = strrchr(tmp, '/'))) + return 0; + + *e = 0; + + return mkdir_p(tmp, m); +} + @@ -10,8 +10,19 @@ 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); +int copy_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. */ +int prune_empty_directories(const char *path, const char *root); + +/* Same as /bin/mkdir -p in the shell */ +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); #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) |