summaryrefslogtreecommitdiffstats
path: root/src/merge.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/merge.c')
-rw-r--r--src/merge.c129
1 files changed, 117 insertions, 12 deletions
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;
}