diff --git a/src/csync_private.h b/src/csync_private.h index b0160b5403..d35f07287d 100644 --- a/src/csync_private.h +++ b/src/csync_private.h @@ -159,6 +159,7 @@ struct csync_file_stat_s { mode_t mode; /* u32 */ int nlink; /* u32 */ int type; /* u32 */ + char *destpath; /* for renames */ enum csync_instructions_e instruction; /* u32 */ char path[1]; /* u8 */ } diff --git a/src/csync_propagate.c b/src/csync_propagate.c index 87e7f9fa22..4ef101cac3 100644 --- a/src/csync_propagate.c +++ b/src/csync_propagate.c @@ -550,6 +550,68 @@ static int _csync_new_file(CSYNC *ctx, csync_file_stat_t *st) { return rc; } +static int _csync_rename_file(CSYNC *ctx, csync_file_stat_t *st) { + int rc = 0; + char errbuf[256] = {0}; + char *suri = NULL; + char *duri = NULL; + + switch (ctx->current) { + case REMOTE_REPLCIA: + if( !(st->path && st->destpath) ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Rename failed: src or dest path empty"); + rc = -1; + } + if (asprintf(&suri, "%s/%s", ctx->remote.uri, st->path) < 0) { + rc = -1; + } + if (asprintf(&duri, "%s/%s", ctx->remote.uri, st->destpath) < 0) { + rc = -1; + } + break; + case LOCAL_REPLICA: + /* No renaming supported by updater */ + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "RENAME is only supported on local filesystem."); + rc = -1; + break; + default: + break; + } + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Renaming %s => %s", suri, duri); + + if (rc > -1 && csync_vio_rename(ctx, suri, duri) < 0) { + switch (errno) { + default: + strerror_r(errno, errbuf, sizeof(errbuf)); + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, + "dir: %s, command: rename, error: %s", + suri, + errbuf); + rc = -1; + break; + } + goto out; + } + + /* set instruction for the statedb merger */ + if( rc > -1 ) { + st->instruction = CSYNC_INSTRUCTION_RENAME; + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "RENAME file: %s", suri); + } + +out: + SAFE_FREE(suri); + SAFE_FREE(duri); + SAFE_FREE(st->destpath); + + /* set instruction for the statedb merger */ + if (rc != 0) { + st->instruction = CSYNC_INSTRUCTION_NONE; + } + + return rc; +} + static int _csync_sync_file(CSYNC *ctx, csync_file_stat_t *st) { int rc = -1; @@ -947,7 +1009,13 @@ static int _csync_propagation_file_visitor(void *obj, void *data) { goto err; } break; - case CSYNC_INSTRUCTION_SYNC: + case CSYNC_INSTRUCTION_RENAME: + if (_csync_rename_file(ctx, st) < 0) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"FAIL RENAME: %s",st->path); + goto err; + } + break; + case CSYNC_INSTRUCTION_SYNC: if (_csync_sync_file(ctx, st) < 0) { CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"FAIL SYNC: %s",st->path); goto err; diff --git a/src/csync_reconcile.c b/src/csync_reconcile.c index a36b351ef5..cca31792da 100644 --- a/src/csync_reconcile.c +++ b/src/csync_reconcile.c @@ -23,6 +23,8 @@ #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" @@ -43,6 +45,10 @@ 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; @@ -75,7 +81,28 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) { cur->instruction = CSYNC_INSTRUCTION_REMOVE; break; case CSYNC_INSTRUCTION_RENAME: - cur->instruction = CSYNC_INSTRUCTION_NEW; + /* 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;