nextcloud-desktop/src/csync_reconcile.c
Klaas Freitag d8967a2bd1 try to propagate EVAL status up by detecting changes in underlying
directories. That does not work as the reconciler runs through the
directoires in the wrong order, ie. the outermost first.
2012-08-23 17:41:53 +03:00

335 lines
12 KiB
C

/*
* 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.
*/
#include "config.h"
#include "csync_private.h"
#include "csync_reconcile.h"
#include "csync_util.h"
#include "csync_statedb.h"
#include "c_jhash.h"
#define CSYNC_LOG_CATEGORY_NAME "csync.reconciler"
#include "csync_log.h"
/*
* 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) {
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;
CSYNC *ctx = NULL;
c_rbtree_t *tree = NULL;
c_rbnode_t *node = NULL;
cur = (csync_file_stat_t *) obj;
ctx = (CSYNC *) data;
/* we need the opposite tree! */
switch (ctx->current) {
case LOCAL_REPLICA:
tree = ctx->remote.tree;
break;
case REMOTE_REPLCIA:
tree = ctx->local.tree;
break;
default:
break;
}
node = c_rbtree_find(tree, &cur->phash);
/* 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) {
other = (csync_file_stat_t*)node->data;
other->instruction = CSYNC_INSTRUCTION_RENAME;
other->destpath = c_strdup( cur->path );
cur->instruction = CSYNC_INSTRUCTION_NONE;
}
if( ! other ) {
cur->instruction = CSYNC_INSTRUCTION_NEW;
}
}
}
break;
default:
break;
}
} else {
/*
* file found on the other replica
*/
other = (csync_file_stat_t *) node->data;
switch (cur->instruction) {
/* 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) {
if(ctx->options.with_conflict_copys)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"file new on both, cur is newer PATH=./%s",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) {
if(ctx->options.with_conflict_copys)
{
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"file new on both, other is newer PATH=./%s",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 */
/* FIXME: Get the id from the server! */
cur->instruction = CSYNC_INSTRUCTION_NONE;
other->instruction = CSYNC_INSTRUCTION_NONE;
if( !cur->md5 ) cur->md5 = other->md5;
}
break;
/* file on other replica has changed too */
case CSYNC_INSTRUCTION_EVAL:
/* file on current replica is newer */
if (cur->modtime > other->modtime) {
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;
}
} 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;
}
break;
/* 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) {
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) {
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;
}
break;
default:
break;
}
}
//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);
}
}
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;
}
int csync_reconcile_updates(CSYNC *ctx) {
int rc;
c_rbtree_t *tree = NULL;
switch (ctx->current) {
case LOCAL_REPLICA:
tree = ctx->local.tree;
break;
case REMOTE_REPLCIA:
tree = ctx->remote.tree;
break;
default:
break;
}
rc = c_rbtree_walk(tree, (void *) ctx, _csync_merge_algorithm_visitor);
return rc;
}
/* vim: set ts=8 sw=2 et cindent: */