From e4d8e793f652b0920cbd2260d556d432887fefce Mon Sep 17 00:00:00 2001 From: Christian Jann Date: Wed, 6 Apr 2011 16:43:04 +0200 Subject: [PATCH] added new conflict resolution to libcsync - use the latest version of the two files under its original filename in both destinations (same as currently implemented) - additionally, create a copy of the older file under a different name using the same file extension with appended marker (e.g. "conflict") and timestamp in both destinations (e.g. textfile.odt -> textfile_conflict-20100222-105000.odt) - At the Moment 'textfile_conflict-20100222-105000.odt' is only created on the side where the conflict has occurred and gets synced to the other side on the second sync. --- src/csync.c | 17 +++++ src/csync.h | 9 +++ src/csync_private.h | 2 + src/csync_propagate.c | 158 ++++++++++++++++++++++++++++++++++++++++++ src/csync_reconcile.c | 102 ++++++++++++++++++++++++--- src/csync_statedb.c | 1 + src/std/c_path.c | 84 ++++++++++++++++++++++ src/std/c_path.h | 27 ++++++++ 8 files changed, 390 insertions(+), 10 deletions(-) diff --git a/src/csync.c b/src/csync.c index e1ebb3e9d..7695be2e0 100644 --- a/src/csync.c +++ b/src/csync.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "c_lib.h" #include "csync_private.h" @@ -110,6 +111,7 @@ int csync_create(CSYNC **csync, const char *local, const char *remote) { ctx->options.max_depth = MAX_DEPTH; ctx->options.max_time_difference = MAX_TIME_DIFFERENCE; ctx->options.unix_extensions = 0; + ctx->options.with_conflict_copys=false; ctx->pwd.uid = getuid(); ctx->pwd.euid = geteuid(); @@ -682,4 +684,19 @@ int csync_get_status(CSYNC *ctx) { return ctx->status; } +int csync_enable_conflictcopys(CSYNC* ctx){ + if (ctx == NULL) { + return -1; + } + + if (ctx->status & CSYNC_STATUS_INIT) { + fprintf(stderr, "This function must be called before initialization."); + return -1; + } + + ctx->options.with_conflict_copys=true; + + return 0; +} + /* vim: set ts=8 sw=2 et cindent: */ diff --git a/src/csync.h b/src/csync.h index f55e4a565..29c04b64a 100644 --- a/src/csync.h +++ b/src/csync.h @@ -274,6 +274,15 @@ int csync_set_auth_callback(CSYNC *ctx, csync_auth_callback cb); */ const char *csync_get_statedb_file(CSYNC *ctx); +/** + * @brief Enable the creation of backup copys if files are changed on both sides + * + * @param ctx The csync context. + * + * @return 0 on success, less than 0 if an error occured. + */ +int csync_enable_conflictcopys(CSYNC *ctx); + /* Used for special modes or debugging */ int csync_get_status(CSYNC *ctx); diff --git a/src/csync_private.h b/src/csync_private.h index 09e317ec4..3fd2a6b7b 100644 --- a/src/csync_private.h +++ b/src/csync_private.h @@ -32,6 +32,7 @@ #define _CSYNC_PRIVATE_H #include +#include #include #include "config.h" @@ -114,6 +115,7 @@ struct csync_s { int sync_symbolic_links; int unix_extensions; char *config_dir; + bool with_conflict_copys; } options; struct { diff --git a/src/csync_propagate.c b/src/csync_propagate.c index 23d4db6cc..89d3f4f27 100644 --- a/src/csync_propagate.c +++ b/src/csync_propagate.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "csync_private.h" #include "csync_propagate.h" @@ -32,6 +33,7 @@ #define CSYNC_LOG_CATEGORY_NAME "csync.propagator" #include "csync_log.h" +#include "csync_util.h" static int _csync_cleanup_cmp(const void *a, const void *b) { csync_file_stat_t *st_a, *st_b; @@ -370,6 +372,137 @@ out: return rc; } +static int _backup_path(char** duri, const char* uri, const char* path) +{ + int rc=0; + C_PATHINFO *info=NULL; + + struct tm *curtime; + time_t sec; + char timestring[16]; + time(&sec); + curtime = localtime(&sec); + strftime(timestring, 16, "%Y%m%d-%H%M%S",curtime); + + info=c_split_path(path); + printf("directory: %s\n",info->directory); + printf("filename : %s\n",info->filename); + printf("extension: %s\n",info->extension); + + if (asprintf(duri, "%s/%s%s_conflict-%s%s", uri,info->directory ,info->filename,timestring,info->extension) < 0) { + rc = -1; + } + + SAFE_FREE(info); + return rc; +} + + +static int _csync_backup_file(CSYNC *ctx, csync_file_stat_t *st) { + enum csync_replica_e srep = -1; + enum csync_replica_e drep = -1; + enum csync_replica_e rep_bak = -1; + + char *suri = NULL; + char *duri = NULL; + + char errbuf[256] = {0}; + + int rc = -1; + + rep_bak = ctx->replica; + + if(st->instruction==CSYNC_INSTRUCTION_CONFLICT) + { + //printf("CSYNC_INSTRUCTION_CONFLICT\n"); + switch (ctx->current) { + case LOCAL_REPLICA: + srep = ctx->remote.type; + drep = ctx->remote.type; + if (asprintf(&suri, "%s/%s", ctx->remote.uri, st->path) < 0) { + rc = -1; + goto out; + } + + if ( _backup_path(&duri, ctx->remote.uri,st->path) < 0) { + rc = -1; + goto out; + } + break; + case REMOTE_REPLCIA: + srep = ctx->local.type; + drep = ctx->local.type; + if (asprintf(&suri, "%s/%s", ctx->local.uri, st->path) < 0) { + rc = -1; + goto out; + } + + if ( _backup_path(&duri, ctx->local.uri, st->path) < 0) { + rc = -1; + goto out; + } + break; + default: + break; + } + } + + else + { + printf("instruction not allowed: %i %s\n",st->instruction,csync_instruction_str(st->instruction)); + rc = -1; + goto out; + } + + printf("suri: %s\n",suri); + printf("duri: %s\n",duri); + + + /* rename the older file to conflict */ + ctx->replica = drep; + if (csync_vio_rename(ctx, suri, duri) < 0) { + switch (errno) { + case ENOMEM: + rc = -1; + break; + default: + rc = 1; + break; + } + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, + "file: %s, command: rename, error: %s", + duri, + strerror_r(errno, errbuf, sizeof(errbuf))); + goto out; + } + + + /* set instruction for the statedb merger */ + //st->instruction = CSYNC_INSTRUCTION_UPDATED; + st->instruction = CSYNC_INSTRUCTION_RENAME; + //maybe updated and change url to the new one + + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "file: %s, instruction: CONFLICT_BACKUP", duri); + + rc = 0; + +out: + ctx->replica = srep; + ctx->replica = drep; + + /* set instruction for the statedb merger */ + if (rc != 0) { + st->instruction = CSYNC_INSTRUCTION_ERROR; + } + + SAFE_FREE(suri); + SAFE_FREE(duri); + + ctx->replica = rep_bak; + + return rc; +} + static int _csync_new_file(CSYNC *ctx, csync_file_stat_t *st) { int rc = -1; @@ -386,6 +519,19 @@ static int _csync_sync_file(CSYNC *ctx, csync_file_stat_t *st) { return rc; } +static int _csync_conflict_file(CSYNC *ctx, csync_file_stat_t *st) { + int rc = -1; + + rc = _csync_backup_file(ctx, st); + + if(rc>=0) + { + rc = _csync_push_file(ctx, st); + } + + return rc; +} + static int _csync_remove_file(CSYNC *ctx, csync_file_stat_t *st) { char errbuf[256] = {0}; char *uri = NULL; @@ -771,6 +917,12 @@ static int _csync_propagation_file_visitor(void *obj, void *data) { goto err; } break; + case CSYNC_INSTRUCTION_CONFLICT: + printf("case CSYNC_INSTRUCTION_CONFLICT: %s\n",st->path); + if (_csync_conflict_file(ctx, st) < 0) { + goto err; + } + break; default: break; } @@ -816,6 +968,12 @@ static int _csync_propagation_dir_visitor(void *obj, void *data) { goto err; } break; + case CSYNC_INSTRUCTION_CONFLICT: + printf("directory conflict\n"); + if (_csync_sync_dir(ctx, st) < 0) { + goto err; + } + break; case CSYNC_INSTRUCTION_REMOVE: if (_csync_remove_dir(ctx, st) < 0) { goto err; diff --git a/src/csync_reconcile.c b/src/csync_reconcile.c index f0ba5e46d..1e38702ad 100644 --- a/src/csync_reconcile.c +++ b/src/csync_reconcile.c @@ -91,11 +91,33 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) { /* file on other replica is new too */ case CSYNC_INSTRUCTION_NEW: if (cur->modtime > other->modtime) { - cur->instruction = CSYNC_INSTRUCTION_SYNC; - other->instruction = CSYNC_INSTRUCTION_NONE; + + if(ctx->options.with_conflict_copys) + { + printf("file new on both, cur is newer PATH=./%s\n",cur->path); + cur->instruction = CSYNC_INSTRUCTION_CONFLICT; + other->instruction = CSYNC_INSTRUCTION_NONE; + } + else + { + cur->instruction = CSYNC_INSTRUCTION_SYNC; + other->instruction = CSYNC_INSTRUCTION_NONE; + } + } else if (cur->modtime < other->modtime) { - cur->instruction = CSYNC_INSTRUCTION_NONE; - other->instruction = CSYNC_INSTRUCTION_SYNC; + + if(ctx->options.with_conflict_copys) + { + printf("file new on both, other is newer PATH=./%s\n",cur->path); + cur->instruction = CSYNC_INSTRUCTION_NONE; + other->instruction = CSYNC_INSTRUCTION_CONFLICT; + } + else + { + cur->instruction = CSYNC_INSTRUCTION_NONE; + other->instruction = CSYNC_INSTRUCTION_SYNC; + } + } else { /* file are equal */ cur->instruction = CSYNC_INSTRUCTION_NONE; @@ -106,10 +128,30 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) { case CSYNC_INSTRUCTION_EVAL: /* file on current replica is newer */ if (cur->modtime > other->modtime) { - cur->instruction = CSYNC_INSTRUCTION_SYNC; + + if(ctx->options.with_conflict_copys) + { + printf("new on cur, modified on other, cur is newer PATH=./%s\n",cur->path); + cur->instruction = CSYNC_INSTRUCTION_CONFLICT; + } + else + { + cur->instruction = CSYNC_INSTRUCTION_SYNC; + } + } else { /* file on opposite replica is newer */ - cur->instruction = CSYNC_INSTRUCTION_NONE; + + if(ctx->options.with_conflict_copys) + { + printf("new on cur, modified on other, other is newer PATH=./%s\n",cur->path); + cur->instruction = CSYNC_INSTRUCTION_NONE; + } + else + { + cur->instruction = CSYNC_INSTRUCTION_NONE; + } + } break; /* file on the other replica has not been modified */ @@ -126,19 +168,59 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) { /* file on other replica is new too */ case CSYNC_INSTRUCTION_NEW: if (cur->modtime > other->modtime) { - cur->instruction = CSYNC_INSTRUCTION_SYNC; + + if(ctx->options.with_conflict_copys) + { + printf("modified on cur, new on other, cur is newer PATH=./%s\n",cur->path); + cur->instruction = CSYNC_INSTRUCTION_CONFLICT; + } + else + { + cur->instruction = CSYNC_INSTRUCTION_SYNC; + } + } else { - cur->instruction = CSYNC_INSTRUCTION_NONE; + + if(ctx->options.with_conflict_copys) + { + printf("modified on cur, new on other, other is newer PATH=./%s\n",cur->path); + cur->instruction = CSYNC_INSTRUCTION_NONE; + } + else + { + cur->instruction = CSYNC_INSTRUCTION_NONE; + } } break; /* file on other replica has changed too */ case CSYNC_INSTRUCTION_EVAL: /* file on current replica is newer */ if (cur->modtime > other->modtime) { - cur->instruction = CSYNC_INSTRUCTION_SYNC; + + if(ctx->options.with_conflict_copys) + { + printf("both modified, cur is newer PATH=./%s\n",cur->path); + cur->instruction = CSYNC_INSTRUCTION_CONFLICT; + other->instruction= CSYNC_INSTRUCTION_NONE; + } + else + { + cur->instruction = CSYNC_INSTRUCTION_SYNC; + } + } else { /* file on opposite replica is newer */ - cur->instruction = CSYNC_INSTRUCTION_NONE; + + if(ctx->options.with_conflict_copys) + { + printf("both modified, other is newer PATH=./%s\n",cur->path); + cur->instruction = CSYNC_INSTRUCTION_NONE; + other->instruction=CSYNC_INSTRUCTION_CONFLICT; + } + else + { + cur->instruction = CSYNC_INSTRUCTION_NONE; + } } break; /* file on the other replica has not been modified */ diff --git a/src/csync_statedb.c b/src/csync_statedb.c index bb66dbc40..40e49022f 100644 --- a/src/csync_statedb.c +++ b/src/csync_statedb.c @@ -288,6 +288,7 @@ static int _insert_metadata_visitor(void *obj, void *data) { case CSYNC_INSTRUCTION_NONE: /* As we only sync the local tree we need this flag here */ case CSYNC_INSTRUCTION_UPDATED: + case CSYNC_INSTRUCTION_CONFLICT: CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "SQL statement: INSERT INTO metadata_temp \n" "\t\t\t(phash, pathlen, path, inode, uid, gid, mode, modtime) VALUES \n" diff --git a/src/std/c_path.c b/src/std/c_path.c index 15c68b092..337b91194 100644 --- a/src/std/c_path.c +++ b/src/std/c_path.c @@ -379,3 +379,87 @@ int c_parse_uri(const char *uri, return -1; } + +/* + * http://refactormycode.com/codes/1345-extracting-directory-filename-and-extension-from-a-path + * Allocate a block of memory that holds the PATHINFO at the beginning + * followed by the actual path. Two extra bytes are allocated (+3 instead + * of just +1) to deal with shifting the filename and extension to protect the trailing '/' + * and the leading '.'. These extra bytes also double as the empty string, as + * well as a pad to keep from reading past the memory block. + * + */ +C_PATHINFO * c_split_path(const char* pathSrc) +{ + size_t length = strlen(pathSrc); + size_t len=0; + C_PATHINFO * pathinfo = (C_PATHINFO *) c_malloc(sizeof(C_PATHINFO) + length + 3); + + if (pathinfo) + { + char * path = (char *) &pathinfo[1]; // copy of the path + char * theEnd = &path[length + 1]; // second null terminator + char * extension; + char * lastSep; + + // Copy the original string and double null terminate it. + strcpy(path, pathSrc); + *theEnd = '\0'; + pathinfo->directory = theEnd; // Assume no path + pathinfo->extension = theEnd; // Assume no extension + pathinfo->filename = path; // Assume filename only + + lastSep = strrchr(path, '/'); + if (lastSep) + { + pathinfo->directory = path; // Pick up the path + //*lastSep++ = '\0'; // Truncate directory + //pathinfo->filename = lastSep; // Pick up name after path + + memmove(lastSep + 2, lastSep+1, strlen(lastSep)); + *(lastSep+1)='\0'; + pathinfo->filename = lastSep+2; + + } + + // Start at the second character of the filename to handle + // filenames that start with '.' like ".login". + // We don't overrun the buffer in the cases of an empty path + // or a path that looks like "/usr/bin/" because of the extra + // byte. + + + extension = strrchr(&pathinfo->filename[1], '.'); + if (extension) + { + // Shift the extension over to protect the leading '.' since + // we need to truncate the filename. + memmove(extension + 1, extension, strlen(extension)); + pathinfo->extension = extension + 1; + + *extension = '\0'; // Truncate filename + } + else + { + //tmp files from kate/kwrite "somefile~": '~' should be the extension + len=strlen(pathinfo->filename); + if(len>1) + { + printf("len: %i\n",(int)len); + printf("%i %i\n",pathinfo->filename[len-1],pathinfo->filename[len-1]); + printf("%i %i\n",'~','~'); + if(pathinfo->filename[len-1]=='~') + { + printf("kate file found\n"); + pathinfo->filename[len-1]='\0'; // Truncate filename + pathinfo->extension[0]='~'; + } + } + } + + } + + return pathinfo; +} + + diff --git a/src/std/c_path.h b/src/std/c_path.h index ca3cfce6c..b094089a4 100644 --- a/src/std/c_path.h +++ b/src/std/c_path.h @@ -105,6 +105,33 @@ int c_tmpname(char *template); int c_parse_uri(const char *uri, char **scheme, char **user, char **passwd, char **host, unsigned int *port, char **path); +/** + * @brief Parts of a path. + * + * @param directory '\0' terminated path including the final '/' + * + * @param filename '\0' terminated string + * + * @param extension '\0' terminated string + * + */ +typedef struct +{ + char * directory; + char * filename; + char * extension; +} C_PATHINFO; + +/** + * @brief Extracting directory, filename and extension from a path. + * + * @param pathSrc The path to parse. + * + * @return Returns a C_PATHINFO structure that should be freed using SAFE_FREE(). + */ +C_PATHINFO * c_split_path(const char* pathSrc); + + /** * }@ */