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.
This commit is contained in:
Christian Jann 2011-04-06 16:43:04 +02:00
parent 29616c0b2e
commit e4d8e793f6
8 changed files with 390 additions and 10 deletions

View file

@ -29,6 +29,7 @@
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdbool.h>
#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: */

View file

@ -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);

View file

@ -32,6 +32,7 @@
#define _CSYNC_PRIVATE_H
#include <stdint.h>
#include <stdbool.h>
#include <sqlite3.h>
#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 {

View file

@ -25,6 +25,7 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#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;

View file

@ -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 */

View file

@ -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"

View file

@ -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;
}

View file

@ -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);
/**
* }@
*/