nextcloud-desktop/src/csync_util.c
2012-08-17 16:48:15 +03:00

414 lines
9.6 KiB
C

/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2006 by Andreas Schneider <mail@cynapses.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "config.h"
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include "c_jhash.h"
#include "csync_util.h"
#include "vio/csync_vio.h"
#if defined(__APPLE__)
# define COMMON_DIGEST_FOR_OPENSSL
# include <CommonCrypto/CommonDigest.h>
# define SHA1 CC_SHA1
#else
# include <openssl/md5.h>
#endif
#define CSYNC_LOG_CATEGORY_NAME "csync.util"
#include "csync_log.h"
typedef struct {
const char *instr_str;
enum csync_instructions_e instr_code;
} _instr_code_struct;
static const _instr_code_struct _instr[] =
{
{ "INSTRUCTION_NONE", CSYNC_INSTRUCTION_NONE },
{ "INSTRUCTION_EVAL", CSYNC_INSTRUCTION_EVAL },
{ "INSTRUCTION_REMOVE", CSYNC_INSTRUCTION_REMOVE },
{ "INSTRUCTION_RENAME", CSYNC_INSTRUCTION_RENAME },
{ "INSTRUCTION_NEW", CSYNC_INSTRUCTION_NEW },
{ "INSTRUCTION_CONFLICT", CSYNC_INSTRUCTION_CONFLICT },
{ "INSTRUCTION_IGNORE", CSYNC_INSTRUCTION_IGNORE },
{ "INSTRUCTION_SYNC", CSYNC_INSTRUCTION_SYNC },
{ "INSTRUCTION_STAT_ERR", CSYNC_INSTRUCTION_STAT_ERROR },
{ "INSTRUCTION_ERROR", CSYNC_INSTRUCTION_ERROR },
{ "INSTRUCTION_DELETED", CSYNC_INSTRUCTION_DELETED },
{ "INSTRUCTION_UPDATED", CSYNC_INSTRUCTION_UPDATED },
{ NULL, CSYNC_INSTRUCTION_ERROR }
};
struct csync_memstat_s {
int size;
int resident;
int shared;
int trs;
int drs;
int lrs;
int dt;
};
const char *csync_instruction_str(enum csync_instructions_e instr)
{
int idx = 0;
while (_instr[idx].instr_str != NULL) {
if (_instr[idx].instr_code == instr) {
return _instr[idx].instr_str;
}
idx++;
}
return "ERROR!";
}
void csync_memstat_check(void) {
int s = 0;
struct csync_memstat_s m;
FILE* fp;
/* get process memory stats */
fp = fopen("/proc/self/statm","r");
if (fp == NULL) {
return;
}
s = fscanf(fp, "%d%d%d%d%d%d%d", &m.size, &m.resident, &m.shared, &m.trs,
&m.drs, &m.lrs, &m.dt);
fclose(fp);
if (s == EOF) {
return;
}
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "Memory: %dK total size, %dK resident, %dK shared",
m.size * 4, m.resident * 4, m.shared * 4);
}
static int _merge_file_trees_visitor(void *obj, void *data) {
csync_file_stat_t *fs = NULL;
csync_file_stat_t *tfs = NULL;
csync_vio_file_stat_t *vst = NULL;
CSYNC *ctx = NULL;
c_rbtree_t *tree = NULL;
c_rbnode_t *node = NULL;
char errbuf[256] = {0};
char *uri = NULL;
int rc = -1;
fs = (csync_file_stat_t *) obj;
ctx = (CSYNC *) data;
/* search for UPDATED file */
if (fs->instruction != CSYNC_INSTRUCTION_UPDATED) {
rc = 0;
goto out;
}
switch (ctx->current) {
case LOCAL_REPLICA:
tree = ctx->local.tree;
break;
case REMOTE_REPLCIA:
tree = ctx->remote.tree;
break;
default:
break;
}
/* check if the file is new or has been synced */
node = c_rbtree_find(tree, &fs->phash);
if (node == NULL) {
csync_file_stat_t *new = NULL;
new = c_malloc(sizeof(csync_file_stat_t) + fs->pathlen + 1);
if (new == NULL) {
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, merge malloc, error: %s",
fs->path,
errbuf);
rc = -1;
goto out;
}
new = memcpy(new, fs, sizeof(csync_file_stat_t) + fs->pathlen + 1);
if (c_rbtree_insert(tree, new) < 0) {
strerror_r(errno, errbuf, sizeof(errbuf));
SAFE_FREE(new);
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, rb tree insert, error: %s",
fs->path,
errbuf);
rc = -1;
goto out;
}
node = c_rbtree_find(tree, &fs->phash);
if (node == NULL) {
rc = -1;
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Unable to find node");
goto out;
}
}
fs = c_rbtree_node_data(node);
switch (ctx->current) {
case LOCAL_REPLICA:
if (asprintf(&uri, "%s/%s", ctx->local.uri, fs->path) < 0) {
rc = -1;
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "file uri alloc failed: %s",
errbuf);
goto out;
}
break;
case REMOTE_REPLCIA:
if (asprintf(&uri, "%s/%s", ctx->remote.uri, fs->path) < 0) {
rc = -1;
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "file uri alloc failed: %s",
errbuf);
goto out;
}
break;
default:
break;
}
/* get file stat of the file on the replica */
vst = csync_vio_file_stat_new();
if (csync_vio_stat(ctx, uri, vst) < 0) {
rc = -1;
strerror_r(errno, errbuf, sizeof(errbuf));
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR,
"file: %s, updating stat failed, error: %s",
uri,
errbuf);
goto out;
}
/* update file stat */
fs->inode = vst->inode;
fs->modtime = vst->mtime;
/* update with the id from the remote repo */
node = c_rbtree_find(ctx->remote.tree, &fs->phash);
if (node == NULL) {
rc = -1;
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Unable to find node");
goto out;
}
tfs = c_rbtree_node_data(node);
fs->md5 = c_strdup( tfs->md5 );
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "PRE UPDATED %s: %s <-> %s", uri, fs->md5, tfs->md5);
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "file: %s, instruction: UPDATED (%s)", uri, fs->md5);
fs->instruction = CSYNC_INSTRUCTION_NONE;
rc = 0;
out:
csync_vio_file_stat_destroy(vst);
SAFE_FREE(uri);
if (rc != 0) {
fs->instruction = CSYNC_INSTRUCTION_ERROR;
}
return rc;
}
/*
* merge the local tree with the new files from remote and update the
* inode numbers
*/
int csync_merge_file_trees(CSYNC *ctx) {
int rc = -1;
/* walk over remote tree, stat on local system */
ctx->current = LOCAL_REPLICA;
ctx->replica = ctx->local.type;
rc = c_rbtree_walk(ctx->remote.tree, ctx, _merge_file_trees_visitor);
if (rc < 0) {
goto out;
}
#if 0
/* We don't have to merge the remote tree atm. */
/* walk over local tree, stat on remote system */
ctx->current = REMOTE_REPLICA;
ctx->replica = ctx->remote.type;
rc = c_rbtree_walk(ctx->local.tree, ctx, _merge_file_trees_visitor);
if (rc < 0) {
goto out;
}
#endif
out:
return rc;
}
int csync_unix_extensions(CSYNC *ctx) {
int rc = -1;
char *uri = NULL;
csync_vio_handle_t *fp = NULL;
ctx->options.unix_extensions = 0;
rc = asprintf(&uri, "%s/csync_unix_extension*test.ctmp", ctx->remote.uri);
if (rc < 0) {
goto out;
}
ctx->replica = ctx->remote.type;
fp = csync_vio_creat(ctx, uri, 0644);
if (fp == NULL) {
rc = 0;
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO,
"Disabled unix filesystem synchronization");
goto out;
}
csync_vio_close(ctx, fp);
ctx->options.unix_extensions = 1;
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "Enabled unix filesystem synchronization");
rc = 1;
out:
csync_vio_unlink(ctx, uri);
SAFE_FREE(uri);
return rc;
}
/* Normalize the uri to <host>/<path> */
uint64_t csync_create_statedb_hash(CSYNC *ctx) {
char *p = NULL;
char *host = NULL;
char *path = NULL;
char name[PATH_MAX] = {0};
uint64_t hash = 0;
if (c_parse_uri(ctx->remote.uri, NULL, NULL, NULL, &host, NULL, &path) < 0) {
SAFE_FREE(host);
SAFE_FREE(path);
return 0;
}
if (host && (p = strchr(host, '.'))) {
*p = '\0';
}
/* len + 1 for \0 */
snprintf(name, PATH_MAX, "%s%s", host ? host : "", path);
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO,
"Normalized path for the statedb hash: %s", name);
hash = c_jhash64((uint8_t *) name, strlen(name), 0);
SAFE_FREE(host);
SAFE_FREE(path);
return hash;
}
static char* digest_to_out(unsigned char* digest)
{
char *out = c_malloc(33);
int n;
if( !digest ) return NULL;
for (n = 0; n < 16; ++n) {
snprintf(&(out[n*2]), 16*2, "%02x", (unsigned int) *(digest+n));
}
return out;
}
#define BUF_SIZE 512
char* csync_file_md5(const char *filename)
{
const char *tmpFileName;
int fd;
MD5_CTX c;
char buf[ BUF_SIZE+1 ];
unsigned char digest[16];
size_t size;
tmpFileName = c_multibyte( filename );
if ( (fd = _topen( tmpFileName, O_RDONLY )) < 0) {
return NULL;
} else {
MD5_Init(&c);
while( (size=read(fd, buf, BUF_SIZE )) > 0) {
buf[size]='\0';
MD5_Update(&c, buf, size);
}
close(fd);
MD5_Final(digest, &c);
}
c_free_multibyte(tmpFileName);
return digest_to_out(digest);
}
char* csync_buffer_md5(const char *str, int length)
{
MD5_CTX c;
unsigned char digest[16];
MD5_Init(&c);
while (length > 0) {
if (length > 512) {
MD5_Update(&c, str, 512);
} else {
MD5_Update(&c, str, length);
}
length -= 512;
str += 512;
}
MD5_Final(digest, &c);
return digest_to_out(digest);
}
/* vim: set ts=8 sw=2 et cindent: */