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:
Olivier Goffart 2013-05-07 15:20:22 +02:00
commit 3c87cd71d7
14 changed files with 381 additions and 81 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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);

View file

@ -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;

View 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 */

View file

@ -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: */

View file

@ -65,6 +65,7 @@
*/
int csync_propagate_files(CSYNC *ctx);
int csync_propagate_rename_file(CSYNC *ctx, csync_file_stat_t *st);
/**
* }@
*/

View file

@ -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
View 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
View 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

View file

@ -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;

View file

@ -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);

View file

@ -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 */

View file

@ -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));