nextcloud-desktop/src/csync_reconcile.c

400 lines
15 KiB
C
Raw Normal View History

2008-05-15 15:50:34 +04:00
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2008 by Andreas Schneider <mail@cynapses.org>
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
2012-03-02 19:47:34 +04:00
#include "config.h"
2008-05-15 15:50:34 +04:00
#include "csync_private.h"
#include "csync_reconcile.h"
#include "csync_util.h"
#include "csync_statedb.h"
2013-01-04 23:45:10 +04:00
#include "csync_rename.h"
#include "c_jhash.h"
2008-05-15 15:50:34 +04:00
#define CSYNC_LOG_CATEGORY_NAME "csync.reconciler"
#include "csync_log.h"
#define ACCEPTED_TIME_DIFF 5
2012-11-20 20:33:25 +04:00
#define ONE_HOUR 3600
static bool _time_dst_off( time_t t1, time_t t2, int dst_offset ) {
bool ret = false;
long int diff = t1 - t2;
if( diff > (dst_offset - ACCEPTED_TIME_DIFF) && (diff < dst_offset+ ACCEPTED_TIME_DIFF) )
ret = true;
return ret;
}
/*
* We merge replicas at the file level. The merged replica contains the
* superset of files that are on the local machine and server copies of
* the replica. In the case where the same file is in both the local
* and server copy, the file that was modified most recently is used.
* This means that new files are not deleted, and updated versions of
* existing files are not overwritten.
*
* When a file is updated, the merge algorithm compares the destination
* file with the the source file. If the destination file is newer
* (timestamp is newer), it is not overwritten. If both files, on the
* source and the destination, have been changed, the newer file wins.
*/
static int _csync_merge_algorithm_visitor(void *obj, void *data) {
2012-08-23 18:33:41 +04:00
csync_file_stat_t *cur = NULL;
csync_file_stat_t *other = NULL;
csync_file_stat_t *tmp = NULL;
uint64_t h = 0;
int len = 0;
2012-08-23 18:33:41 +04:00
CSYNC *ctx = NULL;
c_rbtree_t *tree = NULL;
c_rbnode_t *node = NULL;
2008-05-15 15:50:34 +04:00
2012-08-23 18:33:41 +04:00
cur = (csync_file_stat_t *) obj;
ctx = (CSYNC *) data;
2008-05-15 15:50:34 +04:00
2012-08-23 18:33:41 +04:00
/* we need the opposite tree! */
switch (ctx->current) {
2008-05-15 15:50:34 +04:00
case LOCAL_REPLICA:
2012-08-23 18:33:41 +04:00
tree = ctx->remote.tree;
break;
case REMOTE_REPLICA:
2012-08-23 18:33:41 +04:00
tree = ctx->local.tree;
2008-05-15 15:50:34 +04:00
break;
2012-08-23 18:33:41 +04:00
default:
2008-05-15 15:50:34 +04:00
break;
2012-08-23 18:33:41 +04:00
}
2012-08-23 18:33:41 +04:00
node = c_rbtree_find(tree, &cur->phash);
2013-01-04 23:45:10 +04:00
if (!node && ctx->current == REMOTE_REPLICA) {
/* Check the renamed path as well. */
char *renamed_path = csync_rename_adjust_path(ctx, cur->path);
if (!c_streq(renamed_path, cur->path)) {
len = strlen( renamed_path );
h = c_jhash64((uint8_t *) renamed_path, len, 0);
node = c_rbtree_find(tree, &h);
}
SAFE_FREE(renamed_path);
}
2012-08-23 18:33:41 +04:00
/* file only found on current replica */
if (node == NULL) {
switch(cur->instruction) {
/* file has been modified */
case CSYNC_INSTRUCTION_EVAL:
cur->instruction = CSYNC_INSTRUCTION_NEW;
break;
/* file has been removed on the opposite replica */
case CSYNC_INSTRUCTION_NONE:
cur->instruction = CSYNC_INSTRUCTION_REMOVE;
break;
case CSYNC_INSTRUCTION_RENAME:
/* rename support only on the local replica because of inode needed. */
if(ctx->current == LOCAL_REPLICA ) {
/* use the old name to find the "other" node */
tmp = csync_statedb_get_stat_by_inode(ctx, cur->inode);
/* Find the opposite node. */
if( tmp ) {
/* We need to calculate the phash again because of the phash being stored as int in db. */
if( tmp->path ) {
len = strlen( tmp->path );
h = c_jhash64((uint8_t *) tmp->path, len, 0);
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"PHash of temporar opposite: %llu", h);
node = c_rbtree_find(tree, &h);
}
if(node) {
2013-01-04 23:45:10 +04:00
char *adjusted = csync_rename_adjust_path(ctx, cur->path);
if (!c_streq(adjusted, cur->path)) {
other = (csync_file_stat_t*)node->data;
other->instruction = CSYNC_INSTRUCTION_RENAME;
other->destpath = c_strdup( cur->path );
cur->instruction = CSYNC_INSTRUCTION_NONE;
} else {
/* The parent directory is going to be renamed */
cur->instruction = CSYNC_INSTRUCTION_NONE;
}
SAFE_FREE(adjusted);
2012-08-23 18:33:41 +04:00
}
if( ! other ) {
cur->instruction = CSYNC_INSTRUCTION_NEW;
}
2013-01-06 23:31:23 +04:00
SAFE_FREE(tmp->md5);
SAFE_FREE(tmp);
}
}
2012-08-23 18:33:41 +04:00
break;
default:
break;
}
2012-08-23 18:33:41 +04:00
} else {
/*
2008-05-15 15:50:34 +04:00
* file found on the other replica
*/
2012-11-20 20:33:25 +04:00
bool set_instruction_none = false;
2012-08-23 18:33:41 +04:00
other = (csync_file_stat_t *) node->data;
switch (cur->instruction) {
case CSYNC_INSTRUCTION_RENAME:
/* If the file already exist on the other side, we have a conflict.
Abort the rename and consider it is a new file. */
cur->instruction = CSYNC_INSTRUCTION_NEW;
/* fall trough */
2012-08-23 18:33:41 +04:00
/* file on current replica is new */
case CSYNC_INSTRUCTION_NEW:
switch (other->instruction) {
/* file on other replica is new too */
case CSYNC_INSTRUCTION_NEW:
/* if (cur->modtime > other->modtime) { */
2012-11-20 20:33:25 +04:00
CSYNC_LOG( CSYNC_LOG_PRIORITY_DEBUG, "** size compare: %lld <-> %lld", (long long) cur->size,
(long long) other->size);
if(cur->modtime - other->modtime > ACCEPTED_TIME_DIFF) {
if( other->size == cur->size &&
_time_dst_off( cur->modtime, other->modtime, ONE_HOUR ) ) {
CSYNC_LOG( CSYNC_LOG_PRIORITY_DEBUG, "DST-Problem detected. Skip conflict for %s!", cur->path);
set_instruction_none = true;
} else {
if(ctx->options.with_conflict_copys)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"file new on both, cur is newer PATH=./%s, %llu <-> %llu",
cur->path, (unsigned long long) cur->modtime, (unsigned long long) other->modtime);
cur->instruction = CSYNC_INSTRUCTION_CONFLICT;
other->instruction = CSYNC_INSTRUCTION_NONE;
}
else
{
cur->instruction = CSYNC_INSTRUCTION_SYNC;
other->instruction = CSYNC_INSTRUCTION_NONE;
}
2012-08-23 18:33:41 +04:00
}
2012-11-20 20:33:25 +04:00
/* } else if (cur->modtime < other->modtime) { */
} else if (other->modtime - cur->modtime > ACCEPTED_TIME_DIFF) {
/* Check if we have the dst problem. Older versions of ocsync wrote a wrong
* (ie. localized) mtime to the files which can be ignored if the size is equal
* and the time shift is exactyl one hour. */
if( other->size == cur->size &&
_time_dst_off( other->modtime, cur->modtime, ONE_HOUR ) ) {
CSYNC_LOG( CSYNC_LOG_PRIORITY_DEBUG, "DST-Problem detected. Skip conflict for %s!", cur->path);
set_instruction_none = true;
} else {
if(ctx->options.with_conflict_copys)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"file new on both, other is newer PATH=./%s, %llu <->%llu",
cur->path, (unsigned long long) cur->modtime, (unsigned long long) other->modtime);
cur->instruction = CSYNC_INSTRUCTION_NONE;
other->instruction = CSYNC_INSTRUCTION_CONFLICT;
}
else
{
cur->instruction = CSYNC_INSTRUCTION_NONE;
other->instruction = CSYNC_INSTRUCTION_SYNC;
}
2012-08-23 18:33:41 +04:00
}
} else {
2012-11-20 20:33:25 +04:00
/* The files are equal. */
set_instruction_none = true;
}
if( set_instruction_none ) {
2012-08-23 18:33:41 +04:00
/* file are equal */
/* FIXME: Get the id from the server! */
2012-08-23 18:33:41 +04:00
cur->instruction = CSYNC_INSTRUCTION_NONE;
other->instruction = CSYNC_INSTRUCTION_NONE;
if( !cur->md5 && other->md5 ) cur->md5 = c_strdup(other->md5);
2012-08-23 18:33:41 +04:00
}
2012-11-20 20:33:25 +04:00
2012-08-23 18:33:41 +04:00
break;
/* file on other replica has changed too */
case CSYNC_INSTRUCTION_EVAL:
/* file on current replica is newer */
if (cur->modtime - other->modtime > ACCEPTED_TIME_DIFF ) {
2012-08-23 18:33:41 +04:00
if(ctx->options.with_conflict_copys)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"new on cur, modified on other, cur is newer PATH=./%s",cur->path);
cur->instruction = CSYNC_INSTRUCTION_CONFLICT;
}
else
{
cur->instruction = CSYNC_INSTRUCTION_SYNC;
}
2008-05-15 15:50:34 +04:00
2012-08-23 18:33:41 +04:00
} else {
/* file on opposite replica is newer */
if(ctx->options.with_conflict_copys)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"new on cur, modified on other, other is newer PATH=./%s",cur->path);
cur->instruction = CSYNC_INSTRUCTION_NONE;
}
else
{
cur->instruction = CSYNC_INSTRUCTION_NONE;
}
}
break;
/* file on the other replica has not been modified */
case CSYNC_INSTRUCTION_NONE:
cur->instruction = CSYNC_INSTRUCTION_SYNC;
break;
default:
break;
2008-05-15 21:20:01 +04:00
}
break;
2012-08-23 18:33:41 +04:00
/* file on current replica has been modified */
case CSYNC_INSTRUCTION_EVAL:
switch (other->instruction) {
/* file on other replica is new too */
case CSYNC_INSTRUCTION_NEW:
if (cur->modtime - other->modtime > ACCEPTED_TIME_DIFF) {
2012-08-23 18:33:41 +04:00
if(ctx->options.with_conflict_copys)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"modified on cur, new on other, cur is newer PATH=./%s",cur->path);
cur->instruction = CSYNC_INSTRUCTION_CONFLICT;
}
else
{
cur->instruction = CSYNC_INSTRUCTION_SYNC;
}
} else {
if(ctx->options.with_conflict_copys)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"modified on cur, new on other, other is newer PATH=./%s",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 > ACCEPTED_TIME_DIFF) {
2012-08-23 18:33:41 +04:00
if(ctx->options.with_conflict_copys)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"both modified, cur is newer PATH=./%s",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 */
if(ctx->options.with_conflict_copys)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"both modified, other is newer PATH=./%s",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 */
case CSYNC_INSTRUCTION_NONE:
cur->instruction = CSYNC_INSTRUCTION_SYNC;
break;
default:
break;
2008-05-15 21:20:01 +04:00
}
break;
2012-08-23 18:33:41 +04:00
default:
2008-05-15 21:20:01 +04:00
break;
}
2012-08-23 18:33:41 +04:00
}
//hide instruction NONE messages when log level is set to debug,
//only show these messages on log level trace
if(cur->instruction ==CSYNC_INSTRUCTION_NONE)
{
if(cur->type == CSYNC_FTW_TYPE_DIR)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,
"%-20s dir: %s",
csync_instruction_str(cur->instruction),
cur->path);
}
else
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,
"%-20s file: %s",
csync_instruction_str(cur->instruction),
cur->path);
2008-05-15 15:50:34 +04:00
}
}
2012-08-23 18:33:41 +04:00
else
{
if(cur->type == CSYNC_FTW_TYPE_DIR)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
"%-20s dir: %s",
csync_instruction_str(cur->instruction),
cur->path);
}
else
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
"%-20s file: %s",
csync_instruction_str(cur->instruction),
cur->path);
}
}
return 0;
2008-05-15 15:50:34 +04:00
}
int csync_reconcile_updates(CSYNC *ctx) {
2012-02-04 15:26:27 +04:00
int rc;
2008-05-15 15:50:34 +04:00
c_rbtree_t *tree = NULL;
switch (ctx->current) {
case LOCAL_REPLICA:
tree = ctx->local.tree;
break;
case REMOTE_REPLICA:
2008-05-15 15:50:34 +04:00
tree = ctx->remote.tree;
break;
default:
break;
}
rc = c_rbtree_walk(tree, (void *) ctx, _csync_merge_algorithm_visitor);
2008-05-15 15:50:34 +04:00
2012-02-04 15:26:27 +04:00
return rc;
2008-05-15 15:50:34 +04:00
}
2009-05-13 12:12:07 +04:00
/* vim: set ts=8 sw=2 et cindent: */