mirror of
https://github.com/nextcloud/desktop.git
synced 2024-10-25 05:45:42 +03:00
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:
parent
29616c0b2e
commit
e4d8e793f6
8 changed files with 390 additions and 10 deletions
17
src/csync.c
17
src/csync.c
|
@ -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: */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
/**
|
||||
* }@
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue