nextcloud-desktop/src/csync_propagate.c

1470 lines
36 KiB
C
Raw Normal View History

/*
* 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"
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include "csync_private.h"
#include "csync_propagate.h"
#include "vio/csync_vio.h"
#include "c_jhash.h"
#define CSYNC_LOG_CATEGORY_NAME "csync.propagator"
#include "csync_log.h"
#include "csync_util.h"
static int _csync_cleanup_cmp(const void *a, const void *b) {
csync_file_stat_t *st_a, *st_b;
st_a = (csync_file_stat_t *) a;
st_b = (csync_file_stat_t *) b;
return strcmp(st_a->path, st_b->path);
}
static void _store_id_update(CSYNC *ctx, csync_file_stat_t *st) {
c_list_t *list = NULL;
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "SYNCED remember dir: %s", st->path);
switch (ctx->current) {
case LOCAL_REPLICA:
list = c_list_prepend(ctx->local.id_list, (void*)st);
if( list != NULL ) {
ctx->local.id_list = list;
}
break;
case REMOTE_REPLICA:
list = c_list_prepend(ctx->remote.id_list, (void*)st);
if(list != NULL ) {
ctx->remote.id_list = list;
}
break;
}
}
static bool _push_to_tmp_first(CSYNC *ctx)
{
if( !ctx ) return true;
if( ctx->current == REMOTE_REPLICA ) return true; /* Always push to tmp for destination local file system */
/* If destination is the remote replica check if the switch is set. */
if( !ctx->module.capabilities.atomar_copy_support ) return true;
return false;
}
static bool _use_fd_based_push(CSYNC *ctx)
{
if(!ctx) return false;
if( ctx->module.capabilities.use_send_file_to_propagate ) return true;
return false;
}
static const char*_get_md5( CSYNC *ctx, const char *path ) {
const char *md5 = NULL;
char *buf = NULL;
2012-08-23 18:40:36 +04:00
/* Always use the remote uri path, local does not have Ids. */
if (asprintf(&buf, "%s/%s", ctx->remote.uri, path) < 0) {
return 0;
2012-08-23 18:40:36 +04:00
}
md5 = csync_vio_file_id(ctx, buf);
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "MD5 for %s: %s", buf, md5 ? md5 : "<null>");
SAFE_FREE(buf);
return md5;
2012-08-23 18:40:36 +04:00
}
static int _csync_push_file(CSYNC *ctx, csync_file_stat_t *st) {
enum csync_replica_e srep = -1;
enum csync_replica_e drep = -1;
enum csync_replica_e rep_bak = -1;
char *suri = NULL;
char *duri = NULL;
char *turi = NULL;
char *tdir = NULL;
const char *tmd5 = NULL;
char *prev_tdir = NULL;
csync_vio_handle_t *sfp = NULL;
csync_vio_handle_t *dfp = NULL;
csync_vio_file_stat_t *tstat = NULL;
char errbuf[256] = {0};
char buf[MAX_XFER_BUF_SIZE] = {0};
ssize_t bread = 0;
ssize_t bwritten = 0;
struct timeval times[2];
int rc = -1;
int count = 0;
int flags = 0;
rep_bak = ctx->replica;
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) {
rc = -1;
goto out;
}
if (asprintf(&duri, "%s/%s", ctx->remote.uri, st->path) < 0) {
rc = -1;
goto out;
}
break;
case REMOTE_REPLICA:
srep = ctx->remote.type;
drep = ctx->local.type;
if (asprintf(&suri, "%s/%s", ctx->remote.uri, st->path) < 0) {
rc = -1;
goto out;
}
if (asprintf(&duri, "%s/%s", ctx->local.uri, st->path) < 0) {
rc = -1;
goto out;
}
break;
default:
break;
}
/* Open the source file */
ctx->replica = srep;
flags = O_RDONLY|O_NOFOLLOW;
#ifdef O_NOATIME
/* O_NOATIME can only be set by the owner of the file or the superuser */
if (st->uid == ctx->pwd.uid || ctx->pwd.euid == 0) {
flags |= O_NOATIME;
}
2012-07-19 23:11:35 +04:00
#endif
2008-11-12 23:14:35 +03:00
sfp = csync_vio_open(ctx, suri, flags, 0);
if (sfp == NULL) {
if (errno == ENOMEM) {
rc = -1;
} else {
rc = 1;
}
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: open(O_RDONLY), error: %s",
suri, errbuf );
goto out;
}
if (_push_to_tmp_first(ctx)) {
/* create the temporary file name */
#ifdef _WIN32
if (asprintf(&turi, "%s.~XXXXXX", duri) < 0) {
#else
/* split up the path */
int re = 0;
if(duri) {
char *path = c_dirname(duri);
char *base = c_basename(duri);
if( path ) {
re = asprintf(&turi, "%s/.%s.~XXXXXX", path, base);
} else {
re = asprintf(&turi,".%s.~XXXXXX", base);
}
SAFE_FREE(path);
SAFE_FREE(base);
}
if (re < 0) {
#endif
rc = -1;
goto out;
}
/* We just want a random file name here, open checks if the file exists. */
if (c_tmpname(turi) < 0) {
rc = -1;
goto out;
}
} else {
/* write to the target file directly as the HTTP server does it atomically */
if (asprintf(&turi, "%s", duri) < 0) {
rc = -1;
goto out;
}
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,
"Remote repository atomar push enabled for %s (%d).", turi, ctx->current);
}
2008-06-05 14:02:37 +04:00
/* Create the destination file */
ctx->replica = drep;
while ((dfp = csync_vio_open(ctx, turi, O_CREAT|O_EXCL|O_WRONLY|O_NOCTTY,
C_FILE_MODE)) == NULL) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,
"file: %s, command: open(O_CREAT), error: %d",
duri, errno);
switch (errno) {
case EEXIST:
if (count++ > 10) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: open(O_CREAT), error: max count exceeded",
duri);
rc = 1;
goto out;
}
if(_push_to_tmp_first(ctx)) {
if (c_tmpname(turi) < 0) {
rc = -1;
goto out;
}
}
break;
case ENOENT:
/* get the directory name */
2012-12-15 02:05:17 +04:00
SAFE_FREE(tdir);
tdir = c_dirname(turi);
if (tdir == NULL) {
rc = -1;
goto out;
}
if( prev_tdir && c_streq(tdir, prev_tdir) ) {
/* we're looping */
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN,
"dir: %s, loop in mkdir detected!", tdir);
rc = 1;
goto out;
}
SAFE_FREE(prev_tdir);
prev_tdir = c_strdup(tdir);
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;
case ENOMEM:
rc = -1;
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: open(O_CREAT), error: %s",
turi, errbuf);
goto out;
break;
default:
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: open(O_CREAT), error: %s",
turi, errbuf);
rc = 1;
goto out;
break;
}
}
/* copy file */
if( _use_fd_based_push(ctx) ) {
2012-12-10 21:58:46 +04:00
if (ctx->current == REMOTE_REPLICA)
csync_win32_set_file_hidden(turi, true);
rc = csync_vio_sendfile( ctx, sfp, dfp );
2012-12-10 21:58:46 +04:00
2013-01-16 14:42:13 +04:00
if (ctx->current == REMOTE_REPLICA)
csync_win32_set_file_hidden(turi, false);
2012-12-10 21:58:46 +04:00
if( rc != 0 ) {
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: sendfile, error: %s from errno %d",
suri, errbuf, errno);
goto out;
}
} else {
for (;;) {
ctx->replica = srep;
bread = csync_vio_read(ctx, sfp, buf, MAX_XFER_BUF_SIZE);
if (bread < 0) {
/* read error */
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: read, error: %s",
suri, errbuf);
rc = 1;
goto out;
} else if (bread == 0) {
/* done */
break;
}
ctx->replica = drep;
bwritten = csync_vio_write(ctx, dfp, buf, bread);
if (bwritten < 0 || bread != bwritten) {
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: write, error: bread = %zu, bwritten = %zu - %s",
duri,
bread,
bwritten,
errbuf);
rc = 1;
goto out;
}
}
}
2008-06-18 18:30:00 +04:00
ctx->replica = srep;
if (csync_vio_close(ctx, sfp) < 0) {
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: close, error: %s",
suri,
errbuf);
}
2008-06-18 18:30:00 +04:00
sfp = NULL;
ctx->replica = drep;
if (csync_vio_close(ctx, dfp) < 0) {
dfp = NULL;
switch (errno) {
2012-12-19 15:31:56 +04:00
/* stop if no space left or quota exceeded */
case ENOSPC:
case EDQUOT:
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: close, error: %s",
turi,
errbuf);
rc = -1;
goto out;
break;
default:
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: close, error: %s",
turi,
errbuf);
break;
}
}
2008-06-18 18:30:00 +04:00
dfp = NULL;
if( ctx->module.capabilities.do_post_copy_stat ) {
/*
* Check filesize
* In case the transport is secure and/or the stat is expensive, this check
* could be skipped through module capabilities definitions.
*/
ctx->replica = drep;
tstat = csync_vio_file_stat_new();
if (tstat == NULL) {
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: stat, error: %s",
turi,
errbuf);
rc = -1;
goto out;
}
if (csync_vio_stat(ctx, turi, tstat) < 0) {
switch (errno) {
case ENOMEM:
rc = -1;
break;
default:
rc = 1;
break;
}
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: stat, error: %s",
turi,
errbuf);
goto out;
}
if (st->size != tstat->size) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, error: incorrect filesize (size: %jd should be %jd)",
turi, tstat->size, st->size);
rc = 1;
goto out;
}
2012-07-31 12:40:46 +04:00
if( st->md5 ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "UUUU MD5 sum: %s", st->md5);
} else {
2012-08-23 18:40:36 +04:00
if( tstat->md5 ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Target MD5 sum is %s", tstat->md5 );
if(st->md5) SAFE_FREE(st->md5);
2012-08-23 18:40:36 +04:00
st->md5 = c_strdup(tstat->md5 );
} else {
2012-07-31 12:40:46 +04:00
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "MD5 sum is empty");
2012-08-23 18:40:36 +04:00
}
2012-07-31 12:40:46 +04:00
}
}
if (_push_to_tmp_first(ctx)) {
/* override original file */
ctx->replica = drep;
if (csync_vio_rename(ctx, turi, duri) < 0) {
switch (errno) {
case ENOMEM:
rc = -1;
break;
default:
rc = 1;
break;
}
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: rename, error: %s",
duri,
errbuf);
goto out;
}
}
/* set mode only if it is not the default mode */
if ((st->mode & 07777) != C_FILE_MODE) {
if (csync_vio_chmod(ctx, duri, st->mode) < 0) {
switch (errno) {
case ENOMEM:
rc = -1;
break;
default:
rc = 1;
break;
}
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: chmod, error: %s",
duri,
errbuf);
goto out;
}
}
/* set owner and group if possible */
if (ctx->pwd.euid == 0) {
2008-07-03 13:34:34 +04:00
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;
2008-05-19 13:21:44 +04:00
ctx->replica = drep;
csync_vio_utimes(ctx, duri, times);
/* For remote repos, after the utimes call, the ID has changed again */
2012-08-23 18:40:36 +04:00
/* do a stat on the target again to get a valid md5 */
tmd5 = _get_md5(ctx, st->path);
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "FINAL MD5: %s", tmd5 ? tmd5 : "<null>");
if(tmd5) {
SAFE_FREE(st->md5);
st->md5 = tmd5;
}
2012-08-23 18:40:36 +04:00
2008-09-02 13:43:29 +04:00
/* set instruction for the statedb merger */
st->instruction = CSYNC_INSTRUCTION_UPDATED;
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "PUSHED file: %s", duri);
rc = 0;
if (ctx->current == LOCAL_REPLICA) {
_store_id_update(ctx, st);
}
out:
ctx->replica = srep;
csync_vio_close(ctx, sfp);
ctx->replica = drep;
csync_vio_close(ctx, dfp);
csync_vio_file_stat_destroy(tstat);
2008-09-02 13:43:29 +04:00
/* set instruction for the statedb merger */
if (rc != 0) {
st->instruction = CSYNC_INSTRUCTION_ERROR;
if (turi != NULL) {
if (_push_to_tmp_first(ctx)) {
/* Remove the tmp file in error case. */
csync_vio_unlink(ctx, turi);
}
}
}
SAFE_FREE(prev_tdir);
SAFE_FREE(suri);
SAFE_FREE(duri);
SAFE_FREE(turi);
SAFE_FREE(tdir);
ctx->replica = rep_bak;
return rc;
}
2012-10-27 18:05:16 +04:00
static int _backup_path(CSYNC *ctx, char** duri, const char* uri, const char* path)
{
int rc=0;
C_PATHINFO *info=NULL;
2012-02-04 15:37:33 +04:00
struct tm *curtime;
time_t sec;
char timestring[16];
time(&sec);
curtime = localtime(&sec);
strftime(timestring, 16, "%Y%m%d-%H%M%S",curtime);
2012-02-04 15:37:33 +04:00
info=c_split_path(path);
2011-04-06 19:05:31 +04:00
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"directory: %s",info->directory);
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"filename : %s",info->filename);
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"extension: %s",info->extension);
2012-02-04 15:37:33 +04:00
if (asprintf(duri, "%s/%s%s_conflict-%s%s", uri,info->directory ,info->filename,timestring,info->extension) < 0) {
2012-02-04 15:37:33 +04:00
rc = -1;
}
SAFE_FREE(info);
return rc;
}
static int _csync_backup_file(CSYNC *ctx, csync_file_stat_t *st) {
enum csync_replica_e drep = -1;
enum csync_replica_e rep_bak = -1;
char *suri = NULL;
char *duri = NULL;
char errbuf[256] = {0};
int rc = -1;
rep_bak = ctx->replica;
if(st->instruction==CSYNC_INSTRUCTION_CONFLICT)
{
2011-04-06 19:05:31 +04:00
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"CSYNC_INSTRUCTION_CONFLICT");
switch (ctx->current) {
case LOCAL_REPLICA:
drep = ctx->remote.type;
if (asprintf(&suri, "%s/%s", ctx->remote.uri, st->path) < 0) {
rc = -1;
goto out;
}
2012-10-27 18:05:16 +04:00
if (_backup_path(ctx, &duri, ctx->remote.uri,st->path) < 0) {
rc = -1;
goto out;
}
break;
case REMOTE_REPLICA:
drep = ctx->local.type;
if (asprintf(&suri, "%s/%s", ctx->local.uri, st->path) < 0) {
rc = -1;
goto out;
}
2012-10-27 18:05:16 +04:00
if ( _backup_path(ctx, &duri, ctx->local.uri, st->path) < 0) {
rc = -1;
goto out;
}
break;
default:
break;
}
}
else
{
2011-04-06 19:05:31 +04:00
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"instruction not allowed: %i %s",st->instruction,csync_instruction_str(st->instruction));
rc = -1;
goto out;
}
2011-04-06 19:05:31 +04:00
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"suri: %s",suri);
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"duri: %s",duri);
/* rename the older file to conflict */
ctx->replica = drep;
if (csync_vio_rename(ctx, suri, duri) < 0) {
switch (errno) {
case ENOMEM:
rc = -1;
break;
default:
rc = 1;
break;
}
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: rename, error: %s",
duri,
errbuf);
goto out;
}
/* set instruction for the statedb merger */
st->instruction = CSYNC_INSTRUCTION_NONE;
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "BACKUP file: %s", duri);
rc = 0;
out:
/* set instruction for the statedb merger */
if (rc != 0) {
st->instruction = CSYNC_INSTRUCTION_ERROR;
}
SAFE_FREE(suri);
SAFE_FREE(duri);
ctx->replica = rep_bak;
return rc;
}
static int _csync_new_file(CSYNC *ctx, csync_file_stat_t *st) {
int rc = -1;
rc = _csync_push_file(ctx, st);
return rc;
}
static int _csync_rename_file(CSYNC *ctx, csync_file_stat_t *st) {
int rc = 0;
char errbuf[256] = {0};
struct timeval times[2];
char *suri = NULL;
char *duri = NULL;
const char *tmd5 = NULL;
c_rbnode_t *node = NULL;
switch (ctx->current) {
case REMOTE_REPLICA:
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;
goto out;
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 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( 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 instruction for the statedb merger */
st->instruction = CSYNC_INSTRUCTION_DELETED;
}
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "RENAME file: %s => %s with ID %s", st->path, st->destpath, st->md5);
if (ctx->current == REMOTE_REPLICA) {
_store_id_update(ctx, st);
}
out:
SAFE_FREE(suri);
SAFE_FREE(duri);
/* 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;
rc = _csync_push_file(ctx, st);
return rc;
}
static int _csync_conflict_file(CSYNC *ctx, csync_file_stat_t *st) {
int rc = -1;
rc = _csync_backup_file(ctx, st);
if(rc>=0)
{
rc = _csync_push_file(ctx, st);
}
return rc;
}
static int _csync_remove_file(CSYNC *ctx, csync_file_stat_t *st) {
char errbuf[256] = {0};
char *uri = NULL;
int rc = -1;
switch (ctx->current) {
case LOCAL_REPLICA:
if (asprintf(&uri, "%s/%s", ctx->local.uri, st->path) < 0) {
return -1;
}
break;
case REMOTE_REPLICA:
if (asprintf(&uri, "%s/%s", ctx->remote.uri, st->path) < 0) {
return -1;
}
break;
default:
break;
}
if (csync_vio_unlink(ctx, uri) < 0) {
switch (errno) {
case ENOMEM:
rc = -1;
break;
default:
rc = 1;
break;
}
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, command: unlink, error: %s",
uri,
errbuf);
goto out;
}
2008-09-02 13:43:29 +04:00
/* set instruction for the statedb merger */
st->instruction = CSYNC_INSTRUCTION_DELETED;
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "REMOVED file: %s", uri);
if (ctx->current == REMOTE_REPLICA) {
_store_id_update(ctx, st);
}
rc = 0;
out:
SAFE_FREE(uri);
2008-09-02 13:42:34 +04:00
/* set instruction for the statedb merger */
if (rc != 0) {
/* Write file to statedb, to try to sync again on the next run. */
st->instruction = CSYNC_INSTRUCTION_NONE;
}
return rc;
}
static int _csync_new_dir(CSYNC *ctx, csync_file_stat_t *st) {
2008-09-11 16:16:04 +04:00
enum csync_replica_e dest = -1;
enum csync_replica_e replica_bak;
char errbuf[256] = {0};
char *uri = NULL;
struct timeval times[2];
int rc = -1;
replica_bak = ctx->replica;
switch (ctx->current) {
case LOCAL_REPLICA:
dest = ctx->remote.type;
if (asprintf(&uri, "%s/%s", ctx->remote.uri, st->path) < 0) {
return -1;
}
break;
case REMOTE_REPLICA:
dest = ctx->local.type;
if (asprintf(&uri, "%s/%s", ctx->local.uri, st->path) < 0) {
return -1;
}
break;
default:
break;
}
ctx->replica = dest;
if (csync_vio_mkdirs(ctx, uri, C_DIR_MODE) < 0) {
switch (errno) {
case ENOMEM:
rc = -1;
break;
default:
rc = 1;
break;
}
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"dir: %s, command: mkdirs, error: %s",
uri,
errbuf);
goto out;
}
/* chmod is if it is not the default mode */
if ((st->mode & 07777) != C_DIR_MODE) {
if (csync_vio_chmod(ctx, uri, st->mode) < 0) {
switch (errno) {
case ENOMEM:
rc = -1;
break;
default:
rc = 1;
break;
}
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"dir: %s, command: chmod, error: %s",
uri,
errbuf);
goto out;
}
}
/* set owner and group if possible */
if (ctx->pwd.euid == 0) {
2008-07-03 13:34:34 +04:00
csync_vio_chown(ctx, uri, st->uid, st->gid);
}
times[0].tv_sec = times[1].tv_sec = st->modtime;
times[0].tv_usec = times[1].tv_usec = 0;
csync_vio_utimes(ctx, uri, times);
if (ctx->replica == REMOTE_REPLICA) {
_store_id_update(ctx, st);
2012-08-15 13:46:36 +04:00
}
2012-08-23 18:40:36 +04:00
2008-09-02 13:43:29 +04:00
/* set instruction for the statedb merger */
st->instruction = CSYNC_INSTRUCTION_UPDATED;
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "CREATED dir: %s", uri);
ctx->replica = replica_bak;
rc = 0;
out:
SAFE_FREE(uri);
2008-09-02 13:42:34 +04:00
/* set instruction for the statedb merger */
if (rc != 0) {
st->instruction = CSYNC_INSTRUCTION_ERROR;
}
return rc;
}
static int _csync_sync_dir(CSYNC *ctx, csync_file_stat_t *st) {
2008-09-11 16:16:04 +04:00
enum csync_replica_e dest = -1;
enum csync_replica_e replica_bak;
char errbuf[256] = {0};
char *uri = NULL;
struct timeval times[2];
int rc = -1;
replica_bak = ctx->replica;
switch (ctx->current) {
case LOCAL_REPLICA:
dest = ctx->remote.type;
if (asprintf(&uri, "%s/%s", ctx->remote.uri, st->path) < 0) {
return -1;
}
break;
case REMOTE_REPLICA:
dest = ctx->local.type;
if (asprintf(&uri, "%s/%s", ctx->local.uri, st->path) < 0) {
return -1;
}
break;
default:
break;
}
ctx->replica = dest;
/* chmod is if it is not the default mode */
if ((st->mode & 07777) != C_DIR_MODE) {
if (csync_vio_chmod(ctx, uri, st->mode) < 0) {
switch (errno) {
case ENOMEM:
rc = -1;
break;
default:
rc = 1;
break;
}
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"dir: %s, command: chmod, error: %s",
uri,
errbuf);
goto out;
}
}
/* set owner and group if possible */
if (ctx->pwd.euid == 0) {
2008-07-03 13:34:34 +04:00
csync_vio_chown(ctx, uri, st->uid, st->gid);
}
times[0].tv_sec = times[1].tv_sec = st->modtime;
times[0].tv_usec = times[1].tv_usec = 0;
csync_vio_utimes(ctx, uri, times);
if (ctx->replica == REMOTE_REPLICA) {
_store_id_update(ctx, st);
}
2008-09-02 13:43:29 +04:00
/* set instruction for the statedb merger */
st->instruction = CSYNC_INSTRUCTION_UPDATED;
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "SYNCED dir: %s", uri);
ctx->replica = replica_bak;
rc = 0;
out:
SAFE_FREE(uri);
2008-09-02 13:43:29 +04:00
/* set instruction for the statedb merger */
if (rc != 0) {
st->instruction = CSYNC_INSTRUCTION_ERROR;
}
return rc;
}
static int _csync_remove_dir(CSYNC *ctx, csync_file_stat_t *st) {
c_list_t *list = NULL;
char errbuf[256] = {0};
char *uri = NULL;
int rc = -1;
switch (ctx->current) {
case LOCAL_REPLICA:
if (asprintf(&uri, "%s/%s", ctx->local.uri, st->path) < 0) {
return -1;
}
break;
case REMOTE_REPLICA:
if (asprintf(&uri, "%s/%s", ctx->remote.uri, st->path) < 0) {
return -1;
}
break;
default:
break;
}
if (csync_vio_rmdir(ctx, uri) < 0) {
switch (errno) {
case ENOMEM:
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_FATAL,
"dir: %s, command: rmdir, error: %s",
uri,
errbuf);
rc = -1;
break;
case ENOTEMPTY:
switch (ctx->current) {
case LOCAL_REPLICA:
list = c_list_prepend(ctx->local.list, (void *) st);
if (list == NULL) {
return -1;
}
ctx->local.list = list;
break;
case REMOTE_REPLICA:
list = c_list_prepend(ctx->remote.list, (void *) st);
if (list == NULL) {
return -1;
}
ctx->remote.list = list;
break;
default:
break;
}
rc = 0;
break;
default:
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"dir: %s, command: rmdir, error: %s",
uri,
errbuf);
rc = 1;
break;
}
goto out;
}
2008-09-02 13:43:29 +04:00
/* set instruction for the statedb merger */
st->instruction = CSYNC_INSTRUCTION_DELETED;
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "REMOVED dir: %s", uri);
rc = 0;
out:
SAFE_FREE(uri);
2008-09-02 13:43:29 +04:00
/* set instruction for the statedb merger */
if (rc != 0) {
st->instruction = CSYNC_INSTRUCTION_NONE;
}
return rc;
}
static int _cmp_char( const void *d1, const void *d2 )
{
const char *c1 = (const char*) d1;
const char *c2 = (const char*) d2;
// CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "COMPARE: %s <-> %s", c1, c2);
if( c_streq(c1, c2) ) return 0;
return 1;
}
/* Internal helper method for _csync_correct_id */
static void _csync_correct_id_helper(CSYNC *ctx, char *path, c_list_t **seen_dirs, c_rbtree_t *tree)
{
while( path ) {
uint64_t h;
int len;
c_rbnode_t *node = NULL;
char *old_path = path;
csync_file_stat_t *tfs = NULL;
/* do stuff with the dir here */
if( *seen_dirs && c_list_find_custom( *seen_dirs, path, _cmp_char)) {
// CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "saw this dir already: %s", path);
} else {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "climb on dir: %s", path);
*seen_dirs = c_list_prepend( *seen_dirs, c_strdup(path));
/* Find the correct target entry. */
len = strlen(path);
h = c_jhash64((uint8_t *) path, len, 0);
node = c_rbtree_find(tree, &h);
if (node == NULL) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Unable to find node");
} else {
tfs = c_rbtree_node_data(node);
if( tfs ) {
if(tfs->instruction == CSYNC_INSTRUCTION_DELETED) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Skipping update of MD5 because item is deleted.");
} else {
if(tfs->md5) SAFE_FREE(tfs->md5);
tfs->md5 = _get_md5(ctx, path);
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "MD5 for dir: %s %s (Instruction: %s)", tfs->path,
tfs->md5, csync_instruction_str(tfs->instruction));
if( tfs->md5 && tfs->instruction == CSYNC_INSTRUCTION_NONE ) {
/* set instruction for the statedb merger */
tfs->instruction = CSYNC_INSTRUCTION_UPDATED;
}
}
}
}
}
/* get the parent dir */
path = c_dirname( path );
/* free the old path memory */
SAFE_FREE(old_path );
/* exit on top directory */
if( c_streq(path, ".")) {
SAFE_FREE(path);
path = NULL;
}
}
}
/*
* This function corrects the unique IDs of parent directories of changed
* files. Other than in the file system, the change of a unique Id propagates
* up to the top dir. To save the correct IDs, in all propagations, pathes
* are recorded in the local and remotes id_list lists.
* In this function, the unique ID is queried for each directory once and
* stored into the according entry.
*/
int csync_correct_id(CSYNC *ctx) {
c_list_t *walk = NULL;
c_list_t *seen_dirs = NULL;
c_list_t *list = NULL;
c_rbtree_t *tree = NULL;
char *path = NULL;
switch (ctx->current) {
case LOCAL_REPLICA:
list = ctx->local.id_list;
tree = ctx->local.tree;
break;
case REMOTE_REPLICA:
list = ctx->remote.id_list;
tree = ctx->remote.tree;
break;
default:
break;
}
if (list == NULL) {
return 0;
}
list = c_list_sort(list, _csync_cleanup_cmp);
if (list == NULL) {
return -1;
}
for (walk = c_list_last(list); walk != NULL; walk = c_list_prev(walk)) {
csync_file_stat_t *st = NULL;
st = (csync_file_stat_t *) walk->data;
if( st->type == CSYNC_FTW_TYPE_FILE ) {
path = c_dirname( st->path );
} else if( st->type == CSYNC_FTW_TYPE_DIR ) {
path = c_strdup(st->path); /* Allocate mem for this not to disappoint FREE */
} else {
/* unhandled path */
}
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "correct ID on dir: %s", path);
/* handle the . path */
if( path && path[0] == '.' && strlen(path) == 1) {
SAFE_FREE(path);
path = NULL;
}
_csync_correct_id_helper(ctx, path, &seen_dirs, tree);
if (st->type == CSYNC_FTW_TYPE_FILE && ctx->current == REMOTE_REPLICA
&& st->destpath) {
path = c_dirname( st->destpath );
if( path && path[0] == '.' && strlen(path) == 1) {
SAFE_FREE(path);
path = NULL;
}
_csync_correct_id_helper(ctx, path, &seen_dirs, ctx->local.tree);
}
}
/* Free the seen_dirs list */
if( seen_dirs ) {
c_list_t *walk1 = NULL;
for (walk1 = c_list_first(seen_dirs); walk1 != NULL; walk1 = c_list_next(walk1)) {
char *data = NULL;
data = (char*) walk1->data;
SAFE_FREE(data);
}
}
c_list_free(seen_dirs);
return 0;
}
static int _csync_propagation_cleanup(CSYNC *ctx) {
c_list_t *list = NULL;
c_list_t *walk = NULL;
char *uri = NULL;
char *dir = NULL;
switch (ctx->current) {
case LOCAL_REPLICA:
list = ctx->local.list;
uri = ctx->local.uri;
break;
case REMOTE_REPLICA:
list = ctx->remote.list;
uri = ctx->remote.uri;
break;
default:
break;
}
if (list == NULL) {
return 0;
}
list = c_list_sort(list, _csync_cleanup_cmp);
if (list == NULL) {
return -1;
}
for (walk = c_list_last(list); walk != NULL; walk = c_list_prev(walk)) {
csync_file_stat_t *st = NULL;
st = (csync_file_stat_t *) walk->data;
if (asprintf(&dir, "%s/%s", uri, st->path) < 0) {
return -1;
}
if (csync_vio_rmdir(ctx, dir) < 0) {
2008-07-09 12:10:00 +04:00
/* Write it back to statedb, that we try to delete it next time. */
st->instruction = CSYNC_INSTRUCTION_NONE;
} else {
st->instruction = CSYNC_INSTRUCTION_DELETED;
}
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "CLEANUP dir: %s", dir);
SAFE_FREE(dir);
}
return 0;
}
static int _csync_propagation_file_visitor(void *obj, void *data) {
csync_file_stat_t *st = NULL;
CSYNC *ctx = NULL;
st = (csync_file_stat_t *) obj;
ctx = (CSYNC *) data;
switch(st->type) {
case CSYNC_FTW_TYPE_SLINK:
break;
case CSYNC_FTW_TYPE_FILE:
switch (st->instruction) {
case CSYNC_INSTRUCTION_NEW:
if (_csync_new_file(ctx, st) < 0) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"FAIL NEW: %s",st->path);
2008-05-19 13:21:44 +04:00
goto err;
}
break;
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;
}
break;
case CSYNC_INSTRUCTION_REMOVE:
if (_csync_remove_file(ctx, st) < 0) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"FAIL REMOVE: %s",st->path);
goto err;
}
break;
case CSYNC_INSTRUCTION_CONFLICT:
2011-04-06 19:05:31 +04:00
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"case CSYNC_INSTRUCTION_CONFLICT: %s",st->path);
if (_csync_conflict_file(ctx, st) < 0) {
goto err;
}
break;
default:
break;
}
break;
case CSYNC_FTW_TYPE_DIR:
/*
* We have to walk over the files first. If you create or rename a file
* in a directory on unix. The modification time of the directory gets
* changed.
*/
break;
default:
break;
}
return 0;
err:
return -1;
}
static int _csync_propagation_dir_visitor(void *obj, void *data) {
csync_file_stat_t *st = NULL;
CSYNC *ctx = NULL;
st = (csync_file_stat_t *) obj;
ctx = (CSYNC *) data;
switch(st->type) {
case CSYNC_FTW_TYPE_SLINK:
/* FIXME: implement symlink support */
break;
case CSYNC_FTW_TYPE_FILE:
break;
case CSYNC_FTW_TYPE_DIR:
switch (st->instruction) {
case CSYNC_INSTRUCTION_NEW:
if (_csync_new_dir(ctx, st) < 0) {
goto err;
}
break;
case CSYNC_INSTRUCTION_SYNC:
if (_csync_sync_dir(ctx, st) < 0) {
goto err;
}
break;
case CSYNC_INSTRUCTION_CONFLICT:
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE,"directory attributes different");
if (_csync_sync_dir(ctx, st) < 0) {
goto err;
}
break;
case CSYNC_INSTRUCTION_REMOVE:
if (_csync_remove_dir(ctx, st) < 0) {
goto err;
}
break;
case CSYNC_INSTRUCTION_RENAME:
/* there can't be a rename for dirs. See updater. */
break;
default:
break;
}
break;
default:
break;
}
return 0;
err:
return -1;
}
2008-12-14 01:29:16 +03:00
int csync_propagate_files(CSYNC *ctx) {
c_rbtree_t *tree = NULL;
switch (ctx->current) {
case LOCAL_REPLICA:
tree = ctx->local.tree;
break;
case REMOTE_REPLICA:
tree = ctx->remote.tree;
break;
default:
break;
}
if (c_rbtree_walk(tree, (void *) ctx, _csync_propagation_file_visitor) < 0) {
return -1;
}
if (c_rbtree_walk(tree, (void *) ctx, _csync_propagation_dir_visitor) < 0) {
return -1;
}
if (_csync_propagation_cleanup(ctx) < 0) {
return -1;
}
return 0;
}
2009-05-13 12:12:07 +04:00
/* vim: set ts=8 sw=2 et cindent: */