/* * libcsync -- a library to sync a directory with another * * Copyright (c) 2006 by Andreas Schneider * * 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 #include #include #include "c_jhash.h" #include "csync_util.h" #include "vio/csync_vio.h" #if defined(__APPLE__) # define COMMON_DIGEST_FOR_OPENSSL # include # define SHA1 CC_SHA1 #else # include #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 / */ 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: */