mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-27 17:37:36 +03:00
Merge branch 'rename_folders' into dav
Conflicts: modules/csync_owncloud.c src/csync.c src/csync.h src/csync_propagate.c src/csync_propagate.h src/csync_statedb.c src/csync_statedb.h src/csync_util.c src/std/c_path.h
This commit is contained in:
commit
3c87cd71d7
14 changed files with 381 additions and 81 deletions
|
@ -1872,8 +1872,12 @@ static int owncloud_mkdir(const char *uri, mode_t mode) {
|
|||
set_errno_from_neon_errcode(rc);
|
||||
/* Special for mkcol: it returns 405 if the directory already exists.
|
||||
* To keep csync vio_mkdirs working errno EEXIST has to be returned. */
|
||||
if (errno == EPERM && http_result_code_from_session() == 405)
|
||||
if (errno == EPERM && http_result_code_from_session() == 405) {
|
||||
errno = EEXIST;
|
||||
} else if (rc != NE_OK && _progresscb) {
|
||||
_progresscb(uri, CSYNC_NOTIFY_ERROR, http_result_code_from_session(),
|
||||
(long long)(dav_session.error_string) ,dav_session.userdata);
|
||||
}
|
||||
}
|
||||
SAFE_FREE( path );
|
||||
|
||||
|
@ -1925,14 +1929,22 @@ static int owncloud_rename(const char *olduri, const char *newuri) {
|
|||
if( rc >= 0 ) {
|
||||
DEBUG_WEBDAV("MOVE: %s => %s: %d", src, target, rc );
|
||||
rc = ne_move(dav_session.ctx, 1, src, target );
|
||||
|
||||
set_errno_from_neon_errcode(rc);
|
||||
if (rc == NE_ERROR && http_result_code_from_session() == 409) {
|
||||
/* destination folder might not exist */
|
||||
errno = ENOENT;
|
||||
} else {
|
||||
set_errno_from_neon_errcode(rc);
|
||||
if (rc != NE_OK && _progresscb) {
|
||||
_progresscb(olduri, CSYNC_NOTIFY_ERROR, http_result_code_from_session(),
|
||||
(long long)(dav_session.error_string) ,dav_session.userdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
SAFE_FREE( src );
|
||||
SAFE_FREE( target );
|
||||
|
||||
if( rc != NE_OK )
|
||||
return -1;
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
project(libcsync C)
|
||||
project(libcsync)
|
||||
|
||||
add_subdirectory(std)
|
||||
add_subdirectory(httpbf)
|
||||
|
@ -64,6 +64,8 @@ set(csync_SRCS
|
|||
csync_reconcile.c
|
||||
csync_propagate.c
|
||||
|
||||
csync_rename.cc
|
||||
|
||||
vio/csync_vio.c
|
||||
vio/csync_vio_handle.c
|
||||
vio/csync_vio_file_stat.c
|
||||
|
|
20
src/csync.c
20
src/csync.c
|
@ -56,6 +56,7 @@
|
|||
#include "vio/csync_vio.h"
|
||||
|
||||
#include "csync_log.h"
|
||||
#include "csync_rename.h"
|
||||
|
||||
static int _key_cmp(const void *key, const void *data) {
|
||||
uint64_t a;
|
||||
|
@ -503,6 +504,15 @@ int csync_propagate(CSYNC *ctx) {
|
|||
}
|
||||
ctx->error_code = CSYNC_ERR_NONE;
|
||||
|
||||
ctx->current = REMOTE_REPLICA;
|
||||
ctx->replica = ctx->remote.type;
|
||||
rc = csync_propagate_rename_dirs(ctx);
|
||||
if (rc < 0) {
|
||||
if( ctx->error_code == CSYNC_ERR_NONE )
|
||||
ctx->error_code = csync_errno_to_csync_error( CSYNC_ERR_PROPAGATE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Reconciliation for local replica */
|
||||
csync_gettime(&start);
|
||||
|
||||
|
@ -594,6 +604,7 @@ static int _csync_treewalk_visitor(void *obj, void *data) {
|
|||
trav.instruction = cur->instruction;
|
||||
trav.rename_path = cur->destpath;
|
||||
trav.md5 = cur->md5;
|
||||
trav.error_string = cur->error_string;
|
||||
|
||||
rc = (*visitor)(&trav, twctx->userdata);
|
||||
cur->instruction = trav.instruction;
|
||||
|
@ -687,6 +698,7 @@ static void _tree_destructor(void *data) {
|
|||
freedata = (csync_file_stat_t *) data;
|
||||
SAFE_FREE(freedata->md5);
|
||||
SAFE_FREE(freedata->destpath);
|
||||
SAFE_FREE(freedata->error_string);
|
||||
SAFE_FREE(freedata);
|
||||
}
|
||||
|
||||
|
@ -853,6 +865,12 @@ int csync_destroy(CSYNC *ctx) {
|
|||
ctx->progress = next;
|
||||
}
|
||||
|
||||
while (ctx->progress) {
|
||||
csync_progressinfo_t *next = ctx->progress->next;
|
||||
csync_statedb_free_progressinfo(ctx->progress);
|
||||
ctx->progress = next;
|
||||
}
|
||||
|
||||
/* destroy the rbtrees */
|
||||
if (c_rbtree_size(ctx->local.tree) > 0) {
|
||||
c_rbtree_destroy(ctx->local.tree, _tree_destructor);
|
||||
|
@ -862,6 +880,8 @@ int csync_destroy(CSYNC *ctx) {
|
|||
c_rbtree_destroy(ctx->remote.tree, _tree_destructor);
|
||||
}
|
||||
|
||||
csync_rename_destroy(ctx);
|
||||
|
||||
/* free memory */
|
||||
c_rbtree_free(ctx->local.tree);
|
||||
c_list_free(ctx->local.list);
|
||||
|
|
|
@ -146,6 +146,7 @@ struct csync_tree_walk_file_s {
|
|||
|
||||
const char *rename_path;
|
||||
const char *md5;
|
||||
const char *error_string;
|
||||
};
|
||||
typedef struct csync_tree_walk_file_s TREE_WALK_FILE;
|
||||
|
||||
|
|
|
@ -161,6 +161,7 @@ struct csync_s {
|
|||
char *error_string;
|
||||
|
||||
int status;
|
||||
void *rename_info;
|
||||
};
|
||||
|
||||
|
||||
|
@ -183,6 +184,7 @@ struct csync_file_stat_s {
|
|||
|
||||
char *destpath; /* for renames */
|
||||
const char *md5;
|
||||
const char *error_string;
|
||||
|
||||
enum csync_instructions_e instruction; /* u32 */
|
||||
char path[1]; /* u8 */
|
||||
|
|
|
@ -41,6 +41,14 @@
|
|||
#include "csync_log.h"
|
||||
#include "csync_util.h"
|
||||
#include "csync_misc.h"
|
||||
#include "csync_rename.h"
|
||||
|
||||
static int _csync_build_remote_uri(CSYNC *ctx, char **dst, const char *path) {
|
||||
char *tmp = csync_rename_adjust_path(ctx, path);
|
||||
int ret = asprintf(dst, "%s/%s", ctx->remote.uri, tmp);
|
||||
SAFE_FREE(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _csync_cleanup_cmp(const void *a, const void *b) {
|
||||
csync_file_stat_t *st_a, *st_b;
|
||||
|
@ -51,13 +59,24 @@ static int _csync_cleanup_cmp(const void *a, const void *b) {
|
|||
return strcmp(st_a->path, st_b->path);
|
||||
}
|
||||
|
||||
static void _csync_file_stat_set_error(csync_file_stat_t *st, const char *error)
|
||||
{
|
||||
st->instruction = CSYNC_INSTRUCTION_ERROR;
|
||||
if (st->error_string || !error)
|
||||
return; // do not override first error.
|
||||
st->error_string = c_strdup(error);
|
||||
}
|
||||
|
||||
/* Record the error in the ctx->progress
|
||||
pi may be a previous csync_progressinfo_t from the database.
|
||||
If pi is NULL, a new one is created, else it is re-used
|
||||
*/
|
||||
static void _csync_record_error(CSYNC *ctx, csync_file_stat_t *st, csync_progressinfo_t *pi) {
|
||||
static void _csync_record_error(CSYNC *ctx, csync_file_stat_t *st, csync_progressinfo_t *pi)
|
||||
{
|
||||
_csync_file_stat_set_error(st, csync_get_error_string(ctx));
|
||||
if (pi) {
|
||||
pi->error++;
|
||||
SAFE_FREE(pi->error_string);
|
||||
} else {
|
||||
pi = c_malloc(sizeof(csync_progressinfo_t));
|
||||
pi->chunk = 0;
|
||||
|
@ -68,6 +87,7 @@ static void _csync_record_error(CSYNC *ctx, csync_file_stat_t *st, csync_progres
|
|||
pi->phash = st->phash;
|
||||
pi->error = 1;
|
||||
}
|
||||
pi->error_string = st->error_string ? c_strdup(st->error_string) : NULL;
|
||||
pi->next = ctx->progress;
|
||||
ctx->progress = pi;
|
||||
}
|
||||
|
@ -116,6 +136,7 @@ static int _csync_push_file(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
char *duri = NULL;
|
||||
char *turi = NULL;
|
||||
char *tdir = NULL;
|
||||
char *auri = NULL;
|
||||
const char *tmd5 = NULL;
|
||||
char *prev_tdir = NULL;
|
||||
|
||||
|
@ -153,18 +174,29 @@ static int _csync_push_file(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
hbf_info.transfer_id = progress_info->transferId;
|
||||
}
|
||||
|
||||
csync_progressinfo_t *pi = NULL;
|
||||
pi = csync_statedb_get_progressinfo(ctx, st->phash, st->modtime, st->md5);
|
||||
if (pi && pi->error > 3) {
|
||||
if (!st->error_string && pi->error_string)
|
||||
st->error_string = c_strdup(pi->error_string);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rep_bak = ctx->replica;
|
||||
|
||||
auri = csync_rename_adjust_path(ctx, st->path);
|
||||
|
||||
switch (ctx->current) {
|
||||
case LOCAL_REPLICA:
|
||||
srep = ctx->local.type;
|
||||
drep = ctx->remote.type;
|
||||
if (asprintf(&suri, "%s/%s", ctx->local.uri, st->path) < 0) {
|
||||
if (asprintf(&suri, "%s/%s", ctx->local.uri, auri) < 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
if (asprintf(&duri, "%s/%s", ctx->remote.uri, st->path) < 0) {
|
||||
rc = -1;
|
||||
if (_csync_build_remote_uri(ctx, &duri, st->path) < 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
do_pre_copy_stat = true;
|
||||
|
@ -172,11 +204,11 @@ static int _csync_push_file(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
case REMOTE_REPLICA:
|
||||
srep = ctx->remote.type;
|
||||
drep = ctx->local.type;
|
||||
if (asprintf(&suri, "%s/%s", ctx->remote.uri, st->path) < 0) {
|
||||
if (_csync_build_remote_uri(ctx, &suri, st->path) < 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
if (asprintf(&duri, "%s/%s", ctx->local.uri, st->path) < 0) {
|
||||
if (asprintf(&duri, "%s/%s", ctx->local.uri, auri) < 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
@ -578,7 +610,7 @@ start_fd_based:
|
|||
|
||||
/* For remote repos, after the utimes call, the ID has changed again */
|
||||
/* do a stat on the target again to get a valid md5 */
|
||||
tmd5 = _get_md5(ctx, st->path);
|
||||
tmd5 = _get_md5(ctx, auri);
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "FINAL MD5: %s", tmd5 ? tmd5 : "<null>");
|
||||
|
||||
if(tmd5) {
|
||||
|
@ -604,7 +636,6 @@ out:
|
|||
|
||||
/* set instruction for the statedb merger */
|
||||
if (rc != 0) {
|
||||
st->instruction = CSYNC_INSTRUCTION_ERROR;
|
||||
if (_push_to_tmp_first(ctx)) {
|
||||
if (turi != NULL) {
|
||||
/* Remove the tmp file in error case. */
|
||||
|
@ -624,6 +655,7 @@ out:
|
|||
SAFE_FREE(duri);
|
||||
SAFE_FREE(turi);
|
||||
SAFE_FREE(tdir);
|
||||
SAFE_FREE(auri);
|
||||
|
||||
ctx->replica = rep_bak;
|
||||
|
||||
|
@ -742,7 +774,7 @@ static int _csync_backup_file(CSYNC *ctx, csync_file_stat_t *st, char **duri) {
|
|||
out:
|
||||
/* set instruction for the statedb merger */
|
||||
if (rc != 0) {
|
||||
st->instruction = CSYNC_INSTRUCTION_ERROR;
|
||||
_csync_file_stat_set_error(st, csync_get_error_string(ctx));
|
||||
}
|
||||
|
||||
SAFE_FREE(suri);
|
||||
|
@ -768,12 +800,26 @@ static int _csync_rename_file(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
char *duri = NULL;
|
||||
const char *tmd5 = NULL;
|
||||
c_rbnode_t *node = NULL;
|
||||
|
||||
char *tdir = NULL;
|
||||
csync_file_stat_t *other = NULL;
|
||||
csync_progressinfo_t *pi = NULL;
|
||||
pi = csync_statedb_get_progressinfo(ctx, st->phash, st->modtime, st->md5);
|
||||
if (pi && pi->error > 3) {
|
||||
rc = 1;
|
||||
goto out;
|
||||
|
||||
/* Find the destination entry in the local tree */
|
||||
uint64_t h = c_jhash64((uint8_t *) st->destpath, strlen(st->destpath), 0);
|
||||
node = c_rbtree_find(ctx->local.tree, &h);
|
||||
if(node)
|
||||
other = (csync_file_stat_t *) node->data;
|
||||
|
||||
if (other) {
|
||||
pi = csync_statedb_get_progressinfo(ctx, other->phash, other->modtime, other->md5);
|
||||
if (pi && pi->error > 3) {
|
||||
if (!st->error_string && pi->error_string)
|
||||
st->error_string = c_strdup(pi->error_string);
|
||||
if (!other->error_string && pi->error_string)
|
||||
other->error_string = c_strdup(pi->error_string);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ctx->current) {
|
||||
|
@ -782,10 +828,10 @@ static int _csync_rename_file(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
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) {
|
||||
if (_csync_build_remote_uri(ctx, &suri, st->path) < 0) {
|
||||
rc = -1;
|
||||
}
|
||||
if (asprintf(&duri, "%s/%s", ctx->remote.uri, st->destpath) < 0) {
|
||||
if (_csync_build_remote_uri(ctx, &duri, st->destpath) < 0) {
|
||||
rc = -1;
|
||||
}
|
||||
break;
|
||||
|
@ -800,52 +846,62 @@ static int _csync_rename_file(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
}
|
||||
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;
|
||||
if (! c_streq(suri, duri) && rc > -1) {
|
||||
while ((rc = csync_vio_rename(ctx, suri, duri)) != 0) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
/* get the directory name */
|
||||
if(tdir) {
|
||||
/* we're looping */
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN,
|
||||
"dir: %s, loop in mkdir detected!", tdir);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
tdir = c_dirname(duri);
|
||||
if (tdir == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (csync_vio_mkdirs(ctx, tdir, C_DIR_MODE) < 0) {
|
||||
strerror_r(errno, errbuf, sizeof(errbuf));
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN,
|
||||
"dir: %s, command: mkdirs, error: %s",
|
||||
tdir, errbuf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
strerror_r(errno, errbuf, sizeof(errbuf));
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
|
||||
"dir: %s, command: rename, error: %s",
|
||||
suri,
|
||||
errbuf);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
goto out;
|
||||
|
||||
/* set owner and group if possible */
|
||||
if (ctx->pwd.euid == 0) {
|
||||
csync_vio_chown(ctx, duri, st->uid, st->gid);
|
||||
}
|
||||
|
||||
/* sync time */
|
||||
times[0].tv_sec = times[1].tv_sec = st->modtime;
|
||||
times[0].tv_usec = times[1].tv_usec = 0;
|
||||
|
||||
csync_vio_utimes(ctx, duri, times);
|
||||
|
||||
}
|
||||
|
||||
/* set owner and group if possible */
|
||||
if (ctx->pwd.euid == 0) {
|
||||
csync_vio_chown(ctx, duri, st->uid, st->gid);
|
||||
}
|
||||
|
||||
/* sync time */
|
||||
times[0].tv_sec = times[1].tv_sec = st->modtime;
|
||||
times[0].tv_usec = times[1].tv_usec = 0;
|
||||
|
||||
csync_vio_utimes(ctx, duri, times);
|
||||
|
||||
/* The the uniq ID for the destination */
|
||||
tmd5 = _get_md5(ctx, st->destpath);
|
||||
if (st->type != CSYNC_FTW_TYPE_DIR)
|
||||
tmd5 = _get_md5(ctx, st->destpath);
|
||||
|
||||
if( rc > -1 ) {
|
||||
/* Find the destination entry in the local tree and insert the uniq id */
|
||||
int len = strlen(st->destpath);
|
||||
uint64_t h = c_jhash64((uint8_t *) st->destpath, len, 0);
|
||||
h = c_jhash64((uint8_t *) st->destpath, len, 0);
|
||||
|
||||
/* search in the local tree for the local file to get the mtime */
|
||||
node = c_rbtree_find(ctx->local.tree, &h);
|
||||
if(node == NULL) {
|
||||
/* no local file found. */
|
||||
|
||||
} else {
|
||||
csync_file_stat_t *other = NULL;
|
||||
/* set the mtime which is needed in statedb_get_uniqid */
|
||||
other = (csync_file_stat_t *) node->data;
|
||||
if( other ) {
|
||||
other->md5 = tmd5;
|
||||
}
|
||||
/* set the mtime which is needed in statedb_get_uniqid */
|
||||
if( other ) {
|
||||
other->md5 = tmd5;
|
||||
}
|
||||
/* set instruction for the statedb merger */
|
||||
st->instruction = CSYNC_INSTRUCTION_DELETED;
|
||||
|
@ -855,13 +911,19 @@ static int _csync_rename_file(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
out:
|
||||
SAFE_FREE(suri);
|
||||
SAFE_FREE(duri);
|
||||
SAFE_FREE(tdir);
|
||||
|
||||
/* set instruction for the statedb merger */
|
||||
if (rc != 0) {
|
||||
st->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
_csync_file_stat_set_error(st, csync_get_error_string(ctx));
|
||||
if (other) {
|
||||
|
||||
_csync_record_error(ctx, st, pi);
|
||||
pi = NULL;
|
||||
/* We set the instruction to UPDATED so next try we try to rename again */
|
||||
st->instruction = CSYNC_INSTRUCTION_UPDATED;
|
||||
|
||||
_csync_record_error(ctx, other, pi);
|
||||
pi = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
csync_statedb_free_progressinfo(pi);
|
||||
|
@ -919,6 +981,8 @@ static int _csync_remove_file(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
pi = csync_statedb_get_progressinfo(ctx, st->phash, st->modtime, st->md5);
|
||||
if (pi && pi->error > 3) {
|
||||
rc = 1;
|
||||
if (!st->error_string && pi->error_string)
|
||||
st->error_string = c_strdup(pi->error_string);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -930,7 +994,7 @@ static int _csync_remove_file(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
}
|
||||
break;
|
||||
case REMOTE_REPLICA:
|
||||
if (asprintf(&uri, "%s/%s", ctx->remote.uri, st->path) < 0) {
|
||||
if (_csync_build_remote_uri(ctx, &uri, st->path) < 0) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
@ -987,6 +1051,8 @@ static int _csync_new_dir(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
csync_progressinfo_t *pi = NULL;
|
||||
pi = csync_statedb_get_progressinfo(ctx, st->phash, st->modtime, st->md5);
|
||||
if (pi && pi->error > 3) {
|
||||
if (!st->error_string && pi->error_string)
|
||||
st->error_string = c_strdup(pi->error_string);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
@ -996,7 +1062,7 @@ static int _csync_new_dir(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
switch (ctx->current) {
|
||||
case LOCAL_REPLICA:
|
||||
dest = ctx->remote.type;
|
||||
if (asprintf(&uri, "%s/%s", ctx->remote.uri, st->path) < 0) {
|
||||
if (_csync_build_remote_uri(ctx, &uri, st->path) < 0) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
@ -1070,7 +1136,6 @@ out:
|
|||
|
||||
/* set instruction for the statedb merger */
|
||||
if (rc != 0) {
|
||||
st->instruction = CSYNC_INSTRUCTION_ERROR;
|
||||
_csync_record_error(ctx, st, pi);
|
||||
pi = NULL;
|
||||
}
|
||||
|
@ -1090,6 +1155,8 @@ static int _csync_sync_dir(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
csync_progressinfo_t *pi = NULL;
|
||||
pi = csync_statedb_get_progressinfo(ctx, st->phash, st->modtime, st->md5);
|
||||
if (pi && pi->error > 3) {
|
||||
if (!st->error_string && pi->error_string)
|
||||
st->error_string = c_strdup(pi->error_string);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1099,7 +1166,7 @@ static int _csync_sync_dir(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
switch (ctx->current) {
|
||||
case LOCAL_REPLICA:
|
||||
dest = ctx->remote.type;
|
||||
if (asprintf(&uri, "%s/%s", ctx->remote.uri, st->path) < 0) {
|
||||
if (_csync_build_remote_uri(ctx, &uri, st->path) < 0) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
@ -1157,7 +1224,6 @@ out:
|
|||
|
||||
/* set instruction for the statedb merger */
|
||||
if (rc != 0) {
|
||||
st->instruction = CSYNC_INSTRUCTION_ERROR;
|
||||
_csync_record_error(ctx, st, pi);
|
||||
pi = NULL;
|
||||
}
|
||||
|
@ -1202,7 +1268,7 @@ static int _csync_remove_dir(CSYNC *ctx, csync_file_stat_t *st) {
|
|||
}
|
||||
break;
|
||||
case REMOTE_REPLICA:
|
||||
if (asprintf(&uri, "%s/%s", ctx->remote.uri, st->path) < 0) {
|
||||
if (_csync_build_remote_uri(ctx, &uri, st->path) < 0) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
@ -1448,6 +1514,10 @@ err:
|
|||
return -1;
|
||||
}
|
||||
|
||||
int csync_propagate_rename_file(CSYNC *ctx, csync_file_stat_t *st) {
|
||||
return _csync_rename_file(ctx, st);
|
||||
}
|
||||
|
||||
int csync_propagate_files(CSYNC *ctx) {
|
||||
c_rbtree_t *tree = NULL;
|
||||
|
||||
|
@ -1476,4 +1546,5 @@ int csync_propagate_files(CSYNC *ctx) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* vim: set ts=8 sw=2 et cindent: */
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
*/
|
||||
int csync_propagate_files(CSYNC *ctx);
|
||||
|
||||
int csync_propagate_rename_file(CSYNC *ctx, csync_file_stat_t *st);
|
||||
/**
|
||||
* }@
|
||||
*/
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "csync_reconcile.h"
|
||||
#include "csync_util.h"
|
||||
#include "csync_statedb.h"
|
||||
#include "csync_rename.h"
|
||||
#include "c_jhash.h"
|
||||
|
||||
#define CSYNC_LOG_CATEGORY_NAME "csync.reconciler"
|
||||
|
@ -72,6 +73,18 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
|
|||
}
|
||||
|
||||
node = c_rbtree_find(tree, &cur->phash);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* file only found on current replica */
|
||||
if (node == NULL) {
|
||||
switch(cur->instruction) {
|
||||
|
@ -100,13 +113,19 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
|
|||
}
|
||||
if(node) {
|
||||
other = (csync_file_stat_t*)node->data;
|
||||
}
|
||||
if(!other) {
|
||||
cur->instruction = CSYNC_INSTRUCTION_NEW;
|
||||
} else if (other->instruction == CSYNC_INSTRUCTION_NONE
|
||||
|| cur->type == CSYNC_FTW_TYPE_DIR) {
|
||||
other->instruction = CSYNC_INSTRUCTION_RENAME;
|
||||
other->destpath = c_strdup( cur->path );
|
||||
cur->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
} else {
|
||||
cur->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
other->instruction = CSYNC_INSTRUCTION_SYNC;
|
||||
}
|
||||
if( ! other ) {
|
||||
cur->instruction = CSYNC_INSTRUCTION_NEW;
|
||||
}
|
||||
|
||||
SAFE_FREE(tmp->md5);
|
||||
SAFE_FREE(tmp);
|
||||
}
|
||||
|
@ -123,6 +142,8 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
|
|||
|
||||
switch (cur->instruction) {
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
if(ctx->current != LOCAL_REPLICA )
|
||||
break;
|
||||
/* 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;
|
||||
|
|
121
src/csync_rename.cc
Normal file
121
src/csync_rename.cc
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2012 by Olivier Goffart <ogoffart@woboq.com>
|
||||
*
|
||||
* This program is free software = NULL, you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation = NULL, 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 = NULL, 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 = NULL, if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include "csync_private.h"
|
||||
#include "csync_propagate.h"
|
||||
}
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
static std::string _parentDir(const std::string &path) {
|
||||
int len = path.length();
|
||||
while(len > 0 && path[len-1]!='/') len--;
|
||||
while(len > 0 && path[len-1]=='/') len--;
|
||||
return path.substr(0, len);
|
||||
}
|
||||
|
||||
struct csync_rename_s {
|
||||
static csync_rename_s *get(CSYNC *ctx) {
|
||||
if (!ctx->rename_info) {
|
||||
ctx->rename_info = new csync_rename_s;
|
||||
}
|
||||
return reinterpret_cast<csync_rename_s *>(ctx->rename_info);
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> folder_renamed_to; // map from->to
|
||||
|
||||
struct renameop {
|
||||
csync_file_stat_t *st;
|
||||
bool operator<(const renameop &other) const {
|
||||
return strlen(st->destpath) < strlen(other.st->destpath);
|
||||
}
|
||||
};
|
||||
std::vector<renameop> todo;
|
||||
};
|
||||
|
||||
static int _csync_rename_dir_record(void *obj, void *data) {
|
||||
CSYNC *ctx = reinterpret_cast<CSYNC*>(data);
|
||||
csync_rename_s* d = csync_rename_s::get(ctx);
|
||||
csync_file_stat_t *st = reinterpret_cast<csync_file_stat_t *>(obj);
|
||||
|
||||
if (st->type != CSYNC_FTW_TYPE_DIR || st->instruction != CSYNC_INSTRUCTION_RENAME)
|
||||
return 0;
|
||||
|
||||
csync_rename_s::renameop op = { st };
|
||||
d->todo.push_back(op);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void csync_rename_destroy(CSYNC* ctx)
|
||||
{
|
||||
delete reinterpret_cast<csync_rename_s *>(ctx->rename_info);
|
||||
ctx->rename_info = 0;
|
||||
}
|
||||
|
||||
void csync_rename_record(CSYNC* ctx, const char* from, const char* to)
|
||||
{
|
||||
csync_rename_s::get(ctx)->folder_renamed_to[from] = to;
|
||||
}
|
||||
|
||||
char* csync_rename_adjust_path(CSYNC* ctx, const char* path)
|
||||
{
|
||||
csync_rename_s* d = csync_rename_s::get(ctx);
|
||||
for (std::string p = _parentDir(path); !p.empty(); p = _parentDir(p)) {
|
||||
std::map< std::string, std::string >::iterator it = d->folder_renamed_to.find(p);
|
||||
if (it != d->folder_renamed_to.end()) {
|
||||
std::string rep = it->second + (path + p.length());
|
||||
return c_strdup(rep.c_str());
|
||||
}
|
||||
}
|
||||
return c_strdup(path);
|
||||
}
|
||||
|
||||
int csync_propagate_rename_dirs(CSYNC* ctx)
|
||||
{
|
||||
csync_rename_s* d = csync_rename_s::get(ctx);
|
||||
d->folder_renamed_to.clear();
|
||||
|
||||
if (c_rbtree_walk(ctx->remote.tree, (void *) ctx, _csync_rename_dir_record) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// we need to procceed in order of the size of the destpath to be sure that we do the roots first.
|
||||
std::sort(d->todo.begin(), d->todo.end());
|
||||
for (std::vector< csync_rename_s::renameop >::iterator it = d->todo.begin();
|
||||
it != d->todo.end(); ++it) {
|
||||
|
||||
int r = csync_propagate_rename_file(ctx, it->st);
|
||||
if (r < 0)
|
||||
return -1;
|
||||
if (r > 0)
|
||||
continue;
|
||||
d->folder_renamed_to[it->st->path] = it->st->destpath;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
};
|
36
src/csync_rename.h
Normal file
36
src/csync_rename.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2012 by Olivier Goffart <ogoffart@woboq.com>
|
||||
*
|
||||
* This program is free software = NULL, you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation = NULL, 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 = NULL, 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 = NULL, if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "csync.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char *csync_rename_adjust_path(CSYNC *ctx, const char *path);
|
||||
void csync_rename_destroy(CSYNC *ctx);
|
||||
void csync_rename_record(CSYNC *ctx, const char *from, const char *to);
|
||||
int csync_propagate_rename_dirs(CSYNC* ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -385,6 +385,7 @@ int csync_statedb_create_tables(CSYNC *ctx) {
|
|||
"transferid INTEGER(4),"
|
||||
"error_count INTEGER(8),"
|
||||
"tmpfile VARCHAR(4096),"
|
||||
"error_string VARCHAR(4096),"
|
||||
"PRIMARY KEY(phash)"
|
||||
");"
|
||||
);
|
||||
|
@ -901,7 +902,7 @@ csync_progressinfo_t* csync_statedb_get_progressinfo(CSYNC *ctx, uint64_t phash,
|
|||
c_strlist_t *result = NULL;
|
||||
|
||||
if( ! csync_get_statedb_exists(ctx)) return ret;
|
||||
stmt = sqlite3_mprintf("SELECT error_count, chunk, transferid, tmpfile FROM progress WHERE phash='%llu' AND modtime='%lld' AND md5='%q'",
|
||||
stmt = sqlite3_mprintf("SELECT error_count, chunk, transferid, tmpfile, error_string FROM progress WHERE phash='%llu' AND modtime='%lld' AND md5='%q'",
|
||||
(long long unsigned int) phash, (long long signed int) modtime, md5);
|
||||
if (!stmt) return ret;
|
||||
|
||||
|
@ -911,17 +912,18 @@ csync_progressinfo_t* csync_statedb_get_progressinfo(CSYNC *ctx, uint64_t phash,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (result->count == 4) {
|
||||
if (result->count == 5) {
|
||||
ret = c_malloc(sizeof(csync_progressinfo_t));
|
||||
if (!ret) goto out;
|
||||
ret->next = NULL;
|
||||
ret->error = atoi(result->vector[0]);
|
||||
ret->chunk = atoi(result->vector[1]);
|
||||
ret->error = atoi(result->vector[0]);
|
||||
ret->transferId = atoi(result->vector[2]);
|
||||
ret->tmpfile = c_strdup(result->vector[3]);
|
||||
ret->md5 = md5 ? c_strdup(md5) : NULL;
|
||||
ret->modtime = modtime;
|
||||
ret->phash = phash;
|
||||
ret->error_string = c_strdup(result->vector[4]);
|
||||
}
|
||||
out:
|
||||
c_strlist_destroy(result);
|
||||
|
@ -933,6 +935,7 @@ void csync_statedb_free_progressinfo(csync_progressinfo_t* pi)
|
|||
if (!pi) return;
|
||||
SAFE_FREE(pi->md5);
|
||||
SAFE_FREE(pi->tmpfile);
|
||||
SAFE_FREE(pi->error_string);
|
||||
SAFE_FREE(pi);
|
||||
}
|
||||
|
||||
|
@ -943,15 +946,18 @@ int csync_statedb_write_progressinfo(CSYNC* ctx, csync_progressinfo_t* pi)
|
|||
|
||||
while (rc > -1 && pi) {
|
||||
stmt = sqlite3_mprintf("INSERT INTO progress "
|
||||
"(phash, modtime, md5, chunk, transferid, error_count, tmpfile) VALUES"
|
||||
"(%llu, %lld, '%q', %d, %d, %d, '%q');",
|
||||
"(phash, modtime, md5, chunk, transferid, error_count, tmpfile, error_string) VALUES"
|
||||
"(%llu, %lld, '%q', %d, %d, %d, '%q', '%q');",
|
||||
(long long signed int) pi->phash,
|
||||
(long long int) pi->modtime,
|
||||
pi->md5,
|
||||
pi->chunk,
|
||||
pi->transferId,
|
||||
pi->error,
|
||||
pi->tmpfile);
|
||||
pi->error,
|
||||
pi->tmpfile,
|
||||
pi->error_string
|
||||
);
|
||||
|
||||
if (stmt == NULL) {
|
||||
return -1;
|
||||
|
|
|
@ -119,6 +119,7 @@ typedef struct csync_progressinfo_s {
|
|||
int chunk;
|
||||
int transferId;
|
||||
char *tmpfile;
|
||||
char *error_string;
|
||||
} csync_progressinfo_t;
|
||||
|
||||
csync_progressinfo_t *csync_statedb_get_progressinfo(CSYNC *ctx, uint64_t phash, uint64_t modtime, const char *md5);
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#define CSYNC_LOG_CATEGORY_NAME "csync.updater"
|
||||
#include "csync_log.h"
|
||||
#include "csync_rename.h"
|
||||
|
||||
/* calculate the hash of a given uri */
|
||||
static uint64_t _hash_of_file(CSYNC *ctx, const char *file) {
|
||||
|
@ -197,12 +198,15 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
|||
st->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
} else {
|
||||
/* check if it's a file and has been renamed */
|
||||
if (type == CSYNC_FTW_TYPE_FILE && ctx->current == LOCAL_REPLICA) {
|
||||
if (ctx->current == LOCAL_REPLICA) {
|
||||
tmp = csync_statedb_get_stat_by_inode(ctx, fs->inode);
|
||||
if (tmp && tmp->inode == fs->inode) {
|
||||
if (tmp && tmp->inode == fs->inode && (tmp->modtime == fs->mtime || fs->type == CSYNC_FTW_TYPE_DIR)) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "inodes: %ld <-> %ld", tmp->inode, fs->inode);
|
||||
/* inode found so the file has been renamed */
|
||||
st->instruction = CSYNC_INSTRUCTION_RENAME;
|
||||
if (fs->type == CSYNC_FTW_TYPE_DIR) {
|
||||
csync_rename_record(ctx, tmp->path, path);
|
||||
}
|
||||
goto out;
|
||||
} else {
|
||||
/* file not found in statedb */
|
||||
|
|
|
@ -156,6 +156,8 @@ static int _merge_file_trees_visitor(void *obj, void *data) {
|
|||
new_stat->md5 = c_strdup(fs->md5);
|
||||
if (fs->destpath)
|
||||
new_stat->destpath = c_strdup(fs->destpath);
|
||||
if (fs->error_string)
|
||||
new_stat->error_string = c_strdup(fs->error_string);
|
||||
|
||||
if (c_rbtree_insert(tree, new_stat) < 0) {
|
||||
strerror_r(errno, errbuf, sizeof(errbuf));
|
||||
|
|
Loading…
Reference in a new issue