From: Jocelyn Turcotte Date: Mon, 14 Aug 2017 14:19:52 +0000 (+0200) Subject: Compile almost all of csync as C++ X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~701^2~143 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=ac4be17192fb9cb8c52ea0428238c7343c605d35;p=nextcloud-desktop.git Compile almost all of csync as C++ This will allow us to unify data structures between csync and libsync. Utility functions like csync_time and c_std are still compiled as C since we won't need to be coupled with Qt in the short term. --- diff --git a/csync/tests/csync_tests/check_csync_update.cpp b/csync/tests/csync_tests/check_csync_update.cpp new file mode 100644 index 000000000..cc11bad83 --- /dev/null +++ b/csync/tests/csync_tests/check_csync_update.cpp @@ -0,0 +1,460 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "csync_update.cpp" + +extern "C" { + +#include "torture.h" + +#define TESTDB "/tmp/check_csync/journal.db" + +static int firstrun = 1; + +static void statedb_create_metadata_table(sqlite3 *db) +{ + int rc = 0; + + if( db ) { + const char *sql = "CREATE TABLE IF NOT EXISTS metadata(" + "phash INTEGER(8)," + "pathlen INTEGER," + "path VARCHAR(4096)," + "inode INTEGER," + "uid INTEGER," + "gid INTEGER," + "mode INTEGER," + "modtime INTEGER(8)," + "type INTEGER," + "md5 VARCHAR(32)," + "fileid VARCHAR(128)," + "remotePerm VARCHAR(128)," + "filesize BIGINT," + "ignoredChildrenRemote INT," + "contentChecksum TEXT," + "contentChecksumTypeId INTEGER," + "PRIMARY KEY(phash));"; + + rc = sqlite3_exec(db, sql, NULL, NULL, NULL); + //const char *msg = sqlite3_errmsg(db); + assert_int_equal( rc, SQLITE_OK ); + + sql = "CREATE TABLE IF NOT EXISTS checksumtype(" + "id INTEGER PRIMARY KEY," + "name TEXT UNIQUE" + ");"; + rc = sqlite3_exec(db, sql, NULL, NULL, NULL); + assert_int_equal( rc, SQLITE_OK ); + } +} + +static void statedb_insert_metadata(sqlite3 *db) +{ + int rc = 0; + + if( db ) { + char *stmt = sqlite3_mprintf("INSERT INTO metadata" + "(phash, pathlen, path, inode, uid, gid, mode, modtime,type,md5) VALUES" + "(%lld, %d, '%q', %d, %d, %d, %d, %lld, %d, '%q');", + (long long signed int)42, + 42, + "I_was_wurst_before_I_became_wurstsalat", + 619070, + 42, + 42, + 42, + (long long signed int)42, + 0, + "4711"); + + char *errmsg; + rc = sqlite3_exec(db, stmt, NULL, NULL, &errmsg); + sqlite3_free(stmt); + assert_int_equal( rc, SQLITE_OK ); + } +} + +static int setup(void **state) +{ + CSYNC *csync; + int rc; + + unlink(TESTDB); + rc = system("mkdir -p /tmp/check_csync"); + assert_int_equal(rc, 0); + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + csync_create(&csync, "/tmp/check_csync1"); + csync_init(csync, TESTDB); + + /* Create a new db with metadata */ + sqlite3 *db; + csync->statedb.file = c_strdup(TESTDB); + rc = sqlite3_open(csync->statedb.file, &db); + statedb_create_metadata_table(db); + if( firstrun ) { + statedb_insert_metadata(db); + firstrun = 0; + } + sqlite3_close(db); + + rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db); + assert_int_equal(rc, 0); + + *state = csync; + + return 0; +} + +static int setup_ftw(void **state) +{ + CSYNC *csync; + int rc; + + rc = system("mkdir -p /tmp/check_csync"); + assert_int_equal(rc, 0); + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + csync_create(&csync, "/tmp"); + csync_init(csync, TESTDB); + + sqlite3 *db = NULL; + rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL); + assert_int_equal(rc, SQLITE_OK); + statedb_create_metadata_table(db); + rc = sqlite3_close(db); + assert_int_equal(rc, SQLITE_OK); + + rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db); + assert_int_equal(rc, 0); + + csync->statedb.file = c_strdup( TESTDB ); + *state = csync; + + return 0; +} + +static int teardown(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + unlink( csync->statedb.file); + rc = csync_destroy(csync); + assert_int_equal(rc, 0); + + *state = NULL; + + return 0; +} + +static int teardown_rm(void **state) { + int rc; + + teardown(state); + + rc = system("rm -rf /tmp/check_csync"); + assert_int_equal(rc, 0); + rc = system("rm -rf /tmp/check_csync1"); + assert_int_equal(rc, 0); + + return 0; +} + +/* create a file stat, caller must free memory */ +static csync_vio_file_stat_t* create_fstat(const char *name, + ino_t inode, + time_t mtime) +{ + csync_vio_file_stat_t *fs = NULL; + time_t t; + + fs = csync_vio_file_stat_new(); + if (fs == NULL) { + return NULL; + } + + if (name && *name) { + fs->name = c_strdup(name); + } else { + fs->name = c_strdup("file.txt"); + } + + if (fs->name == NULL) { + csync_vio_file_stat_destroy(fs); + return NULL; + } + + fs->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; + + fs->type = CSYNC_VIO_FILE_TYPE_REGULAR; + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; + + + if (inode == 0) { + fs->inode = 619070; + } else { + fs->inode = inode; + } + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE; + + + fs->size = 157459; + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; + + + + if (mtime == 0) { + fs->atime = fs->ctime = fs->mtime = time(&t); + } else { + fs->atime = fs->ctime = fs->mtime = mtime; + } + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME; + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME; + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; + + return fs; +} + +static int failing_fn(CSYNC *ctx, + const char *file, + const csync_vio_file_stat_t *fs, + int flag) +{ + (void) ctx; + (void) file; + (void) fs; + (void) flag; + + return -1; +} + +/* detect a new file */ +static void check_csync_detect_update(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *st; + csync_vio_file_stat_t *fs; + int rc; + + fs = create_fstat("file.txt", 0, 1217597845); + assert_non_null(fs); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/file.txt", + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, 0); + + /* the instruction should be set to new */ + st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root); + assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); + + /* create a statedb */ + csync_set_status(csync, 0xFFFF); + + csync_vio_file_stat_destroy(fs); +} + +/* Test behaviour in case no db is there. For that its important that the + * test before this one uses teardown_rm. + */ +static void check_csync_detect_update_db_none(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *st; + csync_vio_file_stat_t *fs; + int rc; + + fs = create_fstat("file.txt", 0, 1217597845); + assert_non_null(fs); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/file.txt", + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, 0); + + /* the instruction should be set to new */ + st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root); + assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); + + + /* create a statedb */ + csync_set_status(csync, 0xFFFF); + + csync_vio_file_stat_destroy(fs); +} + +static void check_csync_detect_update_db_eval(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *st; + csync_vio_file_stat_t *fs; + int rc; + + fs = create_fstat("file.txt", 0, 42); + assert_non_null(fs); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/file.txt", + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, 0); + + /* the instruction should be set to new */ + st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root); + assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); + + /* create a statedb */ + csync_set_status(csync, 0xFFFF); + + csync_vio_file_stat_destroy(fs); +} + + +static void check_csync_detect_update_db_rename(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + // csync_file_stat_t *st; + + csync_vio_file_stat_t *fs; + int rc = 0; + + fs = create_fstat("wurst.txt", 0, 42); + assert_non_null(fs); + csync_set_statedb_exists(csync, 1); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/wurst.txt", + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, 0); + + /* the instruction should be set to rename */ + /* + * temporarily broken. + st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root); + assert_int_equal(st->instruction, CSYNC_INSTRUCTION_RENAME); + + st->instruction = CSYNC_INSTRUCTION_UPDATED; + */ + /* create a statedb */ + csync_set_status(csync, 0xFFFF); + + csync_vio_file_stat_destroy(fs); +} + +static void check_csync_detect_update_db_new(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *st; + csync_vio_file_stat_t *fs; + int rc; + + fs = create_fstat("file.txt", 42000, 0); + assert_non_null(fs); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/file.txt", + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, 0); + + /* the instruction should be set to new */ + st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root); + assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); + + + /* create a statedb */ + csync_set_status(csync, 0xFFFF); + + csync_vio_file_stat_destroy(fs); +} + +static void check_csync_detect_update_null(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_vio_file_stat_t *fs; + int rc; + + fs = create_fstat("file.txt", 0, 0); + assert_non_null(fs); + + rc = _csync_detect_update(csync, + NULL, + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, -1); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/file.txt", + NULL, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, -1); + + csync_vio_file_stat_destroy(fs); +} + +static void check_csync_ftw(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_ftw(csync, "/tmp", csync_walker, MAX_DEPTH); + assert_int_equal(rc, 0); +} + +static void check_csync_ftw_empty_uri(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_ftw(csync, "", csync_walker, MAX_DEPTH); + assert_int_equal(rc, -1); +} + +static void check_csync_ftw_failing_fn(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_ftw(csync, "/tmp", failing_fn, MAX_DEPTH); + assert_int_equal(rc, -1); +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(check_csync_detect_update, setup, teardown_rm), + cmocka_unit_test_setup_teardown(check_csync_detect_update_db_none, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_detect_update_db_eval, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_detect_update_db_rename, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_detect_update_db_new, setup, teardown_rm), + cmocka_unit_test_setup_teardown(check_csync_detect_update_null, setup, teardown_rm), + + cmocka_unit_test_setup_teardown(check_csync_ftw, setup_ftw, teardown_rm), + cmocka_unit_test_setup_teardown(check_csync_ftw_empty_uri, setup_ftw, teardown_rm), + cmocka_unit_test_setup_teardown(check_csync_ftw_failing_fn, setup_ftw, teardown_rm), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} + +} diff --git a/src/csync/CMakeLists.txt b/src/csync/CMakeLists.txt index 2bd60e572..09f320202 100644 --- a/src/csync/CMakeLists.txt +++ b/src/csync/CMakeLists.txt @@ -62,30 +62,30 @@ if(NO_RENAME_EXTENSION) endif() set(csync_SRCS - csync.c - csync_exclude.c - csync_log.c - csync_statedb.c + csync.cpp + csync_exclude.cpp + csync_log.cpp + csync_statedb.cpp csync_time.c - csync_util.c - csync_misc.c + csync_util.cpp + csync_misc.cpp - csync_update.c - csync_reconcile.c + csync_update.cpp + csync_reconcile.cpp csync_rename.cpp - vio/csync_vio.c - vio/csync_vio_file_stat.c + vio/csync_vio.cpp + vio/csync_vio_file_stat.cpp ) if (WIN32) list(APPEND csync_SRCS - vio/csync_vio_local_win.c + vio/csync_vio_local_win.cpp ) else() list(APPEND csync_SRCS - vio/csync_vio_local_unix.c + vio/csync_vio_local_unix.cpp ) endif() diff --git a/src/csync/csync.c b/src/csync/csync.c deleted file mode 100644 index 1f75ac3bd..000000000 --- a/src/csync/csync.c +++ /dev/null @@ -1,651 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * Copyright (c) 2012-2013 by Klaas Freitag - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config_csync.h" - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "c_lib.h" -#include "csync_private.h" -#include "csync_exclude.h" -#include "csync_statedb.h" -#include "csync_time.h" -#include "csync_util.h" -#include "csync_misc.h" -#include "std/c_private.h" - -#include "csync_update.h" -#include "csync_reconcile.h" - -#include "vio/csync_vio.h" - -#include "csync_log.h" -#include "csync_rename.h" -#include "c_jhash.h" - -static int _key_cmp(const void *key, const void *data) { - uint64_t a; - csync_file_stat_t *b; - - a = *(uint64_t *) (key); - b = (csync_file_stat_t *) data; - - if (a < b->phash) { - return -1; - } else if (a > b->phash) { - return 1; - } - - return 0; -} - -static int _data_cmp(const void *key, const void *data) { - csync_file_stat_t *a, *b; - - a = (csync_file_stat_t *) key; - b = (csync_file_stat_t *) data; - - if (a->phash < b->phash) { - return -1; - } else if (a->phash > b->phash) { - return 1; - } - - return 0; -} - -void csync_create(CSYNC **csync, const char *local) { - CSYNC *ctx; - size_t len = 0; - - ctx = c_malloc(sizeof(CSYNC)); - - ctx->status_code = CSYNC_STATUS_OK; - - /* remove trailing slashes */ - len = strlen(local); - while(len > 0 && local[len - 1] == '/') --len; - - ctx->local.uri = c_strndup(local, len); - - ctx->status_code = CSYNC_STATUS_OK; - - ctx->current_fs = NULL; - - ctx->abort = false; - - ctx->ignore_hidden_files = true; - - *csync = ctx; -} - -void csync_init(CSYNC *ctx, const char *db_file) { - assert(ctx); - /* Do not initialize twice */ - - assert(!(ctx->status & CSYNC_STATUS_INIT)); - ctx->status_code = CSYNC_STATUS_OK; - - ctx->local.type = LOCAL_REPLICA; - - ctx->remote.type = REMOTE_REPLICA; - - SAFE_FREE(ctx->statedb.file); - ctx->statedb.file = c_strdup(db_file); - - c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp); - c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp); - - ctx->remote.root_perms = 0; - - ctx->status = CSYNC_STATUS_INIT; - - /* initialize random generator */ - srand(time(NULL)); -} - -int csync_update(CSYNC *ctx) { - int rc = -1; - struct timespec start, finish; - - if (ctx == NULL) { - errno = EBADF; - return -1; - } - ctx->status_code = CSYNC_STATUS_OK; - - /* Path of database file is set in csync_init */ - if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) { - rc = -1; - return rc; - } - - ctx->status_code = CSYNC_STATUS_OK; - - csync_memstat_check(); - - if (!ctx->excludes) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "No exclude file loaded or defined!"); - } - - /* update detection for local replica */ - csync_gettime(&start); - ctx->current = LOCAL_REPLICA; - ctx->replica = ctx->local.type; - - rc = csync_ftw(ctx, ctx->local.uri, csync_walker, MAX_DEPTH); - if (rc < 0) { - if(ctx->status_code == CSYNC_STATUS_OK) { - ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR); - } - goto out; - } - - csync_gettime(&finish); - - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, - "Update detection for local replica took %.2f seconds walking %zu files.", - c_secdiff(finish, start), c_rbtree_size(ctx->local.tree)); - csync_memstat_check(); - - /* update detection for remote replica */ - csync_gettime(&start); - ctx->current = REMOTE_REPLICA; - ctx->replica = ctx->remote.type; - - rc = csync_ftw(ctx, "", csync_walker, MAX_DEPTH); - if (rc < 0) { - if(ctx->status_code == CSYNC_STATUS_OK) { - ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR); - } - goto out; - } - - csync_gettime(&finish); - - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, - "Update detection for remote replica took %.2f seconds " - "walking %zu files.", - c_secdiff(finish, start), c_rbtree_size(ctx->remote.tree)); - csync_memstat_check(); - - ctx->status |= CSYNC_STATUS_UPDATE; - - rc = 0; - -out: - csync_statedb_close(ctx); - return rc; -} - -int csync_reconcile(CSYNC *ctx) { - int rc = -1; - struct timespec start, finish; - - if (ctx == NULL) { - errno = EBADF; - return -1; - } - ctx->status_code = CSYNC_STATUS_OK; - - /* Reconciliation for local replica */ - csync_gettime(&start); - - if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) { - rc = -1; - return rc; - } - - ctx->current = LOCAL_REPLICA; - ctx->replica = ctx->local.type; - - rc = csync_reconcile_updates(ctx); - - csync_gettime(&finish); - - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, - "Reconciliation for local replica took %.2f seconds visiting %zu files.", - c_secdiff(finish, start), c_rbtree_size(ctx->local.tree)); - - if (rc < 0) { - if (!CSYNC_STATUS_IS_OK(ctx->status_code)) { - ctx->status_code = csync_errno_to_status( errno, CSYNC_STATUS_RECONCILE_ERROR ); - } - goto out; - } - - /* Reconciliation for remote replica */ - csync_gettime(&start); - - ctx->current = REMOTE_REPLICA; - ctx->replica = ctx->remote.type; - - rc = csync_reconcile_updates(ctx); - - csync_gettime(&finish); - - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, - "Reconciliation for remote replica took %.2f seconds visiting %zu files.", - c_secdiff(finish, start), c_rbtree_size(ctx->remote.tree)); - - if (rc < 0) { - if (!CSYNC_STATUS_IS_OK(ctx->status_code)) { - ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_RECONCILE_ERROR ); - } - goto out; - } - - ctx->status |= CSYNC_STATUS_RECONCILE; - - rc = 0; - -out: - csync_statedb_close(ctx); - return 0; -} - -/* - * local visitor which calls the user visitor with repacked stat info. - */ -static int _csync_treewalk_visitor(void *obj, void *data) { - int rc = 0; - csync_file_stat_t *cur = NULL; - CSYNC *ctx = NULL; - c_rbtree_visit_func *visitor = NULL; - _csync_treewalk_context *twctx = NULL; - TREE_WALK_FILE trav; - c_rbtree_t *other_tree = NULL; - c_rbnode_t *other_node = NULL; - - cur = (csync_file_stat_t *) obj; - ctx = (CSYNC *) data; - - if (ctx == NULL) { - return -1; - } - - /* we need the opposite tree! */ - switch (ctx->current) { - case LOCAL_REPLICA: - other_tree = ctx->remote.tree; - break; - case REMOTE_REPLICA: - other_tree = ctx->local.tree; - break; - default: - break; - } - - other_node = c_rbtree_find(other_tree, &cur->phash); - - if (!other_node) { - /* Check the renamed path as well. */ - int len; - uint64_t h = 0; - 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); - other_node = c_rbtree_find(other_tree, &h); - } - SAFE_FREE(renamed_path); - } - - if (!other_node) { - /* Check the source path as well. */ - int len; - uint64_t h = 0; - char *renamed_path = csync_rename_adjust_path_source(ctx, cur->path); - - if (!c_streq(renamed_path, cur->path)) { - len = strlen( renamed_path ); - h = c_jhash64((uint8_t *) renamed_path, len, 0); - other_node = c_rbtree_find(other_tree, &h); - } - SAFE_FREE(renamed_path); - } - - if (obj == NULL || data == NULL) { - ctx->status_code = CSYNC_STATUS_PARAM_ERROR; - return -1; - } - ctx->status_code = CSYNC_STATUS_OK; - - twctx = (_csync_treewalk_context*) ctx->callbacks.userdata; - if (twctx == NULL) { - ctx->status_code = CSYNC_STATUS_PARAM_ERROR; - return -1; - } - - if (twctx->instruction_filter > 0 && - !(twctx->instruction_filter & cur->instruction) ) { - return 0; - } - - visitor = (c_rbtree_visit_func*)(twctx->user_visitor); - if (visitor != NULL) { - trav.path = cur->path; - trav.size = cur->size; - trav.modtime = cur->modtime; - trav.mode = cur->mode; - trav.type = cur->type; - trav.instruction = cur->instruction; - trav.rename_path = cur->destpath; - trav.etag = cur->etag; - trav.file_id = cur->file_id; - trav.remotePerm = cur->remotePerm; - trav.directDownloadUrl = cur->directDownloadUrl; - trav.directDownloadCookies = cur->directDownloadCookies; - trav.inode = cur->inode; - - trav.error_status = cur->error_status; - trav.has_ignored_files = cur->has_ignored_files; - trav.checksumHeader = cur->checksumHeader; - - if( other_node ) { - csync_file_stat_t *other_stat = (csync_file_stat_t*)other_node->data; - trav.other.etag = other_stat->etag; - trav.other.file_id = other_stat->file_id; - trav.other.instruction = other_stat->instruction; - trav.other.modtime = other_stat->modtime; - trav.other.size = other_stat->size; - } else { - trav.other.etag = 0; - trav.other.file_id = 0; - trav.other.instruction = CSYNC_INSTRUCTION_NONE; - trav.other.modtime = 0; - trav.other.size = 0; - } - - rc = (*visitor)(&trav, twctx->userdata); - cur->instruction = trav.instruction; - if (trav.etag != cur->etag) { // FIXME It would be nice to have this documented - SAFE_FREE(cur->etag); - cur->etag = c_strdup(trav.etag); - } - - return rc; - } - ctx->status_code = CSYNC_STATUS_PARAM_ERROR; - return -1; -} - -/* - * treewalk function, called from its wrappers below. - * - * it encapsulates the user visitor function, the filter and the userdata - * into a treewalk_context structure and calls the rb treewalk function, - * which calls the local _csync_treewalk_visitor in this module. - * The user visitor is called from there. - */ -static int _csync_walk_tree(CSYNC *ctx, c_rbtree_t *tree, csync_treewalk_visit_func *visitor, int filter) -{ - _csync_treewalk_context tw_ctx; - int rc = -1; - - if (ctx == NULL) { - errno = EBADF; - return rc; - } - - if (visitor == NULL || tree == NULL) { - ctx->status_code = CSYNC_STATUS_PARAM_ERROR; - return rc; - } - - tw_ctx.userdata = ctx->callbacks.userdata; - tw_ctx.user_visitor = visitor; - tw_ctx.instruction_filter = filter; - - ctx->callbacks.userdata = &tw_ctx; - - rc = c_rbtree_walk(tree, (void*) ctx, _csync_treewalk_visitor); - if( rc < 0 ) { - if( ctx->status_code == CSYNC_STATUS_OK ) - ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_TREE_ERROR); - } - ctx->callbacks.userdata = tw_ctx.userdata; - - return rc; -} - -/* - * wrapper function for treewalk on the remote tree - */ -int csync_walk_remote_tree(CSYNC *ctx, csync_treewalk_visit_func *visitor, int filter) -{ - c_rbtree_t *tree = NULL; - int rc = -1; - - if(ctx != NULL) { - ctx->status_code = CSYNC_STATUS_OK; - ctx->current = REMOTE_REPLICA; - tree = ctx->remote.tree; - } - - /* all error handling in the called function */ - rc = _csync_walk_tree(ctx, tree, visitor, filter); - return rc; -} - -/* - * wrapper function for treewalk on the local tree - */ -int csync_walk_local_tree(CSYNC *ctx, csync_treewalk_visit_func *visitor, int filter) -{ - c_rbtree_t *tree = NULL; - int rc = -1; - - if (ctx != NULL) { - ctx->status_code = CSYNC_STATUS_OK; - ctx->current = LOCAL_REPLICA; - tree = ctx->local.tree; - } - - /* all error handling in the called function */ - rc = _csync_walk_tree(ctx, tree, visitor, filter); - return rc; -} - -static void _tree_destructor(void *data) { - csync_file_stat_t *freedata = NULL; - - freedata = (csync_file_stat_t *) data; - csync_file_stat_free(freedata); -} - -/* reset all the list to empty. - * used by csync_commit and csync_destroy */ -static void _csync_clean_ctx(CSYNC *ctx) -{ - /* destroy the rbtrees */ - if (c_rbtree_size(ctx->local.tree) > 0) { - c_rbtree_destroy(ctx->local.tree, _tree_destructor); - } - - if (c_rbtree_size(ctx->remote.tree) > 0) { - c_rbtree_destroy(ctx->remote.tree, _tree_destructor); - } - - csync_rename_destroy(ctx); - - /* free memory */ - c_rbtree_free(ctx->local.tree); - c_rbtree_free(ctx->remote.tree); - - SAFE_FREE(ctx->remote.root_perms); -} - -int csync_commit(CSYNC *ctx) { - int rc = 0; - - if (ctx == NULL) { - return -1; - } - - ctx->status_code = CSYNC_STATUS_OK; - - if (ctx->statedb.db != NULL - && csync_statedb_close(ctx) < 0) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "ERR: closing of statedb failed."); - rc = -1; - } - ctx->statedb.db = NULL; - - _csync_clean_ctx(ctx); - - ctx->remote.read_from_db = 0; - ctx->read_remote_from_db = true; - ctx->db_is_empty = false; - - - /* Create new trees */ - c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp); - c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp); - - - ctx->status = CSYNC_STATUS_INIT; - SAFE_FREE(ctx->error_string); - - rc = 0; - return rc; -} - -int csync_destroy(CSYNC *ctx) { - int rc = 0; - - if (ctx == NULL) { - errno = EBADF; - return -1; - } - ctx->status_code = CSYNC_STATUS_OK; - - if (ctx->statedb.db != NULL - && csync_statedb_close(ctx) < 0) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "ERR: closing of statedb failed."); - rc = -1; - } - ctx->statedb.db = NULL; - - _csync_clean_ctx(ctx); - - SAFE_FREE(ctx->statedb.file); - SAFE_FREE(ctx->local.uri); - SAFE_FREE(ctx->error_string); - - SAFE_FREE(ctx); - - return rc; -} - -void *csync_get_userdata(CSYNC *ctx) { - if (ctx == NULL) { - return NULL; - } - return ctx->callbacks.userdata; -} - -int csync_set_userdata(CSYNC *ctx, void *userdata) { - if (ctx == NULL) { - return -1; - } - - ctx->callbacks.userdata = userdata; - - return 0; -} - -csync_auth_callback csync_get_auth_callback(CSYNC *ctx) { - if (ctx == NULL) { - return NULL; - } - - return ctx->callbacks.auth_function; -} - -int csync_set_status(CSYNC *ctx, int status) { - if (ctx == NULL || status < 0) { - return -1; - } - - ctx->status = status; - - return 0; -} - -CSYNC_STATUS csync_get_status(CSYNC *ctx) { - if (ctx == NULL) { - return -1; - } - - return ctx->status_code; -} - -const char *csync_get_status_string(CSYNC *ctx) -{ - return csync_vio_get_status_string(ctx); -} - -void csync_request_abort(CSYNC *ctx) -{ - if (ctx != NULL) { - ctx->abort = true; - } -} - -void csync_resume(CSYNC *ctx) -{ - if (ctx != NULL) { - ctx->abort = false; - } -} - -int csync_abort_requested(CSYNC *ctx) -{ - if (ctx != NULL) { - return ctx->abort; - } else { - return (1 == 0); - } -} - -void csync_file_stat_free(csync_file_stat_t *st) -{ - if (st) { - SAFE_FREE(st->directDownloadUrl); - SAFE_FREE(st->directDownloadCookies); - SAFE_FREE(st->etag); - SAFE_FREE(st->destpath); - SAFE_FREE(st->checksumHeader); - SAFE_FREE(st); - } -} diff --git a/src/csync/csync.cpp b/src/csync/csync.cpp new file mode 100644 index 000000000..6ef3d0d1d --- /dev/null +++ b/src/csync/csync.cpp @@ -0,0 +1,651 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * Copyright (c) 2012-2013 by Klaas Freitag + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_csync.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "c_lib.h" +#include "csync_private.h" +#include "csync_exclude.h" +#include "csync_statedb.h" +#include "csync_time.h" +#include "csync_util.h" +#include "csync_misc.h" +#include "std/c_private.h" + +#include "csync_update.h" +#include "csync_reconcile.h" + +#include "vio/csync_vio.h" + +#include "csync_log.h" +#include "csync_rename.h" +#include "c_jhash.h" + +static int _key_cmp(const void *key, const void *data) { + uint64_t a; + csync_file_stat_t *b; + + a = *(uint64_t *) (key); + b = (csync_file_stat_t *) data; + + if (a < b->phash) { + return -1; + } else if (a > b->phash) { + return 1; + } + + return 0; +} + +static int _data_cmp(const void *key, const void *data) { + csync_file_stat_t *a, *b; + + a = (csync_file_stat_t *) key; + b = (csync_file_stat_t *) data; + + if (a->phash < b->phash) { + return -1; + } else if (a->phash > b->phash) { + return 1; + } + + return 0; +} + +void csync_create(CSYNC **csync, const char *local) { + CSYNC *ctx; + size_t len = 0; + + ctx = (CSYNC*)c_malloc(sizeof(CSYNC)); + + ctx->status_code = CSYNC_STATUS_OK; + + /* remove trailing slashes */ + len = strlen(local); + while(len > 0 && local[len - 1] == '/') --len; + + ctx->local.uri = c_strndup(local, len); + + ctx->status_code = CSYNC_STATUS_OK; + + ctx->current_fs = NULL; + + ctx->abort = false; + + ctx->ignore_hidden_files = true; + + *csync = ctx; +} + +void csync_init(CSYNC *ctx, const char *db_file) { + assert(ctx); + /* Do not initialize twice */ + + assert(!(ctx->status & CSYNC_STATUS_INIT)); + ctx->status_code = CSYNC_STATUS_OK; + + ctx->local.type = LOCAL_REPLICA; + + ctx->remote.type = REMOTE_REPLICA; + + SAFE_FREE(ctx->statedb.file); + ctx->statedb.file = c_strdup(db_file); + + c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp); + c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp); + + ctx->remote.root_perms = 0; + + ctx->status = CSYNC_STATUS_INIT; + + /* initialize random generator */ + srand(time(NULL)); +} + +int csync_update(CSYNC *ctx) { + int rc = -1; + struct timespec start, finish; + + if (ctx == NULL) { + errno = EBADF; + return -1; + } + ctx->status_code = CSYNC_STATUS_OK; + + /* Path of database file is set in csync_init */ + if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) { + rc = -1; + return rc; + } + + ctx->status_code = CSYNC_STATUS_OK; + + csync_memstat_check(); + + if (!ctx->excludes) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "No exclude file loaded or defined!"); + } + + /* update detection for local replica */ + csync_gettime(&start); + ctx->current = LOCAL_REPLICA; + ctx->replica = ctx->local.type; + + rc = csync_ftw(ctx, ctx->local.uri, csync_walker, MAX_DEPTH); + if (rc < 0) { + if(ctx->status_code == CSYNC_STATUS_OK) { + ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR); + } + goto out; + } + + csync_gettime(&finish); + + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, + "Update detection for local replica took %.2f seconds walking %zu files.", + c_secdiff(finish, start), c_rbtree_size(ctx->local.tree)); + csync_memstat_check(); + + /* update detection for remote replica */ + csync_gettime(&start); + ctx->current = REMOTE_REPLICA; + ctx->replica = ctx->remote.type; + + rc = csync_ftw(ctx, "", csync_walker, MAX_DEPTH); + if (rc < 0) { + if(ctx->status_code == CSYNC_STATUS_OK) { + ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_UPDATE_ERROR); + } + goto out; + } + + csync_gettime(&finish); + + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, + "Update detection for remote replica took %.2f seconds " + "walking %zu files.", + c_secdiff(finish, start), c_rbtree_size(ctx->remote.tree)); + csync_memstat_check(); + + ctx->status |= CSYNC_STATUS_UPDATE; + + rc = 0; + +out: + csync_statedb_close(ctx); + return rc; +} + +int csync_reconcile(CSYNC *ctx) { + int rc = -1; + struct timespec start, finish; + + if (ctx == NULL) { + errno = EBADF; + return -1; + } + ctx->status_code = CSYNC_STATUS_OK; + + /* Reconciliation for local replica */ + csync_gettime(&start); + + if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) { + rc = -1; + return rc; + } + + ctx->current = LOCAL_REPLICA; + ctx->replica = ctx->local.type; + + rc = csync_reconcile_updates(ctx); + + csync_gettime(&finish); + + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, + "Reconciliation for local replica took %.2f seconds visiting %zu files.", + c_secdiff(finish, start), c_rbtree_size(ctx->local.tree)); + + if (rc < 0) { + if (!CSYNC_STATUS_IS_OK(ctx->status_code)) { + ctx->status_code = csync_errno_to_status( errno, CSYNC_STATUS_RECONCILE_ERROR ); + } + goto out; + } + + /* Reconciliation for remote replica */ + csync_gettime(&start); + + ctx->current = REMOTE_REPLICA; + ctx->replica = ctx->remote.type; + + rc = csync_reconcile_updates(ctx); + + csync_gettime(&finish); + + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, + "Reconciliation for remote replica took %.2f seconds visiting %zu files.", + c_secdiff(finish, start), c_rbtree_size(ctx->remote.tree)); + + if (rc < 0) { + if (!CSYNC_STATUS_IS_OK(ctx->status_code)) { + ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_RECONCILE_ERROR ); + } + goto out; + } + + ctx->status |= CSYNC_STATUS_RECONCILE; + + rc = 0; + +out: + csync_statedb_close(ctx); + return 0; +} + +/* + * local visitor which calls the user visitor with repacked stat info. + */ +static int _csync_treewalk_visitor(void *obj, void *data) { + int rc = 0; + csync_file_stat_t *cur = NULL; + CSYNC *ctx = NULL; + c_rbtree_visit_func *visitor = NULL; + _csync_treewalk_context *twctx = NULL; + TREE_WALK_FILE trav; + c_rbtree_t *other_tree = NULL; + c_rbnode_t *other_node = NULL; + + cur = (csync_file_stat_t *) obj; + ctx = (CSYNC *) data; + + if (ctx == NULL) { + return -1; + } + + /* we need the opposite tree! */ + switch (ctx->current) { + case LOCAL_REPLICA: + other_tree = ctx->remote.tree; + break; + case REMOTE_REPLICA: + other_tree = ctx->local.tree; + break; + default: + break; + } + + other_node = c_rbtree_find(other_tree, &cur->phash); + + if (!other_node) { + /* Check the renamed path as well. */ + int len; + uint64_t h = 0; + 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); + other_node = c_rbtree_find(other_tree, &h); + } + SAFE_FREE(renamed_path); + } + + if (!other_node) { + /* Check the source path as well. */ + int len; + uint64_t h = 0; + char *renamed_path = csync_rename_adjust_path_source(ctx, cur->path); + + if (!c_streq(renamed_path, cur->path)) { + len = strlen( renamed_path ); + h = c_jhash64((uint8_t *) renamed_path, len, 0); + other_node = c_rbtree_find(other_tree, &h); + } + SAFE_FREE(renamed_path); + } + + if (obj == NULL || data == NULL) { + ctx->status_code = CSYNC_STATUS_PARAM_ERROR; + return -1; + } + ctx->status_code = CSYNC_STATUS_OK; + + twctx = (_csync_treewalk_context*) ctx->callbacks.userdata; + if (twctx == NULL) { + ctx->status_code = CSYNC_STATUS_PARAM_ERROR; + return -1; + } + + if (twctx->instruction_filter > 0 && + !(twctx->instruction_filter & cur->instruction) ) { + return 0; + } + + visitor = (c_rbtree_visit_func*)(twctx->user_visitor); + if (visitor != NULL) { + trav.path = cur->path; + trav.size = cur->size; + trav.modtime = cur->modtime; + trav.mode = cur->mode; + trav.type = cur->type; + trav.instruction = cur->instruction; + trav.rename_path = cur->destpath; + trav.etag = cur->etag; + trav.file_id = cur->file_id; + trav.remotePerm = cur->remotePerm; + trav.directDownloadUrl = cur->directDownloadUrl; + trav.directDownloadCookies = cur->directDownloadCookies; + trav.inode = cur->inode; + + trav.error_status = cur->error_status; + trav.has_ignored_files = cur->has_ignored_files; + trav.checksumHeader = cur->checksumHeader; + + if( other_node ) { + csync_file_stat_t *other_stat = (csync_file_stat_t*)other_node->data; + trav.other.etag = other_stat->etag; + trav.other.file_id = other_stat->file_id; + trav.other.instruction = other_stat->instruction; + trav.other.modtime = other_stat->modtime; + trav.other.size = other_stat->size; + } else { + trav.other.etag = 0; + trav.other.file_id = 0; + trav.other.instruction = CSYNC_INSTRUCTION_NONE; + trav.other.modtime = 0; + trav.other.size = 0; + } + + rc = (*visitor)(&trav, twctx->userdata); + cur->instruction = trav.instruction; + if (trav.etag != cur->etag) { // FIXME It would be nice to have this documented + SAFE_FREE(cur->etag); + cur->etag = c_strdup(trav.etag); + } + + return rc; + } + ctx->status_code = CSYNC_STATUS_PARAM_ERROR; + return -1; +} + +/* + * treewalk function, called from its wrappers below. + * + * it encapsulates the user visitor function, the filter and the userdata + * into a treewalk_context structure and calls the rb treewalk function, + * which calls the local _csync_treewalk_visitor in this module. + * The user visitor is called from there. + */ +static int _csync_walk_tree(CSYNC *ctx, c_rbtree_t *tree, csync_treewalk_visit_func *visitor, int filter) +{ + _csync_treewalk_context tw_ctx; + int rc = -1; + + if (ctx == NULL) { + errno = EBADF; + return rc; + } + + if (visitor == NULL || tree == NULL) { + ctx->status_code = CSYNC_STATUS_PARAM_ERROR; + return rc; + } + + tw_ctx.userdata = ctx->callbacks.userdata; + tw_ctx.user_visitor = visitor; + tw_ctx.instruction_filter = filter; + + ctx->callbacks.userdata = &tw_ctx; + + rc = c_rbtree_walk(tree, (void*) ctx, _csync_treewalk_visitor); + if( rc < 0 ) { + if( ctx->status_code == CSYNC_STATUS_OK ) + ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_TREE_ERROR); + } + ctx->callbacks.userdata = tw_ctx.userdata; + + return rc; +} + +/* + * wrapper function for treewalk on the remote tree + */ +int csync_walk_remote_tree(CSYNC *ctx, csync_treewalk_visit_func *visitor, int filter) +{ + c_rbtree_t *tree = NULL; + int rc = -1; + + if(ctx != NULL) { + ctx->status_code = CSYNC_STATUS_OK; + ctx->current = REMOTE_REPLICA; + tree = ctx->remote.tree; + } + + /* all error handling in the called function */ + rc = _csync_walk_tree(ctx, tree, visitor, filter); + return rc; +} + +/* + * wrapper function for treewalk on the local tree + */ +int csync_walk_local_tree(CSYNC *ctx, csync_treewalk_visit_func *visitor, int filter) +{ + c_rbtree_t *tree = NULL; + int rc = -1; + + if (ctx != NULL) { + ctx->status_code = CSYNC_STATUS_OK; + ctx->current = LOCAL_REPLICA; + tree = ctx->local.tree; + } + + /* all error handling in the called function */ + rc = _csync_walk_tree(ctx, tree, visitor, filter); + return rc; +} + +static void _tree_destructor(void *data) { + csync_file_stat_t *freedata = NULL; + + freedata = (csync_file_stat_t *) data; + csync_file_stat_free(freedata); +} + +/* reset all the list to empty. + * used by csync_commit and csync_destroy */ +static void _csync_clean_ctx(CSYNC *ctx) +{ + /* destroy the rbtrees */ + if (c_rbtree_size(ctx->local.tree) > 0) { + c_rbtree_destroy(ctx->local.tree, _tree_destructor); + } + + if (c_rbtree_size(ctx->remote.tree) > 0) { + c_rbtree_destroy(ctx->remote.tree, _tree_destructor); + } + + csync_rename_destroy(ctx); + + /* free memory */ + c_rbtree_free(ctx->local.tree); + c_rbtree_free(ctx->remote.tree); + + SAFE_FREE(ctx->remote.root_perms); +} + +int csync_commit(CSYNC *ctx) { + int rc = 0; + + if (ctx == NULL) { + return -1; + } + + ctx->status_code = CSYNC_STATUS_OK; + + if (ctx->statedb.db != NULL + && csync_statedb_close(ctx) < 0) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "ERR: closing of statedb failed."); + rc = -1; + } + ctx->statedb.db = NULL; + + _csync_clean_ctx(ctx); + + ctx->remote.read_from_db = 0; + ctx->read_remote_from_db = true; + ctx->db_is_empty = false; + + + /* Create new trees */ + c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp); + c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp); + + + ctx->status = CSYNC_STATUS_INIT; + SAFE_FREE(ctx->error_string); + + rc = 0; + return rc; +} + +int csync_destroy(CSYNC *ctx) { + int rc = 0; + + if (ctx == NULL) { + errno = EBADF; + return -1; + } + ctx->status_code = CSYNC_STATUS_OK; + + if (ctx->statedb.db != NULL + && csync_statedb_close(ctx) < 0) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "ERR: closing of statedb failed."); + rc = -1; + } + ctx->statedb.db = NULL; + + _csync_clean_ctx(ctx); + + SAFE_FREE(ctx->statedb.file); + SAFE_FREE(ctx->local.uri); + SAFE_FREE(ctx->error_string); + + SAFE_FREE(ctx); + + return rc; +} + +void *csync_get_userdata(CSYNC *ctx) { + if (ctx == NULL) { + return NULL; + } + return ctx->callbacks.userdata; +} + +int csync_set_userdata(CSYNC *ctx, void *userdata) { + if (ctx == NULL) { + return -1; + } + + ctx->callbacks.userdata = userdata; + + return 0; +} + +csync_auth_callback csync_get_auth_callback(CSYNC *ctx) { + if (ctx == NULL) { + return NULL; + } + + return ctx->callbacks.auth_function; +} + +int csync_set_status(CSYNC *ctx, int status) { + if (ctx == NULL || status < 0) { + return -1; + } + + ctx->status = status; + + return 0; +} + +CSYNC_STATUS csync_get_status(CSYNC *ctx) { + if (ctx == NULL) { + return CSYNC_STATUS_ERROR; + } + + return ctx->status_code; +} + +const char *csync_get_status_string(CSYNC *ctx) +{ + return csync_vio_get_status_string(ctx); +} + +void csync_request_abort(CSYNC *ctx) +{ + if (ctx != NULL) { + ctx->abort = true; + } +} + +void csync_resume(CSYNC *ctx) +{ + if (ctx != NULL) { + ctx->abort = false; + } +} + +int csync_abort_requested(CSYNC *ctx) +{ + if (ctx != NULL) { + return ctx->abort; + } else { + return (1 == 0); + } +} + +void csync_file_stat_free(csync_file_stat_t *st) +{ + if (st) { + SAFE_FREE(st->directDownloadUrl); + SAFE_FREE(st->directDownloadCookies); + SAFE_FREE(st->etag); + SAFE_FREE(st->destpath); + SAFE_FREE(st->checksumHeader); + SAFE_FREE(st); + } +} diff --git a/src/csync/csync.h b/src/csync/csync.h index daf778c24..73eb5790a 100644 --- a/src/csync/csync.h +++ b/src/csync/csync.h @@ -40,10 +40,6 @@ #include #include -#ifdef __cplusplus -extern "C" { -#endif - enum csync_status_codes_e { CSYNC_STATUS_OK = 0, @@ -221,7 +217,7 @@ struct csync_vio_file_stat_s { int fields; // actually enum csync_vio_file_stat_fields_e fields; enum csync_vio_file_type_e type; - enum csync_vio_file_flags_e flags; + int flags; char *original_name; // only set if locale conversion fails @@ -504,10 +500,6 @@ int OCSYNC_EXPORT csync_abort_requested(CSYNC *ctx); char OCSYNC_EXPORT *csync_normalize_etag(const char *); time_t OCSYNC_EXPORT oc_httpdate_parse( const char *date ); -#ifdef __cplusplus -} -#endif - /** * }@ */ diff --git a/src/csync/csync_exclude.c b/src/csync/csync_exclude.c deleted file mode 100644 index fea662504..000000000 --- a/src/csync/csync_exclude.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config_csync.h" - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include - -#include -#include -#include - -#include "c_lib.h" -#include "c_private.h" - -#include "csync_private.h" -#include "csync_exclude.h" -#include "csync_misc.h" - -#ifdef _WIN32 -#include -#else -#include -#endif - -#define CSYNC_LOG_CATEGORY_NAME "csync.exclude" -#include "csync_log.h" - -#ifndef WITH_TESTING -static -#endif -int _csync_exclude_add(c_strlist_t **inList, const char *string) { - size_t i = 0; - - // We never want duplicates, so check whether the string is already - // in the list first. - if (*inList) { - for (i = 0; i < (*inList)->count; ++i) { - char *pattern = (*inList)->vector[i]; - if (c_streq(pattern, string)) { - return 1; - } - } - } - return c_strlist_add_grow(inList, string); -} - -/** Expands C-like escape sequences. - * - * The returned string is heap-allocated and owned by the caller. - */ -static const char *csync_exclude_expand_escapes(const char * input) -{ - size_t i_len = strlen(input) + 1; - char *out = c_malloc(i_len); // out can only be shorter - - size_t i = 0; - size_t o = 0; - for (; i < i_len; ++i) { - if (input[i] == '\\') { - // at worst input[i+1] is \0 - switch (input[i+1]) { - case '\'': out[o++] = '\''; break; - case '"': out[o++] = '"'; break; - case '?': out[o++] = '?'; break; - case '\\': out[o++] = '\\'; break; - case 'a': out[o++] = '\a'; break; - case 'b': out[o++] = '\b'; break; - case 'f': out[o++] = '\f'; break; - case 'n': out[o++] = '\n'; break; - case 'r': out[o++] = '\r'; break; - case 't': out[o++] = '\t'; break; - case 'v': out[o++] = '\v'; break; - default: - out[o++] = input[i]; - out[o++] = input[i+1]; - break; - } - ++i; - } else { - out[o++] = input[i]; - } - } - return out; -} - -int csync_exclude_load(const char *fname, c_strlist_t **list) { - int fd = -1; - int i = 0; - int rc = -1; - int64_t size; - char *buf = NULL; - char *entry = NULL; - mbchar_t *w_fname; - - if (fname == NULL) { - return -1; - } - -#ifdef _WIN32 - _fmode = _O_BINARY; -#endif - - w_fname = c_utf8_path_to_locale(fname); - if (w_fname == NULL) { - return -1; - } - - fd = _topen(w_fname, O_RDONLY); - c_free_locale_string(w_fname); - if (fd < 0) { - return -1; - } - - size = lseek(fd, 0, SEEK_END); - if (size < 0) { - rc = -1; - goto out; - } - lseek(fd, 0, SEEK_SET); - if (size == 0) { - rc = 0; - goto out; - } - buf = c_malloc(size + 1); - if (read(fd, buf, size) != size) { - rc = -1; - goto out; - } - buf[size] = '\0'; - - /* FIXME: Use fgets and don't add duplicates */ - entry = buf; - for (i = 0; i < size; i++) { - if (buf[i] == '\n' || buf[i] == '\r') { - if (entry != buf + i) { - buf[i] = '\0'; - if (*entry != '#') { - const char *unescaped = csync_exclude_expand_escapes(entry); - rc = _csync_exclude_add(list, unescaped); - if( rc == 0 ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Adding entry: %s", unescaped); - } - SAFE_FREE(unescaped); - if (rc < 0) { - goto out; - } - } - } - entry = buf + i + 1; - } - } - - rc = 0; -out: - SAFE_FREE(buf); - close(fd); - return rc; -} - -// See http://support.microsoft.com/kb/74496 and -// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx -// Additionally, we ignore '$Recycle.Bin', see https://github.com/owncloud/client/issues/2955 -static const char* win_reserved_words[] = {"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", - "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", - "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", "CLOCK$", "$Recycle.Bin" }; - -bool csync_is_windows_reserved_word(const char* filename) { - - size_t win_reserve_words_len = sizeof(win_reserved_words) / sizeof(char*); - size_t j; - - for (j = 0; j < win_reserve_words_len; j++) { - int len_reserved_word = strlen(win_reserved_words[j]); - int len_filename = strlen(filename); - if (len_filename == 2 && filename[1] == ':') { - if (filename[0] >= 'a' && filename[0] <= 'z') { - return true; - } - if (filename[0] >= 'A' && filename[0] <= 'Z') { - return true; - } - } - if (c_strncasecmp(filename, win_reserved_words[j], len_reserved_word) == 0) { - if (len_filename == len_reserved_word) { - return true; - } - if ((len_filename > len_reserved_word) && (filename[len_reserved_word] == '.')) { - return true; - } - } - } - return false; -} - -static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const char *path, int filetype, bool check_leading_dirs) { - size_t i = 0; - const char *bname = NULL; - size_t blen = 0; - char *conflict = NULL; - int rc = -1; - CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED; - CSYNC_EXCLUDE_TYPE type = CSYNC_NOT_EXCLUDED; - - /* split up the path */ - bname = strrchr(path, '/'); - if (bname) { - bname += 1; // don't include the / - } else { - bname = path; - } - blen = strlen(bname); - - rc = csync_fnmatch("._sync_*.db*", bname, 0); - if (rc == 0) { - match = CSYNC_FILE_SILENTLY_EXCLUDED; - goto out; - } - rc = csync_fnmatch(".sync_*.db*", bname, 0); - if (rc == 0) { - match = CSYNC_FILE_SILENTLY_EXCLUDED; - goto out; - } - rc = csync_fnmatch(".csync_journal.db*", bname, 0); - if (rc == 0) { - match = CSYNC_FILE_SILENTLY_EXCLUDED; - goto out; - } - - // check the strlen and ignore the file if its name is longer than 254 chars. - // whenever changing this also check createDownloadTmpFileName - if (blen > 254) { - match = CSYNC_FILE_EXCLUDE_LONG_FILENAME; - goto out; - } - -#ifdef _WIN32 - // Windows cannot sync files ending in spaces (#2176). It also cannot - // distinguish files ending in '.' from files without an ending, - // as '.' is a separator that is not stored internally, so let's - // not allow to sync those to avoid file loss/ambiguities (#416) - if (blen > 1) { - if (bname[blen-1]== ' ') { - match = CSYNC_FILE_EXCLUDE_TRAILING_SPACE; - goto out; - } else if (bname[blen-1]== '.' ) { - match = CSYNC_FILE_EXCLUDE_INVALID_CHAR; - goto out; - } - } - - if (csync_is_windows_reserved_word(bname)) { - match = CSYNC_FILE_EXCLUDE_INVALID_CHAR; - goto out; - } - - // Filter out characters not allowed in a filename on windows - const char *p = NULL; - for (p = path; *p; p++) { - switch (*p) { - case '\\': - case ':': - case '?': - case '*': - case '"': - case '>': - case '<': - case '|': - match = CSYNC_FILE_EXCLUDE_INVALID_CHAR; - goto out; - default: - break; - } - } -#endif - - /* We create a desktop.ini on Windows for the sidebar icon, make sure we don't sync them. */ - rc = csync_fnmatch("Desktop.ini", bname, 0); - if (rc == 0) { - match = CSYNC_FILE_SILENTLY_EXCLUDED; - goto out; - } - - rc = csync_fnmatch(".owncloudsync.log*", bname, 0); - if (rc == 0) { - match = CSYNC_FILE_SILENTLY_EXCLUDED; - goto out; - } - - /* Always ignore conflict files, not only via the exclude list */ - rc = csync_fnmatch("*_conflict-*", bname, 0); - if (rc == 0) { - match = CSYNC_FILE_EXCLUDE_CONFLICT; - goto out; - } - - if (getenv("CSYNC_CONFLICT_FILE_USERNAME")) { - rc = asprintf(&conflict, "*_conflict_%s-*", getenv("CSYNC_CONFLICT_FILE_USERNAME")); - if (rc < 0) { - goto out; - } - rc = csync_fnmatch(conflict, path, 0); - if (rc == 0) { - match = CSYNC_FILE_EXCLUDE_CONFLICT; - SAFE_FREE(conflict); - goto out; - } - SAFE_FREE(conflict); - } - - if( ! excludes ) { - goto out; - } - - c_strlist_t *path_components = NULL; - if (check_leading_dirs) { - /* Build a list of path components to check. */ - path_components = c_strlist_new(32); - char *path_split = strdup(path); - size_t len = strlen(path_split); - for (i = len; ; --i) { - // read backwards until a path separator is found - if (i != 0 && path_split[i-1] != '/') { - continue; - } - - // check 'basename', i.e. for "/foo/bar/fi" we'd check 'fi', 'bar', 'foo' - if (path_split[i] != 0) { - c_strlist_add_grow(&path_components, path_split + i); - } - - if (i == 0) { - break; - } - - // check 'dirname', i.e. for "/foo/bar/fi" we'd check '/foo/bar', '/foo' - path_split[i-1] = '\0'; - c_strlist_add_grow(&path_components, path_split); - } - SAFE_FREE(path_split); - } - - /* Loop over all exclude patterns and evaluate the given path */ - for (i = 0; match == CSYNC_NOT_EXCLUDED && i < excludes->count; i++) { - bool match_dirs_only = false; - char *pattern = excludes->vector[i]; - - type = CSYNC_FILE_EXCLUDE_LIST; - if (!pattern[0]) { /* empty pattern */ - continue; - } - /* Excludes starting with ']' means it can be cleanup */ - if (pattern[0] == ']') { - ++pattern; - if (filetype == CSYNC_FTW_TYPE_FILE) { - type = CSYNC_FILE_EXCLUDE_AND_REMOVE; - } - } - /* Check if the pattern applies to pathes only. */ - if (pattern[strlen(pattern)-1] == '/') { - if (!check_leading_dirs && filetype == CSYNC_FTW_TYPE_FILE) { - continue; - } - match_dirs_only = true; - pattern[strlen(pattern)-1] = '\0'; /* Cut off the slash */ - } - - /* check if the pattern contains a / and if, compare to the whole path */ - if (strchr(pattern, '/')) { - rc = csync_fnmatch(pattern, path, FNM_PATHNAME); - if( rc == 0 ) { - match = type; - } - /* if the pattern requires a dir, but path is not, its still not excluded. */ - if (match_dirs_only && filetype != CSYNC_FTW_TYPE_DIR) { - match = CSYNC_NOT_EXCLUDED; - } - } - - /* if still not excluded, check each component and leading directory of the path */ - if (match == CSYNC_NOT_EXCLUDED && check_leading_dirs) { - size_t j = 0; - if (match_dirs_only && filetype == CSYNC_FTW_TYPE_FILE) { - j = 1; // skip the first entry, which is bname - } - for (; j < path_components->count; ++j) { - rc = csync_fnmatch(pattern, path_components->vector[j], 0); - if (rc == 0) { - match = type; - break; - } - } - } else if (match == CSYNC_NOT_EXCLUDED && !check_leading_dirs) { - rc = csync_fnmatch(pattern, bname, 0); - if (rc == 0) { - match = type; - } - } - if (match_dirs_only) { - /* restore the '/' */ - pattern[strlen(pattern)] = '/'; - } - } - c_strlist_destroy(path_components); - - out: - - return match; -} - -CSYNC_EXCLUDE_TYPE csync_excluded_traversal(c_strlist_t *excludes, const char *path, int filetype) { - return _csync_excluded_common(excludes, path, filetype, false); -} - -CSYNC_EXCLUDE_TYPE csync_excluded_no_ctx(c_strlist_t *excludes, const char *path, int filetype) { - return _csync_excluded_common(excludes, path, filetype, true); -} - diff --git a/src/csync/csync_exclude.cpp b/src/csync/csync_exclude.cpp new file mode 100644 index 000000000..9ab187e71 --- /dev/null +++ b/src/csync/csync_exclude.cpp @@ -0,0 +1,436 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_csync.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include + +#include +#include +#include + +#include "c_lib.h" +#include "c_private.h" + +#include "csync_private.h" +#include "csync_exclude.h" +#include "csync_misc.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +#define CSYNC_LOG_CATEGORY_NAME "csync.exclude" +#include "csync_log.h" + +#ifndef WITH_TESTING +static +#endif +int _csync_exclude_add(c_strlist_t **inList, const char *string) { + size_t i = 0; + + // We never want duplicates, so check whether the string is already + // in the list first. + if (*inList) { + for (i = 0; i < (*inList)->count; ++i) { + char *pattern = (*inList)->vector[i]; + if (c_streq(pattern, string)) { + return 1; + } + } + } + return c_strlist_add_grow(inList, string); +} + +/** Expands C-like escape sequences. + * + * The returned string is heap-allocated and owned by the caller. + */ +static const char *csync_exclude_expand_escapes(const char * input) +{ + size_t i_len = strlen(input) + 1; + char *out = (char*)c_malloc(i_len); // out can only be shorter + + size_t i = 0; + size_t o = 0; + for (; i < i_len; ++i) { + if (input[i] == '\\') { + // at worst input[i+1] is \0 + switch (input[i+1]) { + case '\'': out[o++] = '\''; break; + case '"': out[o++] = '"'; break; + case '?': out[o++] = '?'; break; + case '\\': out[o++] = '\\'; break; + case 'a': out[o++] = '\a'; break; + case 'b': out[o++] = '\b'; break; + case 'f': out[o++] = '\f'; break; + case 'n': out[o++] = '\n'; break; + case 'r': out[o++] = '\r'; break; + case 't': out[o++] = '\t'; break; + case 'v': out[o++] = '\v'; break; + default: + out[o++] = input[i]; + out[o++] = input[i+1]; + break; + } + ++i; + } else { + out[o++] = input[i]; + } + } + return out; +} + +int csync_exclude_load(const char *fname, c_strlist_t **list) { + int fd = -1; + int i = 0; + int rc = -1; + int64_t size; + char *buf = NULL; + char *entry = NULL; + mbchar_t *w_fname; + + if (fname == NULL) { + return -1; + } + +#ifdef _WIN32 + _fmode = _O_BINARY; +#endif + + w_fname = c_utf8_path_to_locale(fname); + if (w_fname == NULL) { + return -1; + } + + fd = _topen(w_fname, O_RDONLY); + c_free_locale_string(w_fname); + if (fd < 0) { + return -1; + } + + size = lseek(fd, 0, SEEK_END); + if (size < 0) { + rc = -1; + goto out; + } + lseek(fd, 0, SEEK_SET); + if (size == 0) { + rc = 0; + goto out; + } + buf = (char*)c_malloc(size + 1); + if (read(fd, buf, size) != size) { + rc = -1; + goto out; + } + buf[size] = '\0'; + + /* FIXME: Use fgets and don't add duplicates */ + entry = buf; + for (i = 0; i < size; i++) { + if (buf[i] == '\n' || buf[i] == '\r') { + if (entry != buf + i) { + buf[i] = '\0'; + if (*entry != '#') { + const char *unescaped = csync_exclude_expand_escapes(entry); + rc = _csync_exclude_add(list, unescaped); + if( rc == 0 ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Adding entry: %s", unescaped); + } + SAFE_FREE(unescaped); + if (rc < 0) { + goto out; + } + } + } + entry = buf + i + 1; + } + } + + rc = 0; +out: + SAFE_FREE(buf); + close(fd); + return rc; +} + +// See http://support.microsoft.com/kb/74496 and +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx +// Additionally, we ignore '$Recycle.Bin', see https://github.com/owncloud/client/issues/2955 +static const char* win_reserved_words[] = {"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", + "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", + "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", "CLOCK$", "$Recycle.Bin" }; + +bool csync_is_windows_reserved_word(const char* filename) { + + size_t win_reserve_words_len = sizeof(win_reserved_words) / sizeof(char*); + size_t j; + + for (j = 0; j < win_reserve_words_len; j++) { + int len_reserved_word = strlen(win_reserved_words[j]); + int len_filename = strlen(filename); + if (len_filename == 2 && filename[1] == ':') { + if (filename[0] >= 'a' && filename[0] <= 'z') { + return true; + } + if (filename[0] >= 'A' && filename[0] <= 'Z') { + return true; + } + } + if (c_strncasecmp(filename, win_reserved_words[j], len_reserved_word) == 0) { + if (len_filename == len_reserved_word) { + return true; + } + if ((len_filename > len_reserved_word) && (filename[len_reserved_word] == '.')) { + return true; + } + } + } + return false; +} + +static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const char *path, int filetype, bool check_leading_dirs) { + size_t i = 0; + const char *bname = NULL; + size_t blen = 0; + char *conflict = NULL; + int rc = -1; + CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED; + CSYNC_EXCLUDE_TYPE type = CSYNC_NOT_EXCLUDED; + c_strlist_t *path_components = NULL; + + /* split up the path */ + bname = strrchr(path, '/'); + if (bname) { + bname += 1; // don't include the / + } else { + bname = path; + } + blen = strlen(bname); + + rc = csync_fnmatch("._sync_*.db*", bname, 0); + if (rc == 0) { + match = CSYNC_FILE_SILENTLY_EXCLUDED; + goto out; + } + rc = csync_fnmatch(".sync_*.db*", bname, 0); + if (rc == 0) { + match = CSYNC_FILE_SILENTLY_EXCLUDED; + goto out; + } + rc = csync_fnmatch(".csync_journal.db*", bname, 0); + if (rc == 0) { + match = CSYNC_FILE_SILENTLY_EXCLUDED; + goto out; + } + + // check the strlen and ignore the file if its name is longer than 254 chars. + // whenever changing this also check createDownloadTmpFileName + if (blen > 254) { + match = CSYNC_FILE_EXCLUDE_LONG_FILENAME; + goto out; + } + +#ifdef _WIN32 + // Windows cannot sync files ending in spaces (#2176). It also cannot + // distinguish files ending in '.' from files without an ending, + // as '.' is a separator that is not stored internally, so let's + // not allow to sync those to avoid file loss/ambiguities (#416) + if (blen > 1) { + if (bname[blen-1]== ' ') { + match = CSYNC_FILE_EXCLUDE_TRAILING_SPACE; + goto out; + } else if (bname[blen-1]== '.' ) { + match = CSYNC_FILE_EXCLUDE_INVALID_CHAR; + goto out; + } + } + + if (csync_is_windows_reserved_word(bname)) { + match = CSYNC_FILE_EXCLUDE_INVALID_CHAR; + goto out; + } + + // Filter out characters not allowed in a filename on windows + for (const char *p = path; *p; p++) { + switch (*p) { + case '\\': + case ':': + case '?': + case '*': + case '"': + case '>': + case '<': + case '|': + match = CSYNC_FILE_EXCLUDE_INVALID_CHAR; + goto out; + default: + break; + } + } +#endif + + /* We create a desktop.ini on Windows for the sidebar icon, make sure we don't sync them. */ + rc = csync_fnmatch("Desktop.ini", bname, 0); + if (rc == 0) { + match = CSYNC_FILE_SILENTLY_EXCLUDED; + goto out; + } + + rc = csync_fnmatch(".owncloudsync.log*", bname, 0); + if (rc == 0) { + match = CSYNC_FILE_SILENTLY_EXCLUDED; + goto out; + } + + /* Always ignore conflict files, not only via the exclude list */ + rc = csync_fnmatch("*_conflict-*", bname, 0); + if (rc == 0) { + match = CSYNC_FILE_EXCLUDE_CONFLICT; + goto out; + } + + if (getenv("CSYNC_CONFLICT_FILE_USERNAME")) { + rc = asprintf(&conflict, "*_conflict_%s-*", getenv("CSYNC_CONFLICT_FILE_USERNAME")); + if (rc < 0) { + goto out; + } + rc = csync_fnmatch(conflict, path, 0); + if (rc == 0) { + match = CSYNC_FILE_EXCLUDE_CONFLICT; + SAFE_FREE(conflict); + goto out; + } + SAFE_FREE(conflict); + } + + if( ! excludes ) { + goto out; + } + + if (check_leading_dirs) { + /* Build a list of path components to check. */ + path_components = c_strlist_new(32); + char *path_split = strdup(path); + size_t len = strlen(path_split); + for (i = len; ; --i) { + // read backwards until a path separator is found + if (i != 0 && path_split[i-1] != '/') { + continue; + } + + // check 'basename', i.e. for "/foo/bar/fi" we'd check 'fi', 'bar', 'foo' + if (path_split[i] != 0) { + c_strlist_add_grow(&path_components, path_split + i); + } + + if (i == 0) { + break; + } + + // check 'dirname', i.e. for "/foo/bar/fi" we'd check '/foo/bar', '/foo' + path_split[i-1] = '\0'; + c_strlist_add_grow(&path_components, path_split); + } + SAFE_FREE(path_split); + } + + /* Loop over all exclude patterns and evaluate the given path */ + for (i = 0; match == CSYNC_NOT_EXCLUDED && i < excludes->count; i++) { + bool match_dirs_only = false; + char *pattern = excludes->vector[i]; + + type = CSYNC_FILE_EXCLUDE_LIST; + if (!pattern[0]) { /* empty pattern */ + continue; + } + /* Excludes starting with ']' means it can be cleanup */ + if (pattern[0] == ']') { + ++pattern; + if (filetype == CSYNC_FTW_TYPE_FILE) { + type = CSYNC_FILE_EXCLUDE_AND_REMOVE; + } + } + /* Check if the pattern applies to pathes only. */ + if (pattern[strlen(pattern)-1] == '/') { + if (!check_leading_dirs && filetype == CSYNC_FTW_TYPE_FILE) { + continue; + } + match_dirs_only = true; + pattern[strlen(pattern)-1] = '\0'; /* Cut off the slash */ + } + + /* check if the pattern contains a / and if, compare to the whole path */ + if (strchr(pattern, '/')) { + rc = csync_fnmatch(pattern, path, FNM_PATHNAME); + if( rc == 0 ) { + match = type; + } + /* if the pattern requires a dir, but path is not, its still not excluded. */ + if (match_dirs_only && filetype != CSYNC_FTW_TYPE_DIR) { + match = CSYNC_NOT_EXCLUDED; + } + } + + /* if still not excluded, check each component and leading directory of the path */ + if (match == CSYNC_NOT_EXCLUDED && check_leading_dirs) { + size_t j = 0; + if (match_dirs_only && filetype == CSYNC_FTW_TYPE_FILE) { + j = 1; // skip the first entry, which is bname + } + for (; j < path_components->count; ++j) { + rc = csync_fnmatch(pattern, path_components->vector[j], 0); + if (rc == 0) { + match = type; + break; + } + } + } else if (match == CSYNC_NOT_EXCLUDED && !check_leading_dirs) { + rc = csync_fnmatch(pattern, bname, 0); + if (rc == 0) { + match = type; + } + } + if (match_dirs_only) { + /* restore the '/' */ + pattern[strlen(pattern)] = '/'; + } + } + c_strlist_destroy(path_components); + + out: + + return match; +} + +CSYNC_EXCLUDE_TYPE csync_excluded_traversal(c_strlist_t *excludes, const char *path, int filetype) { + return _csync_excluded_common(excludes, path, filetype, false); +} + +CSYNC_EXCLUDE_TYPE csync_excluded_no_ctx(c_strlist_t *excludes, const char *path, int filetype) { + return _csync_excluded_common(excludes, path, filetype, true); +} + diff --git a/src/csync/csync_log.c b/src/csync/csync_log.c deleted file mode 100644 index 4a2e24a32..000000000 --- a/src/csync/csync_log.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config_csync.h" - -#include -#include -#include - -#include "csync_private.h" -#include "csync_log.h" - -CSYNC_THREAD int csync_log_level; -CSYNC_THREAD csync_log_callback csync_log_cb; - -void csync_log(int verbosity, - const char *function, - const char *format, ...) -{ - csync_log_callback log_fn = csync_get_log_callback(); - if (log_fn && verbosity <= csync_get_log_level()) { - char buffer[1024]; - va_list va; - - va_start(va, format); - vsnprintf(buffer, sizeof(buffer), format, va); - va_end(va); - - log_fn(verbosity, function, buffer); - return; - } -} - -int csync_set_log_level(int level) { - if (level < 0) { - return -1; - } - - csync_log_level = level; - - return 0; -} - -int csync_get_log_level(void) { - return csync_log_level; -} - -int csync_set_log_callback(csync_log_callback cb) { - if (cb == NULL) { - return -1; - } - - csync_log_cb = cb; - - return 0; -} - -csync_log_callback csync_get_log_callback(void) { - return csync_log_cb; -} diff --git a/src/csync/csync_log.cpp b/src/csync/csync_log.cpp new file mode 100644 index 000000000..4a2e24a32 --- /dev/null +++ b/src/csync/csync_log.cpp @@ -0,0 +1,77 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_csync.h" + +#include +#include +#include + +#include "csync_private.h" +#include "csync_log.h" + +CSYNC_THREAD int csync_log_level; +CSYNC_THREAD csync_log_callback csync_log_cb; + +void csync_log(int verbosity, + const char *function, + const char *format, ...) +{ + csync_log_callback log_fn = csync_get_log_callback(); + if (log_fn && verbosity <= csync_get_log_level()) { + char buffer[1024]; + va_list va; + + va_start(va, format); + vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + + log_fn(verbosity, function, buffer); + return; + } +} + +int csync_set_log_level(int level) { + if (level < 0) { + return -1; + } + + csync_log_level = level; + + return 0; +} + +int csync_get_log_level(void) { + return csync_log_level; +} + +int csync_set_log_callback(csync_log_callback cb) { + if (cb == NULL) { + return -1; + } + + csync_log_cb = cb; + + return 0; +} + +csync_log_callback csync_get_log_callback(void) { + return csync_log_cb; +} diff --git a/src/csync/csync_misc.c b/src/csync/csync_misc.c deleted file mode 100644 index d23236113..000000000 --- a/src/csync/csync_misc.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * Copyright (c) 2012-2013 by Klaas Freitag - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config_csync.h" - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include -#include - -#if _WIN32 -# ifndef _WIN32_IE -# define _WIN32_IE 0x0501 // SHGetSpecialFolderPath -# endif -# include -#else /* _WIN32 */ -# include -#endif /* _WIN32 */ - -#include "c_lib.h" -#include "csync_misc.h" -#include "csync_macros.h" -#include "csync_log.h" - -#ifdef HAVE_FNMATCH -#include - -int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags) { - return fnmatch(__pattern, __name, __flags); -} - -#else /* HAVE_FNMATCH */ - -#include -int csync_fnmatch(const char *pattern, const char *name, int flags) { - BOOL match; - - (void) flags; - - match = PathMatchSpecA(name, pattern); - - if(match) - return 0; - else - return 1; -} -#endif /* HAVE_FNMATCH */ - -CSYNC_STATUS csync_errno_to_status(int error, CSYNC_STATUS default_status) -{ - CSYNC_STATUS status = CSYNC_STATUS_OK; - - switch (error) { - case 0: - status = CSYNC_STATUS_OK; - break; - /* The custom errnos first. */ - case ERRNO_GENERAL_ERROR: - status = CSYNC_STATUS_UNSUCCESSFUL; - break; - case ERRNO_LOOKUP_ERROR: /* In Neon: Server or proxy hostname lookup failed */ - status = CSYNC_STATUS_LOOKUP_ERROR; - break; - case ERRNO_USER_UNKNOWN_ON_SERVER: /* Neon: User authentication on server failed. */ - status = CSYNC_STATUS_SERVER_AUTH_ERROR; - break; - case ERRNO_PROXY_AUTH: - status = CSYNC_STATUS_PROXY_AUTH_ERROR; /* Neon: User authentication on proxy failed */ - break; - case ERRNO_CONNECT: - status = CSYNC_STATUS_CONNECT_ERROR; /* Network: Connection error */ - break; - case ERRNO_TIMEOUT: - status = CSYNC_STATUS_TIMEOUT; /* Network: Timeout error */ - break; - case ERRNO_SERVICE_UNAVAILABLE: - status = CSYNC_STATUS_SERVICE_UNAVAILABLE; /* Service temporarily down */ - break; - case ERRNO_STORAGE_UNAVAILABLE: - status = CSYNC_STATUS_STORAGE_UNAVAILABLE; /* Storage temporarily unavailable */ - break; - case EFBIG: - status = CSYNC_STATUS_FILE_SIZE_ERROR; /* File larger than 2MB */ - break; - case ERRNO_PRECONDITION: - case ERRNO_RETRY: - case ERRNO_REDIRECT: - case ERRNO_WRONG_CONTENT: - status = CSYNC_STATUS_HTTP_ERROR; - break; - - case EPERM: /* Operation not permitted */ - case EACCES: /* Permission denied */ - status = CSYNC_STATUS_PERMISSION_DENIED; - break; - case ENOENT: /* No such file or directory */ - status = CSYNC_STATUS_NOT_FOUND; - break; - case EAGAIN: /* Try again */ - status = CSYNC_STATUS_TIMEOUT; - break; - case EEXIST: /* File exists */ - status = CSYNC_STATUS_FILE_EXISTS; - break; - case EINVAL: - status = CSYNC_STATUS_PARAM_ERROR; - break; - case ENOSPC: - status = CSYNC_STATUS_OUT_OF_SPACE; - break; - - /* All the remaining basic errnos: */ - case EIO: /* I/O error */ - case ESRCH: /* No such process */ - case EINTR: /* Interrupted system call */ - case ENXIO: /* No such device or address */ - case E2BIG: /* Argument list too long */ - case ENOEXEC: /* Exec format error */ - case EBADF: /* Bad file number */ - case ECHILD: /* No child processes */ - case ENOMEM: /* Out of memory */ - case EFAULT: /* Bad address */ -#ifndef _WIN32 - case ENOTBLK: /* Block device required */ -#endif - case EBUSY: /* Device or resource busy */ - case EXDEV: /* Cross-device link */ - case ENODEV: /* No such device */ - case ENOTDIR: /* Not a directory */ - case EISDIR: /* Is a directory */ - case ENFILE: /* File table overflow */ - case EMFILE: /* Too many open files */ - case ENOTTY: /* Not a typewriter */ -#ifndef _WIN32 - case ETXTBSY: /* Text file busy */ -#endif - case ESPIPE: /* Illegal seek */ - case EROFS: /* Read-only file system */ - case EMLINK: /* Too many links */ - case EPIPE: /* Broken pipe */ - - case ERRNO_ERROR_STRING: - default: - status = default_status; - } - - return status; -} - -/* Remove possible quotes, and also the -gzip at the end - * Remove "-gzip" at the end (cf. https://github.comowncloud/client/issues/1195) - * The caller must take ownership of the resulting string. - */ -char *csync_normalize_etag(const char *etag) -{ - int len = 0; - char *buf = NULL; - if (!etag) - return NULL; - - len = strlen(etag); - /* strip "XXXX-gzip" */ - if(len >= 7 && etag[0] == '"' && c_streq(etag + len - 6, "-gzip\"")) { - etag++; - len -= 7; - } - /* strip leading -gzip */ - if(len >= 5 && c_streq(etag + len - 5, "-gzip")) { - len -= 5; - } - /* strip normal quotes */ - if (etag[0] == '"' && etag[len-1] == '"') { - etag++; - len -= 2; - } - - buf = c_malloc( len+1 ); - strncpy( buf, etag, len ); - buf[len] = '\0'; - return buf; -} - diff --git a/src/csync/csync_misc.cpp b/src/csync/csync_misc.cpp new file mode 100644 index 000000000..cf719439f --- /dev/null +++ b/src/csync/csync_misc.cpp @@ -0,0 +1,206 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * Copyright (c) 2012-2013 by Klaas Freitag + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_csync.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include + +#if _WIN32 +# ifndef _WIN32_IE +# define _WIN32_IE 0x0501 // SHGetSpecialFolderPath +# endif +# include +#else /* _WIN32 */ +# include +#endif /* _WIN32 */ + +#include "c_lib.h" +#include "csync_misc.h" +#include "csync_macros.h" +#include "csync_log.h" + +#ifdef HAVE_FNMATCH +#include + +int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags) { + return fnmatch(__pattern, __name, __flags); +} + +#else /* HAVE_FNMATCH */ + +#include +int csync_fnmatch(const char *pattern, const char *name, int flags) { + BOOL match; + + (void) flags; + + match = PathMatchSpecA(name, pattern); + + if(match) + return 0; + else + return 1; +} +#endif /* HAVE_FNMATCH */ + +CSYNC_STATUS csync_errno_to_status(int error, CSYNC_STATUS default_status) +{ + CSYNC_STATUS status = CSYNC_STATUS_OK; + + switch (error) { + case 0: + status = CSYNC_STATUS_OK; + break; + /* The custom errnos first. */ + case ERRNO_GENERAL_ERROR: + status = CSYNC_STATUS_UNSUCCESSFUL; + break; + case ERRNO_LOOKUP_ERROR: /* In Neon: Server or proxy hostname lookup failed */ + status = CSYNC_STATUS_LOOKUP_ERROR; + break; + case ERRNO_USER_UNKNOWN_ON_SERVER: /* Neon: User authentication on server failed. */ + status = CSYNC_STATUS_SERVER_AUTH_ERROR; + break; + case ERRNO_PROXY_AUTH: + status = CSYNC_STATUS_PROXY_AUTH_ERROR; /* Neon: User authentication on proxy failed */ + break; + case ERRNO_CONNECT: + status = CSYNC_STATUS_CONNECT_ERROR; /* Network: Connection error */ + break; + case ERRNO_TIMEOUT: + status = CSYNC_STATUS_TIMEOUT; /* Network: Timeout error */ + break; + case ERRNO_SERVICE_UNAVAILABLE: + status = CSYNC_STATUS_SERVICE_UNAVAILABLE; /* Service temporarily down */ + break; + case ERRNO_STORAGE_UNAVAILABLE: + status = CSYNC_STATUS_STORAGE_UNAVAILABLE; /* Storage temporarily unavailable */ + break; + case EFBIG: + status = CSYNC_STATUS_FILE_SIZE_ERROR; /* File larger than 2MB */ + break; + case ERRNO_PRECONDITION: + case ERRNO_RETRY: + case ERRNO_REDIRECT: + case ERRNO_WRONG_CONTENT: + status = CSYNC_STATUS_HTTP_ERROR; + break; + + case EPERM: /* Operation not permitted */ + case EACCES: /* Permission denied */ + status = CSYNC_STATUS_PERMISSION_DENIED; + break; + case ENOENT: /* No such file or directory */ + status = CSYNC_STATUS_NOT_FOUND; + break; + case EAGAIN: /* Try again */ + status = CSYNC_STATUS_TIMEOUT; + break; + case EEXIST: /* File exists */ + status = CSYNC_STATUS_FILE_EXISTS; + break; + case EINVAL: + status = CSYNC_STATUS_PARAM_ERROR; + break; + case ENOSPC: + status = CSYNC_STATUS_OUT_OF_SPACE; + break; + + /* All the remaining basic errnos: */ + case EIO: /* I/O error */ + case ESRCH: /* No such process */ + case EINTR: /* Interrupted system call */ + case ENXIO: /* No such device or address */ + case E2BIG: /* Argument list too long */ + case ENOEXEC: /* Exec format error */ + case EBADF: /* Bad file number */ + case ECHILD: /* No child processes */ + case ENOMEM: /* Out of memory */ + case EFAULT: /* Bad address */ +#ifndef _WIN32 + case ENOTBLK: /* Block device required */ +#endif + case EBUSY: /* Device or resource busy */ + case EXDEV: /* Cross-device link */ + case ENODEV: /* No such device */ + case ENOTDIR: /* Not a directory */ + case EISDIR: /* Is a directory */ + case ENFILE: /* File table overflow */ + case EMFILE: /* Too many open files */ + case ENOTTY: /* Not a typewriter */ +#ifndef _WIN32 + case ETXTBSY: /* Text file busy */ +#endif + case ESPIPE: /* Illegal seek */ + case EROFS: /* Read-only file system */ + case EMLINK: /* Too many links */ + case EPIPE: /* Broken pipe */ + + case ERRNO_ERROR_STRING: + default: + status = default_status; + } + + return status; +} + +/* Remove possible quotes, and also the -gzip at the end + * Remove "-gzip" at the end (cf. https://github.comowncloud/client/issues/1195) + * The caller must take ownership of the resulting string. + */ +char *csync_normalize_etag(const char *etag) +{ + int len = 0; + char *buf = NULL; + if (!etag) + return NULL; + + len = strlen(etag); + /* strip "XXXX-gzip" */ + if(len >= 7 && etag[0] == '"' && c_streq(etag + len - 6, "-gzip\"")) { + etag++; + len -= 7; + } + /* strip leading -gzip */ + if(len >= 5 && c_streq(etag + len - 5, "-gzip")) { + len -= 5; + } + /* strip normal quotes */ + if (etag[0] == '"' && etag[len-1] == '"') { + etag++; + len -= 2; + } + + buf = (char*)c_malloc( len+1 ); + strncpy( buf, etag, len ); + buf[len] = '\0'; + return buf; +} + diff --git a/src/csync/csync_private.h b/src/csync/csync_private.h index 2edc26019..3d1632e41 100644 --- a/src/csync/csync_private.h +++ b/src/csync/csync_private.h @@ -163,7 +163,7 @@ struct csync_file_stat_s { size_t pathlen; /* u64 */ uint64_t inode; /* u64 */ mode_t mode; /* u32 */ - unsigned int type : 4; + enum csync_ftw_type_e type : 4; unsigned int child_modified : 1; unsigned int has_ignored_files : 1; /* specify that a directory, or child directory contains ignored files */ diff --git a/src/csync/csync_reconcile.c b/src/csync/csync_reconcile.c deleted file mode 100644 index 59156e800..000000000 --- a/src/csync/csync_reconcile.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config_csync.h" - -#include -#include "csync_private.h" -#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" -#include "csync_log.h" - -#include "inttypes.h" - -/* Check if a file is ignored because one parent is ignored. - * return the node of the ignored directoy if it's the case, or NULL if it is not ignored */ -static c_rbnode_t *_csync_check_ignored(c_rbtree_t *tree, const char *path, int pathlen) { - uint64_t h = 0; - c_rbnode_t *node = NULL; - - /* compute the size of the parent directory */ - int parentlen = pathlen - 1; - while (parentlen > 0 && path[parentlen] != '/') { - parentlen--; - } - if (parentlen <= 0) { - return NULL; - } - - h = c_jhash64((uint8_t *) path, parentlen, 0); - node = c_rbtree_find(tree, &h); - if (node) { - csync_file_stat_t *n = (csync_file_stat_t*)node->data; - if (n->instruction == CSYNC_INSTRUCTION_IGNORE) { - /* Yes, we are ignored */ - return node; - } else { - /* Not ignored */ - return NULL; - } - } else { - /* Try if the parent itself is ignored */ - return _csync_check_ignored(tree, path, parentlen); - } -} - -/* Returns true if we're reasonably certain that hash equality - * for the header means content equality. - * - * Cryptographic safety is not required - this is mainly - * intended to rule out checksums like Adler32 that are not intended for - * hashing and have high likelihood of collision with particular inputs. - */ -static bool _csync_is_collision_safe_hash(const char *checksum_header) -{ - return strncmp(checksum_header, "SHA1:", 5) == 0 - || strncmp(checksum_header, "MD5:", 4) == 0; -} - -/** - * The main function in the reconcile pass. - * - * It's called for each entry in the local and remote rbtrees by - * csync_reconcile() - * - * Before the reconcile phase the trees already know about changes - * relative to the sync journal. This function's job is to spot conflicts - * between local and remote changes and adjust the nodes accordingly. - * - * See doc/dev/sync-algorithm.md for an overview. - * - * - * Older detail comment: - * - * We merge replicas at the file level. The merged replica contains the - * superset of files that are on the local machine and server copies of - * the replica. In the case where the same file is in both the local - * and server copy, the file that was modified most recently is used. - * This means that new files are not deleted, and updated versions of - * existing files are not overwritten. - * - * When a file is updated, the merge algorithm compares the destination - * file with the the source file. If the destination file is newer - * (timestamp is newer), it is not overwritten. If both files, on the - * source and the destination, have been changed, the newer file wins. - */ -static int _csync_merge_algorithm_visitor(void *obj, void *data) { - csync_file_stat_t *cur = NULL; - csync_file_stat_t *other = NULL; - csync_file_stat_t *tmp = NULL; - uint64_t h = 0; - int len = 0; - - CSYNC *ctx = NULL; - c_rbtree_t *tree = NULL; - c_rbnode_t *node = NULL; - - cur = (csync_file_stat_t *) obj; - ctx = (CSYNC *) data; - - /* we need the opposite tree! */ - switch (ctx->current) { - case LOCAL_REPLICA: - tree = ctx->remote.tree; - break; - case REMOTE_REPLICA: - tree = ctx->local.tree; - break; - default: - break; - } - - node = c_rbtree_find(tree, &cur->phash); - - if (!node) { - /* 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); - } - if (!node) { - /* Check if it is ignored */ - node = _csync_check_ignored(tree, cur->path, cur->pathlen); - /* If it is ignored, other->instruction will be IGNORE so this one will also be ignored */ - } - - /* file only found on current replica */ - if (node == NULL) { - switch(cur->instruction) { - /* file has been modified */ - case CSYNC_INSTRUCTION_EVAL: - cur->instruction = CSYNC_INSTRUCTION_NEW; - break; - /* file has been removed on the opposite replica */ - case CSYNC_INSTRUCTION_NONE: - case CSYNC_INSTRUCTION_UPDATE_METADATA: - if (cur->has_ignored_files) { - /* Do not remove a directory that has ignored files */ - break; - } - if (cur->child_modified) { - /* re-create directory that has modified contents */ - cur->instruction = CSYNC_INSTRUCTION_NEW; - break; - } - cur->instruction = CSYNC_INSTRUCTION_REMOVE; - break; - case CSYNC_INSTRUCTION_EVAL_RENAME: - if(ctx->current == LOCAL_REPLICA ) { - /* use the old name to find the "other" node */ - tmp = csync_statedb_get_stat_by_inode(ctx, cur->inode); - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Finding opposite temp through inode %" PRIu64 ": %s", - cur->inode, tmp ? "true":"false"); - } else if( ctx->current == REMOTE_REPLICA ) { - tmp = csync_statedb_get_stat_by_file_id(ctx, cur->file_id); - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Finding opposite temp through file ID %s: %s", - cur->file_id, tmp ? "true":"false"); - } else { - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Unknown replica..."); - } - - if( tmp ) { - len = strlen( tmp->path ); - if( len > 0 ) { - h = c_jhash64((uint8_t *) tmp->path, len, 0); - /* First, check that the file is NOT in our tree (another file with the same name was added) */ - node = c_rbtree_find(ctx->current == REMOTE_REPLICA ? ctx->remote.tree : ctx->local.tree, &h); - if (node) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Origin found in our tree : %s", tmp->path); - } else { - /* Find the temporar file in the other tree. */ - node = c_rbtree_find(tree, &h); - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "PHash of temporary opposite (%s): %" PRIu64 " %s", - tmp->path , h, node ? "found": "not found" ); - if (node) { - other = (csync_file_stat_t*)node->data; - } else { - /* the renamed file could not be found in the opposite tree. That is because it - * is not longer existing there, maybe because it was renamed or deleted. - * The journal is cleaned up later after propagation. - */ - } - } - } - - if(!other) { - cur->instruction = CSYNC_INSTRUCTION_NEW; - } else if (other->instruction == CSYNC_INSTRUCTION_NONE - || other->instruction == CSYNC_INSTRUCTION_UPDATE_METADATA - || cur->type == CSYNC_FTW_TYPE_DIR) { - other->instruction = CSYNC_INSTRUCTION_RENAME; - other->destpath = c_strdup( cur->path ); - if( !c_streq(cur->file_id, "") ) { - csync_vio_set_file_id( other->file_id, cur->file_id ); - } - other->inode = cur->inode; - cur->instruction = CSYNC_INSTRUCTION_NONE; - } else if (other->instruction == CSYNC_INSTRUCTION_REMOVE) { - other->instruction = CSYNC_INSTRUCTION_RENAME; - other->destpath = c_strdup( cur->path ); - - if( !c_streq(cur->file_id, "") ) { - csync_vio_set_file_id( other->file_id, cur->file_id ); - } - other->inode = cur->inode; - cur->instruction = CSYNC_INSTRUCTION_NONE; - } else if (other->instruction == CSYNC_INSTRUCTION_NEW) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "OOOO=> NEW detected in other tree!"); - cur->instruction = CSYNC_INSTRUCTION_CONFLICT; - } else { - assert(other->type != CSYNC_FTW_TYPE_DIR); - cur->instruction = CSYNC_INSTRUCTION_NONE; - other->instruction = CSYNC_INSTRUCTION_SYNC; - } - csync_file_stat_free(tmp); - } - - break; - default: - break; - } - } else { - bool is_conflict = true; - /* - * file found on the other replica - */ - other = (csync_file_stat_t *) node->data; - - switch (cur->instruction) { - case CSYNC_INSTRUCTION_UPDATE_METADATA: - if (other->instruction == CSYNC_INSTRUCTION_UPDATE_METADATA && ctx->current == LOCAL_REPLICA) { - // Remote wins, the SyncEngine will pick relevant local metadata since the remote tree is walked last. - cur->instruction = CSYNC_INSTRUCTION_NONE; - } - break; - case CSYNC_INSTRUCTION_EVAL_RENAME: - /* 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; - /* fall trough */ - /* file on current replica is changed or new */ - case CSYNC_INSTRUCTION_EVAL: - case CSYNC_INSTRUCTION_NEW: - // This operation is usually a no-op and will by default return false - if (csync_file_locked_or_open(ctx->local.uri, cur->path)) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "[Reconciler] IGNORING file %s/%s since it is locked / open", ctx->local.uri, cur->path); - cur->instruction = CSYNC_INSTRUCTION_ERROR; - if (cur->error_status == CSYNC_STATUS_OK) // don't overwrite error - cur->error_status = CYSNC_STATUS_FILE_LOCKED_OR_OPEN; - break; - } else { - //CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "[Reconciler] not ignoring file %s/%s", ctx->local.uri, cur->path); - } - switch (other->instruction) { - /* file on other replica is changed or new */ - case CSYNC_INSTRUCTION_NEW: - case CSYNC_INSTRUCTION_EVAL: - if (other->type == CSYNC_FTW_TYPE_DIR && - cur->type == CSYNC_FTW_TYPE_DIR) { - // Folders of the same path are always considered equals - is_conflict = false; - } else { - // If the size or mtime is different, it's definitely a conflict. - is_conflict = ((other->size != cur->size) || (other->modtime != cur->modtime)); - - // It could be a conflict even if size and mtime match! - // - // In older client versions we always treated these cases as a - // non-conflict. This behavior is preserved in case the server - // doesn't provide a suitable content hash. - // - // When it does have one, however, we do create a job, but the job - // will compare hashes and avoid the download if they are equal. - const char *remoteChecksumHeader = - (ctx->current == REMOTE_REPLICA ? cur->checksumHeader : other->checksumHeader); - if (remoteChecksumHeader) { - is_conflict |= _csync_is_collision_safe_hash(remoteChecksumHeader); - } - - // SO: If there is no checksum, we can have !is_conflict here - // even though the files have different content! This is an - // intentional tradeoff. Downloading and comparing files would - // be technically correct in this situation but leads to too - // much waste. - // In particular this kind of NEW/NEW situation with identical - // sizes and mtimes pops up when the local database is lost for - // whatever reason. - } - if (ctx->current == REMOTE_REPLICA) { - // If the files are considered equal, only update the DB with the etag from remote - cur->instruction = is_conflict ? CSYNC_INSTRUCTION_CONFLICT : CSYNC_INSTRUCTION_UPDATE_METADATA; - other->instruction = CSYNC_INSTRUCTION_NONE; - } else { - cur->instruction = CSYNC_INSTRUCTION_NONE; - other->instruction = is_conflict ? CSYNC_INSTRUCTION_CONFLICT : CSYNC_INSTRUCTION_UPDATE_METADATA; - } - - break; - /* file on the other replica has not been modified */ - case CSYNC_INSTRUCTION_NONE: - case CSYNC_INSTRUCTION_UPDATE_METADATA: - if (cur->type != other->type) { - // If the type of the entity changed, it's like NEW, but - // needs to delete the other entity first. - cur->instruction = CSYNC_INSTRUCTION_TYPE_CHANGE; - other->instruction = CSYNC_INSTRUCTION_NONE; - } else if (cur->type == CSYNC_FTW_TYPE_DIR) { - cur->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; - other->instruction = CSYNC_INSTRUCTION_NONE; - } else { - cur->instruction = CSYNC_INSTRUCTION_SYNC; - other->instruction = CSYNC_INSTRUCTION_NONE; - } - break; - case CSYNC_INSTRUCTION_IGNORE: - cur->instruction = CSYNC_INSTRUCTION_IGNORE; - break; - default: - break; - } - default: - break; - } - } - - //hide instruction NONE messages when log level is set to debug, - //only show these messages on log level trace - const char *repo = ctx->current == REMOTE_REPLICA ? "server" : "client"; - if(cur->instruction ==CSYNC_INSTRUCTION_NONE) - { - if(cur->type == CSYNC_FTW_TYPE_DIR) - { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, - "%-30s %s dir: %s", - csync_instruction_str(cur->instruction), - repo, - cur->path); - } - else - { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, - "%-30s %s file: %s", - csync_instruction_str(cur->instruction), - repo, - cur->path); - } - } - else - { - if(cur->type == CSYNC_FTW_TYPE_DIR) - { - CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, - "%-30s %s dir: %s", - csync_instruction_str(cur->instruction), - repo, - cur->path); - } - else - { - CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, - "%-30s %s file: %s", - csync_instruction_str(cur->instruction), - repo, - cur->path); - } - } - - return 0; -} - -int csync_reconcile_updates(CSYNC *ctx) { - int rc; - 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; - } - - rc = c_rbtree_walk(tree, (void *) ctx, _csync_merge_algorithm_visitor); - if( rc < 0 ) { - ctx->status_code = CSYNC_STATUS_RECONCILE_ERROR; - } - return rc; -} - -/* vim: set ts=8 sw=2 et cindent: */ diff --git a/src/csync/csync_reconcile.cpp b/src/csync/csync_reconcile.cpp new file mode 100644 index 000000000..3b02398fc --- /dev/null +++ b/src/csync/csync_reconcile.cpp @@ -0,0 +1,420 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_csync.h" + +#include +#include "csync_private.h" +#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" +#include "csync_log.h" + +// Needed for PRIu64 on MinGW in C++ mode. +#define __STDC_FORMAT_MACROS +#include "inttypes.h" + +/* Check if a file is ignored because one parent is ignored. + * return the node of the ignored directoy if it's the case, or NULL if it is not ignored */ +static c_rbnode_t *_csync_check_ignored(c_rbtree_t *tree, const char *path, int pathlen) { + uint64_t h = 0; + c_rbnode_t *node = NULL; + + /* compute the size of the parent directory */ + int parentlen = pathlen - 1; + while (parentlen > 0 && path[parentlen] != '/') { + parentlen--; + } + if (parentlen <= 0) { + return NULL; + } + + h = c_jhash64((uint8_t *) path, parentlen, 0); + node = c_rbtree_find(tree, &h); + if (node) { + csync_file_stat_t *n = (csync_file_stat_t*)node->data; + if (n->instruction == CSYNC_INSTRUCTION_IGNORE) { + /* Yes, we are ignored */ + return node; + } else { + /* Not ignored */ + return NULL; + } + } else { + /* Try if the parent itself is ignored */ + return _csync_check_ignored(tree, path, parentlen); + } +} + +/* Returns true if we're reasonably certain that hash equality + * for the header means content equality. + * + * Cryptographic safety is not required - this is mainly + * intended to rule out checksums like Adler32 that are not intended for + * hashing and have high likelihood of collision with particular inputs. + */ +static bool _csync_is_collision_safe_hash(const char *checksum_header) +{ + return strncmp(checksum_header, "SHA1:", 5) == 0 + || strncmp(checksum_header, "MD5:", 4) == 0; +} + +/** + * The main function in the reconcile pass. + * + * It's called for each entry in the local and remote rbtrees by + * csync_reconcile() + * + * Before the reconcile phase the trees already know about changes + * relative to the sync journal. This function's job is to spot conflicts + * between local and remote changes and adjust the nodes accordingly. + * + * See doc/dev/sync-algorithm.md for an overview. + * + * + * Older detail comment: + * + * We merge replicas at the file level. The merged replica contains the + * superset of files that are on the local machine and server copies of + * the replica. In the case where the same file is in both the local + * and server copy, the file that was modified most recently is used. + * This means that new files are not deleted, and updated versions of + * existing files are not overwritten. + * + * When a file is updated, the merge algorithm compares the destination + * file with the the source file. If the destination file is newer + * (timestamp is newer), it is not overwritten. If both files, on the + * source and the destination, have been changed, the newer file wins. + */ +static int _csync_merge_algorithm_visitor(void *obj, void *data) { + csync_file_stat_t *cur = NULL; + csync_file_stat_t *other = NULL; + csync_file_stat_t *tmp = NULL; + uint64_t h = 0; + int len = 0; + + CSYNC *ctx = NULL; + c_rbtree_t *tree = NULL; + c_rbnode_t *node = NULL; + + cur = (csync_file_stat_t *) obj; + ctx = (CSYNC *) data; + + /* we need the opposite tree! */ + switch (ctx->current) { + case LOCAL_REPLICA: + tree = ctx->remote.tree; + break; + case REMOTE_REPLICA: + tree = ctx->local.tree; + break; + default: + break; + } + + node = c_rbtree_find(tree, &cur->phash); + + if (!node) { + /* 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); + } + if (!node) { + /* Check if it is ignored */ + node = _csync_check_ignored(tree, cur->path, cur->pathlen); + /* If it is ignored, other->instruction will be IGNORE so this one will also be ignored */ + } + + /* file only found on current replica */ + if (node == NULL) { + switch(cur->instruction) { + /* file has been modified */ + case CSYNC_INSTRUCTION_EVAL: + cur->instruction = CSYNC_INSTRUCTION_NEW; + break; + /* file has been removed on the opposite replica */ + case CSYNC_INSTRUCTION_NONE: + case CSYNC_INSTRUCTION_UPDATE_METADATA: + if (cur->has_ignored_files) { + /* Do not remove a directory that has ignored files */ + break; + } + if (cur->child_modified) { + /* re-create directory that has modified contents */ + cur->instruction = CSYNC_INSTRUCTION_NEW; + break; + } + cur->instruction = CSYNC_INSTRUCTION_REMOVE; + break; + case CSYNC_INSTRUCTION_EVAL_RENAME: + if(ctx->current == LOCAL_REPLICA ) { + /* use the old name to find the "other" node */ + tmp = csync_statedb_get_stat_by_inode(ctx, cur->inode); + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Finding opposite temp through inode %" PRIu64 ": %s", + cur->inode, tmp ? "true":"false"); + } else if( ctx->current == REMOTE_REPLICA ) { + tmp = csync_statedb_get_stat_by_file_id(ctx, cur->file_id); + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Finding opposite temp through file ID %s: %s", + cur->file_id, tmp ? "true":"false"); + } else { + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Unknown replica..."); + } + + if( tmp ) { + len = strlen( tmp->path ); + if( len > 0 ) { + h = c_jhash64((uint8_t *) tmp->path, len, 0); + /* First, check that the file is NOT in our tree (another file with the same name was added) */ + node = c_rbtree_find(ctx->current == REMOTE_REPLICA ? ctx->remote.tree : ctx->local.tree, &h); + if (node) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Origin found in our tree : %s", tmp->path); + } else { + /* Find the temporar file in the other tree. */ + node = c_rbtree_find(tree, &h); + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "PHash of temporary opposite (%s): %" PRIu64 " %s", + tmp->path , h, node ? "found": "not found" ); + if (node) { + other = (csync_file_stat_t*)node->data; + } else { + /* the renamed file could not be found in the opposite tree. That is because it + * is not longer existing there, maybe because it was renamed or deleted. + * The journal is cleaned up later after propagation. + */ + } + } + } + + if(!other) { + cur->instruction = CSYNC_INSTRUCTION_NEW; + } else if (other->instruction == CSYNC_INSTRUCTION_NONE + || other->instruction == CSYNC_INSTRUCTION_UPDATE_METADATA + || cur->type == CSYNC_FTW_TYPE_DIR) { + other->instruction = CSYNC_INSTRUCTION_RENAME; + other->destpath = c_strdup( cur->path ); + if( !c_streq(cur->file_id, "") ) { + csync_vio_set_file_id( other->file_id, cur->file_id ); + } + other->inode = cur->inode; + cur->instruction = CSYNC_INSTRUCTION_NONE; + } else if (other->instruction == CSYNC_INSTRUCTION_REMOVE) { + other->instruction = CSYNC_INSTRUCTION_RENAME; + other->destpath = c_strdup( cur->path ); + + if( !c_streq(cur->file_id, "") ) { + csync_vio_set_file_id( other->file_id, cur->file_id ); + } + other->inode = cur->inode; + cur->instruction = CSYNC_INSTRUCTION_NONE; + } else if (other->instruction == CSYNC_INSTRUCTION_NEW) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "OOOO=> NEW detected in other tree!"); + cur->instruction = CSYNC_INSTRUCTION_CONFLICT; + } else { + assert(other->type != CSYNC_FTW_TYPE_DIR); + cur->instruction = CSYNC_INSTRUCTION_NONE; + other->instruction = CSYNC_INSTRUCTION_SYNC; + } + csync_file_stat_free(tmp); + } + + break; + default: + break; + } + } else { + bool is_conflict = true; + /* + * file found on the other replica + */ + other = (csync_file_stat_t *) node->data; + + switch (cur->instruction) { + case CSYNC_INSTRUCTION_UPDATE_METADATA: + if (other->instruction == CSYNC_INSTRUCTION_UPDATE_METADATA && ctx->current == LOCAL_REPLICA) { + // Remote wins, the SyncEngine will pick relevant local metadata since the remote tree is walked last. + cur->instruction = CSYNC_INSTRUCTION_NONE; + } + break; + case CSYNC_INSTRUCTION_EVAL_RENAME: + /* 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; + /* fall trough */ + /* file on current replica is changed or new */ + case CSYNC_INSTRUCTION_EVAL: + case CSYNC_INSTRUCTION_NEW: + // This operation is usually a no-op and will by default return false + if (csync_file_locked_or_open(ctx->local.uri, cur->path)) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "[Reconciler] IGNORING file %s/%s since it is locked / open", ctx->local.uri, cur->path); + cur->instruction = CSYNC_INSTRUCTION_ERROR; + if (cur->error_status == CSYNC_STATUS_OK) // don't overwrite error + cur->error_status = CYSNC_STATUS_FILE_LOCKED_OR_OPEN; + break; + } else { + //CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "[Reconciler] not ignoring file %s/%s", ctx->local.uri, cur->path); + } + switch (other->instruction) { + /* file on other replica is changed or new */ + case CSYNC_INSTRUCTION_NEW: + case CSYNC_INSTRUCTION_EVAL: + if (other->type == CSYNC_FTW_TYPE_DIR && + cur->type == CSYNC_FTW_TYPE_DIR) { + // Folders of the same path are always considered equals + is_conflict = false; + } else { + // If the size or mtime is different, it's definitely a conflict. + is_conflict = ((other->size != cur->size) || (other->modtime != cur->modtime)); + + // It could be a conflict even if size and mtime match! + // + // In older client versions we always treated these cases as a + // non-conflict. This behavior is preserved in case the server + // doesn't provide a suitable content hash. + // + // When it does have one, however, we do create a job, but the job + // will compare hashes and avoid the download if they are equal. + const char *remoteChecksumHeader = + (ctx->current == REMOTE_REPLICA ? cur->checksumHeader : other->checksumHeader); + if (remoteChecksumHeader) { + is_conflict |= _csync_is_collision_safe_hash(remoteChecksumHeader); + } + + // SO: If there is no checksum, we can have !is_conflict here + // even though the files have different content! This is an + // intentional tradeoff. Downloading and comparing files would + // be technically correct in this situation but leads to too + // much waste. + // In particular this kind of NEW/NEW situation with identical + // sizes and mtimes pops up when the local database is lost for + // whatever reason. + } + if (ctx->current == REMOTE_REPLICA) { + // If the files are considered equal, only update the DB with the etag from remote + cur->instruction = is_conflict ? CSYNC_INSTRUCTION_CONFLICT : CSYNC_INSTRUCTION_UPDATE_METADATA; + other->instruction = CSYNC_INSTRUCTION_NONE; + } else { + cur->instruction = CSYNC_INSTRUCTION_NONE; + other->instruction = is_conflict ? CSYNC_INSTRUCTION_CONFLICT : CSYNC_INSTRUCTION_UPDATE_METADATA; + } + + break; + /* file on the other replica has not been modified */ + case CSYNC_INSTRUCTION_NONE: + case CSYNC_INSTRUCTION_UPDATE_METADATA: + if (cur->type != other->type) { + // If the type of the entity changed, it's like NEW, but + // needs to delete the other entity first. + cur->instruction = CSYNC_INSTRUCTION_TYPE_CHANGE; + other->instruction = CSYNC_INSTRUCTION_NONE; + } else if (cur->type == CSYNC_FTW_TYPE_DIR) { + cur->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; + other->instruction = CSYNC_INSTRUCTION_NONE; + } else { + cur->instruction = CSYNC_INSTRUCTION_SYNC; + other->instruction = CSYNC_INSTRUCTION_NONE; + } + break; + case CSYNC_INSTRUCTION_IGNORE: + cur->instruction = CSYNC_INSTRUCTION_IGNORE; + break; + default: + break; + } + default: + break; + } + } + + //hide instruction NONE messages when log level is set to debug, + //only show these messages on log level trace + const char *repo = ctx->current == REMOTE_REPLICA ? "server" : "client"; + if(cur->instruction ==CSYNC_INSTRUCTION_NONE) + { + if(cur->type == CSYNC_FTW_TYPE_DIR) + { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, + "%-30s %s dir: %s", + csync_instruction_str(cur->instruction), + repo, + cur->path); + } + else + { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, + "%-30s %s file: %s", + csync_instruction_str(cur->instruction), + repo, + cur->path); + } + } + else + { + if(cur->type == CSYNC_FTW_TYPE_DIR) + { + CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, + "%-30s %s dir: %s", + csync_instruction_str(cur->instruction), + repo, + cur->path); + } + else + { + CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, + "%-30s %s file: %s", + csync_instruction_str(cur->instruction), + repo, + cur->path); + } + } + + return 0; +} + +int csync_reconcile_updates(CSYNC *ctx) { + int rc; + 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; + } + + rc = c_rbtree_walk(tree, (void *) ctx, _csync_merge_algorithm_visitor); + if( rc < 0 ) { + ctx->status_code = CSYNC_STATUS_RECONCILE_ERROR; + } + return rc; +} + +/* vim: set ts=8 sw=2 et cindent: */ diff --git a/src/csync/csync_rename.cpp b/src/csync/csync_rename.cpp index 5cc43ce22..79bb78e34 100644 --- a/src/csync/csync_rename.cpp +++ b/src/csync/csync_rename.cpp @@ -18,10 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -extern "C" { #include "csync_private.h" #include "csync_rename.h" -} #include #include @@ -55,7 +53,6 @@ struct csync_rename_s { std::vector todo; }; -extern "C" { void csync_rename_destroy(CSYNC* ctx) { delete reinterpret_cast(ctx->rename_info); @@ -98,5 +95,3 @@ bool csync_rename_count(CSYNC *ctx) { csync_rename_s* d = csync_rename_s::get(ctx); return d->folder_renamed_from.size(); } - -} diff --git a/src/csync/csync_rename.h b/src/csync/csync_rename.h index 53968324c..7702a1376 100644 --- a/src/csync/csync_rename.h +++ b/src/csync/csync_rename.h @@ -22,10 +22,6 @@ #include "csync.h" -#ifdef __cplusplus -extern "C" { -#endif - /* Return the final destination path of a given patch in case of renames */ char OCSYNC_EXPORT *csync_rename_adjust_path(CSYNC *ctx, const char *path); /* Return the source of a given path in case of renames */ @@ -34,7 +30,3 @@ void OCSYNC_EXPORT csync_rename_destroy(CSYNC *ctx); void OCSYNC_EXPORT csync_rename_record(CSYNC *ctx, const char *from, const char *to); /* Return the amount of renamed item recorded */ bool OCSYNC_EXPORT csync_rename_count(CSYNC *ctx); - -#ifdef __cplusplus -} -#endif diff --git a/src/csync/csync_statedb.c b/src/csync/csync_statedb.c deleted file mode 100644 index 056ca48dd..000000000 --- a/src/csync/csync_statedb.c +++ /dev/null @@ -1,656 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * Copyright (c) 2012-2013 by Klaas Freitag - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config_csync.h" - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "c_lib.h" -#include "csync_private.h" -#include "csync_statedb.h" -#include "csync_util.h" -#include "csync_misc.h" -#include "csync_exclude.h" - -#include "c_string.h" -#include "c_jhash.h" -#include "csync_time.h" - -#define CSYNC_LOG_CATEGORY_NAME "csync.statedb" -#include "csync_log.h" -#include "csync_rename.h" - -#define BUF_SIZE 16 - -#define sqlite_open(A, B) sqlite3_open_v2(A,B, SQLITE_OPEN_READONLY+SQLITE_OPEN_NOMUTEX, NULL) - -#define SQLTM_TIME 150 -#define SQLTM_COUNT 10 - -#define SQLITE_BUSY_HANDLED(F) if(1) { \ - int n = 0; \ - do { rc = F ; \ - if( (rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED) ) { \ - n++; \ - csync_sleep(SQLTM_TIME); \ - } \ - }while( (n < SQLTM_COUNT) && ((rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED))); \ - } - - -void csync_set_statedb_exists(CSYNC *ctx, int val) { - ctx->statedb.exists = val; -} - -int csync_get_statedb_exists(CSYNC *ctx) { - return ctx->statedb.exists; -} - -static int _csync_check_db_integrity(sqlite3 *db) { - c_strlist_t *result = NULL; - int rc = -1; - - result = csync_statedb_query(db, "PRAGMA quick_check;"); - if (result != NULL) { - /* There is a result */ - if (result->count > 0) { - if (c_streq(result->vector[0], "ok")) { - rc = 0; - } - } - c_strlist_destroy(result); - } - - if( sqlite3_threadsafe() == 0 ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "* WARNING: SQLite module is not threadsafe!"); - rc = -1; - } - - return rc; -} - -static int _csync_statedb_is_empty(sqlite3 *db) { - c_strlist_t *result = NULL; - int rc = 0; - - result = csync_statedb_query(db, "SELECT COUNT(phash) FROM metadata LIMIT 1 OFFSET 0;"); - if (result == NULL) { - rc = 1; - } - c_strlist_destroy(result); - - return rc; -} - -#ifndef NDEBUG -static void sqlite_profile( void *x, const char* sql, sqlite3_uint64 time) -{ - (void)x; - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, - "_SQL_ %s: %llu", sql, time); - -} -#endif - -int csync_statedb_load(CSYNC *ctx, const char *statedb, sqlite3 **pdb) { - int rc = -1; - c_strlist_t *result = NULL; - sqlite3 *db = NULL; - - if( !ctx ) { - return -1; - } - - if (ctx->statedb.db) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: DB already open"); - ctx->status_code = CSYNC_STATUS_PARAM_ERROR; - return -1; - } - - ctx->statedb.lastReturnValue = SQLITE_OK; - - /* Openthe database */ - if (sqlite_open(statedb, &db) != SQLITE_OK) { - const char *errmsg= sqlite3_errmsg(ctx->statedb.db); - CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: Failed to sqlite3 open statedb - bail out: %s.", - errmsg ? errmsg : ""); - - rc = -1; - ctx->status_code = CSYNC_STATUS_STATEDB_LOAD_ERROR; - goto out; - } - - if (_csync_check_db_integrity(db) != 0) { - const char *errmsg= sqlite3_errmsg(db); - CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: sqlite3 integrity check failed - bail out: %s.", - errmsg ? errmsg : ""); - rc = -1; - ctx->status_code = CSYNC_STATUS_STATEDB_CORRUPTED; - goto out; - } - - if (_csync_statedb_is_empty(db)) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "statedb contents doesn't exist"); - csync_set_statedb_exists(ctx, 0); - } else { - csync_set_statedb_exists(ctx, 1); - } - - /* Print out the version */ - // - result = csync_statedb_query(db, "SELECT sqlite_version();"); - if (result && result->count >= 1) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "sqlite3 version \"%s\"", *result->vector); - } - c_strlist_destroy(result); - - /* optimization for speeding up SQLite */ - result = csync_statedb_query(db, "PRAGMA synchronous = NORMAL;"); - c_strlist_destroy(result); - result = csync_statedb_query(db, "PRAGMA case_sensitive_like = ON;"); - c_strlist_destroy(result); - - /* set a busy handler with 5 seconds timeout */ - sqlite3_busy_timeout(db, 5000); - -#ifndef NDEBUG - sqlite3_profile(db, sqlite_profile, 0 ); -#endif - *pdb = db; - - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Success"); - - return 0; -out: - sqlite3_close(db); - return rc; -} - -int csync_statedb_close(CSYNC *ctx) { - int rc = 0; - - if (!ctx) { - return -1; - } - - /* deallocate query resources */ - if( ctx->statedb.by_fileid_stmt ) { - sqlite3_finalize(ctx->statedb.by_fileid_stmt); - ctx->statedb.by_fileid_stmt = NULL; - } - if( ctx->statedb.by_hash_stmt ) { - sqlite3_finalize(ctx->statedb.by_hash_stmt); - ctx->statedb.by_hash_stmt = NULL; - } - if( ctx->statedb.by_inode_stmt) { - sqlite3_finalize(ctx->statedb.by_inode_stmt); - ctx->statedb.by_inode_stmt = NULL; - } - - ctx->statedb.lastReturnValue = SQLITE_OK; - - int sr = sqlite3_close(ctx->statedb.db); - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "sqlite3_close=%d", sr); - - ctx->statedb.db = 0; - - return rc; -} - -#define METADATA_QUERY \ - "phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, " \ - "filesize, ignoredChildrenRemote, " \ - "contentchecksumtype.name || ':' || contentChecksum " \ - "FROM metadata " \ - "LEFT JOIN checksumtype as contentchecksumtype ON metadata.contentChecksumTypeId == contentchecksumtype.id" - -// This funciton parses a line from the metadata table into the given csync_file_stat -// structure which it is also allocating. -// Note that this function calls laso sqlite3_step to actually get the info from db and -// returns the sqlite return type. -static int _csync_file_stat_from_metadata_table( csync_file_stat_t **st, sqlite3_stmt *stmt ) -{ - int rc = SQLITE_ERROR; - int column_count; - int len; - - if( ! stmt ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Fatal: Statement is NULL."); - return SQLITE_ERROR; - } - - column_count = sqlite3_column_count(stmt); - - SQLITE_BUSY_HANDLED( sqlite3_step(stmt) ); - - if( rc == SQLITE_ROW ) { - if(column_count > 7) { - const char *name; - - /* phash, pathlen, path, inode, uid, gid, mode, modtime */ - len = sqlite3_column_int(stmt, 1); - *st = c_malloc(sizeof(csync_file_stat_t) + len + 1); - /* clear the whole structure */ - ZERO_STRUCTP(*st); - - /* The query suceeded so use the phash we pass to the function. */ - (*st)->phash = sqlite3_column_int64(stmt, 0); - - (*st)->pathlen = sqlite3_column_int(stmt, 1); - name = (const char*) sqlite3_column_text(stmt, 2); - memcpy((*st)->path, (len ? name : ""), len + 1); - (*st)->inode = sqlite3_column_int64(stmt,3); - (*st)->mode = sqlite3_column_int(stmt, 6); - (*st)->modtime = strtoul((char*)sqlite3_column_text(stmt, 7), NULL, 10); - - if(*st && column_count > 8 ) { - (*st)->type = sqlite3_column_int(stmt, 8); - } - - if(column_count > 9 && sqlite3_column_text(stmt, 9)) { - (*st)->etag = c_strdup( (char*) sqlite3_column_text(stmt, 9) ); - } - if(column_count > 10 && sqlite3_column_text(stmt,10)) { - csync_vio_set_file_id((*st)->file_id, (char*) sqlite3_column_text(stmt, 10)); - } - if(column_count > 11 && sqlite3_column_text(stmt,11)) { - strncpy((*st)->remotePerm, - (char*) sqlite3_column_text(stmt, 11), - REMOTE_PERM_BUF_SIZE); - } - if(column_count > 12 && sqlite3_column_int64(stmt,12)) { - (*st)->size = sqlite3_column_int64(stmt, 12); - } - if(column_count > 13) { - (*st)->has_ignored_files = sqlite3_column_int(stmt, 13); - } - if (column_count > 14 && sqlite3_column_text(stmt, 14)) { - (*st)->checksumHeader = c_strdup((char *)sqlite3_column_text(stmt, 14)); - } - - } - } else { - if( rc != SQLITE_DONE ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Query results in %d", rc); - } - } - return rc; -} - -/* caller must free the memory */ -csync_file_stat_t *csync_statedb_get_stat_by_hash(CSYNC *ctx, - uint64_t phash) -{ - csync_file_stat_t *st = NULL; - int rc; - - if( !ctx || ctx->db_is_empty ) { - return NULL; - } - - if( ctx->statedb.by_hash_stmt == NULL ) { - const char *hash_query = "SELECT " METADATA_QUERY " WHERE phash=?1"; - - SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, hash_query, strlen(hash_query), &ctx->statedb.by_hash_stmt, NULL)); - ctx->statedb.lastReturnValue = rc; - if( rc != SQLITE_OK ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for hash query."); - return NULL; - } - } - - if( ctx->statedb.by_hash_stmt == NULL ) { - return NULL; - } - - sqlite3_bind_int64(ctx->statedb.by_hash_stmt, 1, (long long signed int)phash); - - rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_hash_stmt); - ctx->statedb.lastReturnValue = rc; - if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata: %d!", rc); - } - sqlite3_reset(ctx->statedb.by_hash_stmt); - - return st; -} - -csync_file_stat_t *csync_statedb_get_stat_by_file_id(CSYNC *ctx, - const char *file_id ) { - csync_file_stat_t *st = NULL; - int rc = 0; - - if (!file_id) { - return 0; - } - if (c_streq(file_id, "")) { - return 0; - } - - if( !ctx || ctx->db_is_empty ) { - return NULL; - } - - if( ctx->statedb.by_fileid_stmt == NULL ) { - const char *query = "SELECT " METADATA_QUERY " WHERE fileid=?1"; - - SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, query, strlen(query), &ctx->statedb.by_fileid_stmt, NULL)); - ctx->statedb.lastReturnValue = rc; - if( rc != SQLITE_OK ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for file id query."); - return NULL; - } - } - - /* bind the query value */ - sqlite3_bind_text(ctx->statedb.by_fileid_stmt, 1, file_id, -1, SQLITE_STATIC); - - rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_fileid_stmt); - ctx->statedb.lastReturnValue = rc; - if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata: %d!", rc); - } - // clear the resources used by the statement. - sqlite3_reset(ctx->statedb.by_fileid_stmt); - - return st; -} - -/* caller must free the memory */ -csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx, - uint64_t inode) -{ - csync_file_stat_t *st = NULL; - int rc; - - if (!inode) { - return NULL; - } - - if( !ctx || ctx->db_is_empty ) { - return NULL; - } - - if( ctx->statedb.by_inode_stmt == NULL ) { - const char *inode_query = "SELECT " METADATA_QUERY " WHERE inode=?1"; - - SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, inode_query, strlen(inode_query), &ctx->statedb.by_inode_stmt, NULL)); - ctx->statedb.lastReturnValue = rc; - if( rc != SQLITE_OK ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for inode query."); - return NULL; - } - } - - if( ctx->statedb.by_inode_stmt == NULL ) { - return NULL; - } - - sqlite3_bind_int64(ctx->statedb.by_inode_stmt, 1, (long long signed int)inode); - - rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_inode_stmt); - ctx->statedb.lastReturnValue = rc; - if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata by inode: %d!", rc); - } - sqlite3_reset(ctx->statedb.by_inode_stmt); - - return st; -} - -int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) { - int rc; - sqlite3_stmt *stmt = NULL; - int64_t cnt = 0; - - if( !path ) { - return -1; - } - - if( !ctx || ctx->db_is_empty ) { - return -1; - } - - /* Select the entries for anything that starts with (path+'/') - * In other words, anything that is between path+'/' and path+'0', - * (because '0' follows '/' in ascii) - */ - const char *below_path_query = "SELECT " METADATA_QUERY " WHERE path > (?||'/') AND path < (?||'0') ORDER BY path||'/' ASC"; - SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, below_path_query, -1, &stmt, NULL)); - ctx->statedb.lastReturnValue = rc; - if( rc != SQLITE_OK ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for below path query."); - return -1; - } - - if (stmt == NULL) { - return -1; - } - - sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 2, path, -1, SQLITE_STATIC); - - cnt = 0; - - ctx->statedb.lastReturnValue = rc; - do { - csync_file_stat_t *st = NULL; - - rc = _csync_file_stat_from_metadata_table( &st, stmt); - if( st ) { - /* When selective sync is used, the database may have subtrees with a parent - * whose etag (md5) is _invalid_. These are ignored and shall not appear in the - * remote tree. - * Sometimes folders that are not ignored by selective sync get marked as - * _invalid_, but that is not a problem as the next discovery will retrieve - * their correct etags again and we don't run into this case. - */ - if( c_streq(st->etag, "_invalid_") ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s selective sync excluded", st->path); - char *skipbase = c_strdup(st->path); - skipbase[st->pathlen] = '/'; - int skiplen = st->pathlen + 1; - - /* Skip over all entries with the same base path. Note that this depends - * strongly on the ordering of the retrieved items. */ - do { - csync_file_stat_free(st); - rc = _csync_file_stat_from_metadata_table( &st, stmt); - if( st && strncmp(st->path, skipbase, skiplen) != 0 ) { - break; - } - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s selective sync excluded because the parent is", st->path); - } while( rc == SQLITE_ROW ); - - /* End of data? */ - if( rc != SQLITE_ROW || !st ) { - continue; - } - } - - /* Check for exclusion from the tree. - * Note that this is only a safety net in case the ignore list changes - * without a full remote discovery being triggered. */ - CSYNC_EXCLUDE_TYPE excluded = csync_excluded_traversal(ctx->excludes, st->path, st->type); - if (excluded != CSYNC_NOT_EXCLUDED) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s excluded (%d)", st->path, excluded); - - if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE - || excluded == CSYNC_FILE_SILENTLY_EXCLUDED) { - csync_file_stat_free(st); - continue; - } - - st->instruction = CSYNC_INSTRUCTION_IGNORE; - } - - /* store into result list. */ - if (c_rbtree_insert(ctx->remote.tree, (void *) st) < 0) { - csync_file_stat_free(st); - ctx->status_code = CSYNC_STATUS_TREE_ERROR; - break; - } - cnt++; - } - } while( rc == SQLITE_ROW ); - - ctx->statedb.lastReturnValue = rc; - if( rc != SQLITE_DONE ) { - ctx->status_code = CSYNC_STATUS_TREE_ERROR; - } else { - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "%" PRId64 " entries read below path %s from db.", cnt, path); - } - sqlite3_finalize(stmt); - - return 0; -} - -/* query the statedb, caller must free the memory */ -c_strlist_t *csync_statedb_query(sqlite3 *db, - const char *statement) { - int err = SQLITE_OK; - int rc = SQLITE_OK; - size_t i = 0; - size_t busy_count = 0; - size_t retry_count = 0; - size_t column_count = 0; - sqlite3_stmt *stmt; - const char *tail = NULL; - const char *field = NULL; - c_strlist_t *result = NULL; - int row = 0; - - do { - /* compile SQL program into a virtual machine, reattempteing if busy */ - do { - if (busy_count) { - csync_sleep(100); - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "sqlite3_prepare: BUSY counter: %zu", busy_count); - } - err = sqlite3_prepare(db, statement, -1, &stmt, &tail); - } while (err == SQLITE_BUSY && busy_count ++ < 120); - - if (err != SQLITE_OK) { - if (err == SQLITE_BUSY) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Gave up waiting for lock to clear"); - } - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, - "sqlite3_compile error: %s - on query %s", - sqlite3_errmsg(db), statement); - break; - } else { - busy_count = 0; - column_count = sqlite3_column_count(stmt); - - /* execute virtual machine by iterating over rows */ - for(;;) { - err = sqlite3_step(stmt); - - if (err == SQLITE_BUSY) { - if (busy_count++ > 120) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Busy counter has reached its maximum. Aborting this sql statement"); - break; - } - csync_sleep(100); - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "sqlite3_step: BUSY counter: %zu", busy_count); - continue; - } - - if (err == SQLITE_MISUSE) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "sqlite3_step: MISUSE!!"); - } - - if (err == SQLITE_DONE) { - if (result == NULL) { - result = c_strlist_new(1); - } - break; - } - - if (err == SQLITE_ERROR) { - break; - } - - row++; - if( result ) { - result = c_strlist_expand(result, row*column_count); - } else { - result = c_strlist_new(column_count); - } - - if (result == NULL) { - return NULL; - } - - /* iterate over columns */ - for (i = 0; i < column_count; i++) { - field = (const char *) sqlite3_column_text(stmt, i); - if (!field) - field = ""; - // CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "sqlite3_column_text: %s", field); - if (c_strlist_add(result, field) < 0) { - c_strlist_destroy(result); - return NULL; - } - } - } /* end infinite for loop */ - - /* deallocate vm resources */ - rc = sqlite3_finalize(stmt); - - if (err != SQLITE_DONE && rc != SQLITE_SCHEMA) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "sqlite_step error: %s - on query: %s", sqlite3_errmsg(db), statement); - if (result != NULL) { - c_strlist_destroy(result); - } - return NULL; - } - - if (rc == SQLITE_SCHEMA) { - retry_count ++; - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "SQLITE_SCHEMA error occurred on query: %s", statement); - if (retry_count < 10) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Retrying now."); - } else { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "RETRY count has reached its maximum. Aborting statement: %s", statement); - if (result != NULL) { - c_strlist_destroy(result); - } - result = c_strlist_new(1); - } - } - } - } while (rc == SQLITE_SCHEMA && retry_count < 10); - - return result; -} - -/* vim: set ts=8 sw=2 et cindent: */ diff --git a/src/csync/csync_statedb.cpp b/src/csync/csync_statedb.cpp new file mode 100644 index 000000000..63eb813e7 --- /dev/null +++ b/src/csync/csync_statedb.cpp @@ -0,0 +1,659 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * Copyright (c) 2012-2013 by Klaas Freitag + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_csync.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include + +#include "c_lib.h" +#include "csync_private.h" +#include "csync_statedb.h" +#include "csync_util.h" +#include "csync_misc.h" +#include "csync_exclude.h" + +#include "c_string.h" +#include "c_jhash.h" +#include "csync_time.h" + +#define CSYNC_LOG_CATEGORY_NAME "csync.statedb" +#include "csync_log.h" +#include "csync_rename.h" + +// Needed for PRIu64 on MinGW in C++ mode. +#define __STDC_FORMAT_MACROS +#include + +#define BUF_SIZE 16 + +#define sqlite_open(A, B) sqlite3_open_v2(A,B, SQLITE_OPEN_READONLY+SQLITE_OPEN_NOMUTEX, NULL) + +#define SQLTM_TIME 150 +#define SQLTM_COUNT 10 + +#define SQLITE_BUSY_HANDLED(F) if(1) { \ + int n = 0; \ + do { rc = F ; \ + if( (rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED) ) { \ + n++; \ + csync_sleep(SQLTM_TIME); \ + } \ + }while( (n < SQLTM_COUNT) && ((rc == SQLITE_BUSY) || (rc == SQLITE_LOCKED))); \ + } + + +void csync_set_statedb_exists(CSYNC *ctx, int val) { + ctx->statedb.exists = val; +} + +int csync_get_statedb_exists(CSYNC *ctx) { + return ctx->statedb.exists; +} + +static int _csync_check_db_integrity(sqlite3 *db) { + c_strlist_t *result = NULL; + int rc = -1; + + result = csync_statedb_query(db, "PRAGMA quick_check;"); + if (result != NULL) { + /* There is a result */ + if (result->count > 0) { + if (c_streq(result->vector[0], "ok")) { + rc = 0; + } + } + c_strlist_destroy(result); + } + + if( sqlite3_threadsafe() == 0 ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "* WARNING: SQLite module is not threadsafe!"); + rc = -1; + } + + return rc; +} + +static int _csync_statedb_is_empty(sqlite3 *db) { + c_strlist_t *result = NULL; + int rc = 0; + + result = csync_statedb_query(db, "SELECT COUNT(phash) FROM metadata LIMIT 1 OFFSET 0;"); + if (result == NULL) { + rc = 1; + } + c_strlist_destroy(result); + + return rc; +} + +#ifndef NDEBUG +static void sqlite_profile( void *x, const char* sql, sqlite3_uint64 time) +{ + (void)x; + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, + "_SQL_ %s: %llu", sql, time); + +} +#endif + +int csync_statedb_load(CSYNC *ctx, const char *statedb, sqlite3 **pdb) { + int rc = -1; + c_strlist_t *result = NULL; + sqlite3 *db = NULL; + + if( !ctx ) { + return -1; + } + + if (ctx->statedb.db) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: DB already open"); + ctx->status_code = CSYNC_STATUS_PARAM_ERROR; + return -1; + } + + ctx->statedb.lastReturnValue = SQLITE_OK; + + /* Openthe database */ + if (sqlite_open(statedb, &db) != SQLITE_OK) { + const char *errmsg= sqlite3_errmsg(ctx->statedb.db); + CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: Failed to sqlite3 open statedb - bail out: %s.", + errmsg ? errmsg : ""); + + rc = -1; + ctx->status_code = CSYNC_STATUS_STATEDB_LOAD_ERROR; + goto out; + } + + if (_csync_check_db_integrity(db) != 0) { + const char *errmsg= sqlite3_errmsg(db); + CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "ERR: sqlite3 integrity check failed - bail out: %s.", + errmsg ? errmsg : ""); + rc = -1; + ctx->status_code = CSYNC_STATUS_STATEDB_CORRUPTED; + goto out; + } + + if (_csync_statedb_is_empty(db)) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "statedb contents doesn't exist"); + csync_set_statedb_exists(ctx, 0); + } else { + csync_set_statedb_exists(ctx, 1); + } + + /* Print out the version */ + // + result = csync_statedb_query(db, "SELECT sqlite_version();"); + if (result && result->count >= 1) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "sqlite3 version \"%s\"", *result->vector); + } + c_strlist_destroy(result); + + /* optimization for speeding up SQLite */ + result = csync_statedb_query(db, "PRAGMA synchronous = NORMAL;"); + c_strlist_destroy(result); + result = csync_statedb_query(db, "PRAGMA case_sensitive_like = ON;"); + c_strlist_destroy(result); + + /* set a busy handler with 5 seconds timeout */ + sqlite3_busy_timeout(db, 5000); + +#ifndef NDEBUG + sqlite3_profile(db, sqlite_profile, 0 ); +#endif + *pdb = db; + + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Success"); + + return 0; +out: + sqlite3_close(db); + return rc; +} + +int csync_statedb_close(CSYNC *ctx) { + int rc = 0; + + if (!ctx) { + return -1; + } + + /* deallocate query resources */ + if( ctx->statedb.by_fileid_stmt ) { + sqlite3_finalize(ctx->statedb.by_fileid_stmt); + ctx->statedb.by_fileid_stmt = NULL; + } + if( ctx->statedb.by_hash_stmt ) { + sqlite3_finalize(ctx->statedb.by_hash_stmt); + ctx->statedb.by_hash_stmt = NULL; + } + if( ctx->statedb.by_inode_stmt) { + sqlite3_finalize(ctx->statedb.by_inode_stmt); + ctx->statedb.by_inode_stmt = NULL; + } + + ctx->statedb.lastReturnValue = SQLITE_OK; + + int sr = sqlite3_close(ctx->statedb.db); + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "sqlite3_close=%d", sr); + + ctx->statedb.db = 0; + + return rc; +} + +#define METADATA_QUERY \ + "phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, " \ + "filesize, ignoredChildrenRemote, " \ + "contentchecksumtype.name || ':' || contentChecksum " \ + "FROM metadata " \ + "LEFT JOIN checksumtype as contentchecksumtype ON metadata.contentChecksumTypeId == contentchecksumtype.id" + +// This funciton parses a line from the metadata table into the given csync_file_stat +// structure which it is also allocating. +// Note that this function calls laso sqlite3_step to actually get the info from db and +// returns the sqlite return type. +static int _csync_file_stat_from_metadata_table( csync_file_stat_t **st, sqlite3_stmt *stmt ) +{ + int rc = SQLITE_ERROR; + int column_count; + int len; + + if( ! stmt ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Fatal: Statement is NULL."); + return SQLITE_ERROR; + } + + column_count = sqlite3_column_count(stmt); + + SQLITE_BUSY_HANDLED( sqlite3_step(stmt) ); + + if( rc == SQLITE_ROW ) { + if(column_count > 7) { + const char *name; + + /* phash, pathlen, path, inode, uid, gid, mode, modtime */ + len = sqlite3_column_int(stmt, 1); + *st = (csync_file_stat_t*)c_malloc(sizeof(csync_file_stat_t) + len + 1); + /* clear the whole structure */ + ZERO_STRUCTP(*st); + + /* The query suceeded so use the phash we pass to the function. */ + (*st)->phash = sqlite3_column_int64(stmt, 0); + + (*st)->pathlen = sqlite3_column_int(stmt, 1); + name = (const char*) sqlite3_column_text(stmt, 2); + memcpy((*st)->path, (len ? name : ""), len + 1); + (*st)->inode = sqlite3_column_int64(stmt,3); + (*st)->mode = sqlite3_column_int(stmt, 6); + (*st)->modtime = strtoul((char*)sqlite3_column_text(stmt, 7), NULL, 10); + + if(*st && column_count > 8 ) { + (*st)->type = static_cast(sqlite3_column_int(stmt, 8)); + } + + if(column_count > 9 && sqlite3_column_text(stmt, 9)) { + (*st)->etag = c_strdup( (char*) sqlite3_column_text(stmt, 9) ); + } + if(column_count > 10 && sqlite3_column_text(stmt,10)) { + csync_vio_set_file_id((*st)->file_id, (char*) sqlite3_column_text(stmt, 10)); + } + if(column_count > 11 && sqlite3_column_text(stmt,11)) { + strncpy((*st)->remotePerm, + (char*) sqlite3_column_text(stmt, 11), + REMOTE_PERM_BUF_SIZE); + } + if(column_count > 12 && sqlite3_column_int64(stmt,12)) { + (*st)->size = sqlite3_column_int64(stmt, 12); + } + if(column_count > 13) { + (*st)->has_ignored_files = sqlite3_column_int(stmt, 13); + } + if (column_count > 14 && sqlite3_column_text(stmt, 14)) { + (*st)->checksumHeader = c_strdup((char *)sqlite3_column_text(stmt, 14)); + } + + } + } else { + if( rc != SQLITE_DONE ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Query results in %d", rc); + } + } + return rc; +} + +/* caller must free the memory */ +csync_file_stat_t *csync_statedb_get_stat_by_hash(CSYNC *ctx, + uint64_t phash) +{ + csync_file_stat_t *st = NULL; + int rc; + + if( !ctx || ctx->db_is_empty ) { + return NULL; + } + + if( ctx->statedb.by_hash_stmt == NULL ) { + const char *hash_query = "SELECT " METADATA_QUERY " WHERE phash=?1"; + + SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, hash_query, strlen(hash_query), &ctx->statedb.by_hash_stmt, NULL)); + ctx->statedb.lastReturnValue = rc; + if( rc != SQLITE_OK ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for hash query."); + return NULL; + } + } + + if( ctx->statedb.by_hash_stmt == NULL ) { + return NULL; + } + + sqlite3_bind_int64(ctx->statedb.by_hash_stmt, 1, (long long signed int)phash); + + rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_hash_stmt); + ctx->statedb.lastReturnValue = rc; + if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata: %d!", rc); + } + sqlite3_reset(ctx->statedb.by_hash_stmt); + + return st; +} + +csync_file_stat_t *csync_statedb_get_stat_by_file_id(CSYNC *ctx, + const char *file_id ) { + csync_file_stat_t *st = NULL; + int rc = 0; + + if (!file_id) { + return 0; + } + if (c_streq(file_id, "")) { + return 0; + } + + if( !ctx || ctx->db_is_empty ) { + return NULL; + } + + if( ctx->statedb.by_fileid_stmt == NULL ) { + const char *query = "SELECT " METADATA_QUERY " WHERE fileid=?1"; + + SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, query, strlen(query), &ctx->statedb.by_fileid_stmt, NULL)); + ctx->statedb.lastReturnValue = rc; + if( rc != SQLITE_OK ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for file id query."); + return NULL; + } + } + + /* bind the query value */ + sqlite3_bind_text(ctx->statedb.by_fileid_stmt, 1, file_id, -1, SQLITE_STATIC); + + rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_fileid_stmt); + ctx->statedb.lastReturnValue = rc; + if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata: %d!", rc); + } + // clear the resources used by the statement. + sqlite3_reset(ctx->statedb.by_fileid_stmt); + + return st; +} + +/* caller must free the memory */ +csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx, + uint64_t inode) +{ + csync_file_stat_t *st = NULL; + int rc; + + if (!inode) { + return NULL; + } + + if( !ctx || ctx->db_is_empty ) { + return NULL; + } + + if( ctx->statedb.by_inode_stmt == NULL ) { + const char *inode_query = "SELECT " METADATA_QUERY " WHERE inode=?1"; + + SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, inode_query, strlen(inode_query), &ctx->statedb.by_inode_stmt, NULL)); + ctx->statedb.lastReturnValue = rc; + if( rc != SQLITE_OK ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for inode query."); + return NULL; + } + } + + if( ctx->statedb.by_inode_stmt == NULL ) { + return NULL; + } + + sqlite3_bind_int64(ctx->statedb.by_inode_stmt, 1, (long long signed int)inode); + + rc = _csync_file_stat_from_metadata_table(&st, ctx->statedb.by_inode_stmt); + ctx->statedb.lastReturnValue = rc; + if( !(rc == SQLITE_ROW || rc == SQLITE_DONE) ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Could not get line from metadata by inode: %d!", rc); + } + sqlite3_reset(ctx->statedb.by_inode_stmt); + + return st; +} + +int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) { + int rc; + sqlite3_stmt *stmt = NULL; + int64_t cnt = 0; + + if( !path ) { + return -1; + } + + if( !ctx || ctx->db_is_empty ) { + return -1; + } + + /* Select the entries for anything that starts with (path+'/') + * In other words, anything that is between path+'/' and path+'0', + * (because '0' follows '/' in ascii) + */ + const char *below_path_query = "SELECT " METADATA_QUERY " WHERE path > (?||'/') AND path < (?||'0') ORDER BY path||'/' ASC"; + SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, below_path_query, -1, &stmt, NULL)); + ctx->statedb.lastReturnValue = rc; + if( rc != SQLITE_OK ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for below path query."); + return -1; + } + + if (stmt == NULL) { + return -1; + } + + sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, path, -1, SQLITE_STATIC); + + cnt = 0; + + ctx->statedb.lastReturnValue = rc; + do { + csync_file_stat_t *st = NULL; + + rc = _csync_file_stat_from_metadata_table( &st, stmt); + if( st ) { + /* When selective sync is used, the database may have subtrees with a parent + * whose etag (md5) is _invalid_. These are ignored and shall not appear in the + * remote tree. + * Sometimes folders that are not ignored by selective sync get marked as + * _invalid_, but that is not a problem as the next discovery will retrieve + * their correct etags again and we don't run into this case. + */ + if( c_streq(st->etag, "_invalid_") ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s selective sync excluded", st->path); + char *skipbase = c_strdup(st->path); + skipbase[st->pathlen] = '/'; + int skiplen = st->pathlen + 1; + + /* Skip over all entries with the same base path. Note that this depends + * strongly on the ordering of the retrieved items. */ + do { + csync_file_stat_free(st); + rc = _csync_file_stat_from_metadata_table( &st, stmt); + if( st && strncmp(st->path, skipbase, skiplen) != 0 ) { + break; + } + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s selective sync excluded because the parent is", st->path); + } while( rc == SQLITE_ROW ); + + /* End of data? */ + if( rc != SQLITE_ROW || !st ) { + continue; + } + } + + /* Check for exclusion from the tree. + * Note that this is only a safety net in case the ignore list changes + * without a full remote discovery being triggered. */ + CSYNC_EXCLUDE_TYPE excluded = csync_excluded_traversal(ctx->excludes, st->path, st->type); + if (excluded != CSYNC_NOT_EXCLUDED) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s excluded (%d)", st->path, excluded); + + if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE + || excluded == CSYNC_FILE_SILENTLY_EXCLUDED) { + csync_file_stat_free(st); + continue; + } + + st->instruction = CSYNC_INSTRUCTION_IGNORE; + } + + /* store into result list. */ + if (c_rbtree_insert(ctx->remote.tree, (void *) st) < 0) { + csync_file_stat_free(st); + ctx->status_code = CSYNC_STATUS_TREE_ERROR; + break; + } + cnt++; + } + } while( rc == SQLITE_ROW ); + + ctx->statedb.lastReturnValue = rc; + if( rc != SQLITE_DONE ) { + ctx->status_code = CSYNC_STATUS_TREE_ERROR; + } else { + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "%" PRId64 " entries read below path %s from db.", cnt, path); + } + sqlite3_finalize(stmt); + + return 0; +} + +/* query the statedb, caller must free the memory */ +c_strlist_t *csync_statedb_query(sqlite3 *db, + const char *statement) { + int err = SQLITE_OK; + int rc = SQLITE_OK; + size_t i = 0; + size_t busy_count = 0; + size_t retry_count = 0; + size_t column_count = 0; + sqlite3_stmt *stmt; + const char *tail = NULL; + const char *field = NULL; + c_strlist_t *result = NULL; + int row = 0; + + do { + /* compile SQL program into a virtual machine, reattempteing if busy */ + do { + if (busy_count) { + csync_sleep(100); + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "sqlite3_prepare: BUSY counter: %zu", busy_count); + } + err = sqlite3_prepare(db, statement, -1, &stmt, &tail); + } while (err == SQLITE_BUSY && busy_count ++ < 120); + + if (err != SQLITE_OK) { + if (err == SQLITE_BUSY) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Gave up waiting for lock to clear"); + } + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, + "sqlite3_compile error: %s - on query %s", + sqlite3_errmsg(db), statement); + break; + } else { + busy_count = 0; + column_count = sqlite3_column_count(stmt); + + /* execute virtual machine by iterating over rows */ + for(;;) { + err = sqlite3_step(stmt); + + if (err == SQLITE_BUSY) { + if (busy_count++ > 120) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Busy counter has reached its maximum. Aborting this sql statement"); + break; + } + csync_sleep(100); + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "sqlite3_step: BUSY counter: %zu", busy_count); + continue; + } + + if (err == SQLITE_MISUSE) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "sqlite3_step: MISUSE!!"); + } + + if (err == SQLITE_DONE) { + if (result == NULL) { + result = c_strlist_new(1); + } + break; + } + + if (err == SQLITE_ERROR) { + break; + } + + row++; + if( result ) { + result = c_strlist_expand(result, row*column_count); + } else { + result = c_strlist_new(column_count); + } + + if (result == NULL) { + return NULL; + } + + /* iterate over columns */ + for (i = 0; i < column_count; i++) { + field = (const char *) sqlite3_column_text(stmt, i); + if (!field) + field = ""; + // CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "sqlite3_column_text: %s", field); + if (c_strlist_add(result, field) < 0) { + c_strlist_destroy(result); + return NULL; + } + } + } /* end infinite for loop */ + + /* deallocate vm resources */ + rc = sqlite3_finalize(stmt); + + if (err != SQLITE_DONE && rc != SQLITE_SCHEMA) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "sqlite_step error: %s - on query: %s", sqlite3_errmsg(db), statement); + if (result != NULL) { + c_strlist_destroy(result); + } + return NULL; + } + + if (rc == SQLITE_SCHEMA) { + retry_count ++; + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "SQLITE_SCHEMA error occurred on query: %s", statement); + if (retry_count < 10) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Retrying now."); + } else { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "RETRY count has reached its maximum. Aborting statement: %s", statement); + if (result != NULL) { + c_strlist_destroy(result); + } + result = c_strlist_new(1); + } + } + } + } while (rc == SQLITE_SCHEMA && retry_count < 10); + + return result; +} + +/* vim: set ts=8 sw=2 et cindent: */ diff --git a/src/csync/csync_statedb.h b/src/csync/csync_statedb.h index 601e34a1b..dc58a9b08 100644 --- a/src/csync/csync_statedb.h +++ b/src/csync/csync_statedb.h @@ -33,10 +33,6 @@ #ifndef _CSYNC_STATEDB_H #define _CSYNC_STATEDB_H -#ifdef __cplusplus -extern "C" { -#endif - #include "c_lib.h" #include "csync_private.h" @@ -96,10 +92,6 @@ int csync_statedb_get_below_path(CSYNC *ctx, const char *path); */ c_strlist_t *csync_statedb_query(sqlite3 *db, const char *statement); -#ifdef __cplusplus -} -#endif - /** * }@ */ diff --git a/src/csync/csync_time.c b/src/csync/csync_time.c index 85bdd5ff4..3092819a3 100644 --- a/src/csync/csync_time.c +++ b/src/csync/csync_time.c @@ -34,6 +34,8 @@ #ifndef _WIN32 #include #include +#else +#include #endif #define CSYNC_LOG_CATEGORY_NAME "csync.time" diff --git a/src/csync/csync_time.h b/src/csync/csync_time.h index 1492bef8d..55aa09841 100644 --- a/src/csync/csync_time.h +++ b/src/csync/csync_time.h @@ -21,6 +21,10 @@ #ifndef _CSYNC_TIME_H #define _CSYNC_TIME_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include "csync_private.h" @@ -28,4 +32,8 @@ int csync_gettime(struct timespec *tp); void csync_sleep(unsigned int msecs); +#ifdef __cplusplus +} +#endif + #endif /* _CSYNC_TIME_H */ diff --git a/src/csync/csync_update.c b/src/csync/csync_update.c deleted file mode 100644 index 1a069442e..000000000 --- a/src/csync/csync_update.c +++ /dev/null @@ -1,862 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * Copyright (c) 2012-2013 by Klaas Freitag - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config_csync.h" - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include -#include - -#include "c_lib.h" -#include "c_jhash.h" - -#include "csync_private.h" -#include "csync_exclude.h" -#include "csync_statedb.h" -#include "csync_update.h" -#include "csync_util.h" -#include "csync_misc.h" - -#include "vio/csync_vio.h" - -#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) { - const char *path; - int len; - uint64_t h = 0; - - if( ctx && file ) { - path = file; - if (ctx->current == LOCAL_REPLICA) { - if (strlen(path) <= strlen(ctx->local.uri)) { - return 0; - } - path += strlen(ctx->local.uri) + 1; - } - len = strlen(path); - h = c_jhash64((uint8_t *) path, len, 0); - } - return h; -} - -#ifdef NO_RENAME_EXTENSION -/* Return true if the two path have the same extension. false otherwise. */ -static bool _csync_sameextension(const char *p1, const char *p2) { - /* Find pointer to the extensions */ - const char *e1 = strrchr(p1, '.'); - const char *e2 = strrchr(p2, '.'); - - /* If the found extension contains a '/', it is because the . was in the folder name - * => no extensions */ - if (e1 && strchr(e1, '/')) e1 = NULL; - if (e2 && strchr(e2, '/')) e2 = NULL; - - /* If none have extension, it is the same extension */ - if (!e1 && !e2) - return true; - - /* c_streq takes care of the rest */ - return c_streq(e1, e2); -} -#endif - -static bool _last_db_return_error(CSYNC* ctx) { - return ctx->statedb.lastReturnValue != SQLITE_OK && ctx->statedb.lastReturnValue != SQLITE_DONE && ctx->statedb.lastReturnValue != SQLITE_ROW; -} - -/* - * This static method is needed because the type members of the two structs use - * different enum values. A direct comparion is not neccessarily correct. - * - * tmp is csync_file_stat_t - * fs is csync_vio_file_stat_t with this vio type: - * enum csync_vio_file_type_e { - * CSYNC_VIO_FILE_TYPE_UNKNOWN, - * CSYNC_VIO_FILE_TYPE_REGULAR, - * CSYNC_VIO_FILE_TYPE_DIRECTORY, - * CSYNC_VIO_FILE_TYPE_FIFO, - * CSYNC_VIO_FILE_TYPE_SOCKET, - * CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE, - * CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE, - * CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK - * }; - * - * csync_file_stat_t can be: - * CSYNC_FTW_TYPE_SKIP, CSYNC_FTW_TYPE_FILE - * CSYNC_FTW_TYPE_DIR, CSYNC_FTW_TYPE_SLINK - */ -static bool _csync_filetype_different( const csync_file_stat_t *tmp, const csync_vio_file_stat_t *fs) -{ - if( !(tmp && fs)) return false; - - if( tmp->type == CSYNC_FTW_TYPE_SKIP ) return true; - - if( tmp->type == CSYNC_FTW_TYPE_DIR && fs->type != CSYNC_VIO_FILE_TYPE_DIRECTORY ) - return true; - if( tmp->type == CSYNC_FTW_TYPE_FILE && fs->type != CSYNC_VIO_FILE_TYPE_REGULAR ) - return true; - if( tmp->type == CSYNC_FTW_TYPE_SLINK && fs->type != CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK ) - return true; - - return false; // both are NOT different. -} - -/* Return true if two mtime are considered equal - * We consider mtime that are one hour difference to be equal if they are one hour appart - * because on some system (FAT) the date is changing when the daylight saving is changing */ -static bool _csync_mtime_equal(time_t a, time_t b) -{ - if (a == b) - return true; - - /* 1h of difference +- 1 second because the accuracy of FAT is 2 seconds (#2438) */ - if (fabs(3600 - fabs(difftime(a, b))) < 2) - return true; - - return false; -} - -/** - * The main function of the discovery/update pass. - * - * It's called (indirectly) by csync_update(), once for each entity in the - * local filesystem and once for each entity in the server data. - * - * It has two main jobs: - * - figure out whether anything happened compared to the sync journal - * and set (primarily) the instruction flag accordingly - * - build the ctx->local.tree / ctx->remote.tree - * - * See doc/dev/sync-algorithm.md for an overview. - */ -static int _csync_detect_update(CSYNC *ctx, const char *file, - const csync_vio_file_stat_t *fs, const int type) { - uint64_t h = 0; - size_t len = 0; - size_t size = 0; - const char *path = NULL; - csync_file_stat_t *st = NULL; - csync_file_stat_t *tmp = NULL; - CSYNC_EXCLUDE_TYPE excluded; - - if ((file == NULL) || (fs == NULL)) { - errno = EINVAL; - ctx->status_code = CSYNC_STATUS_PARAM_ERROR; - return -1; - } - - path = file; - if (ctx->current == LOCAL_REPLICA) { - if (strlen(path) <= strlen(ctx->local.uri)) { - ctx->status_code = CSYNC_STATUS_PARAM_ERROR; - return -1; - } - path += strlen(ctx->local.uri) + 1; - } - - len = strlen(path); - - if (type == CSYNC_FTW_TYPE_SKIP) { - excluded =CSYNC_FILE_EXCLUDE_STAT_FAILED; - } else { - /* Check if file is excluded */ - excluded = csync_excluded_traversal(ctx->excludes, path, type); - } - - if( excluded == CSYNC_NOT_EXCLUDED ) { - /* Even if it is not excluded by a pattern, maybe it is to be ignored - * because it's a hidden file that should not be synced. - * This code should probably be in csync_exclude, but it does not have the fs parameter. - * Keep it here for now */ - if (ctx->ignore_hidden_files && (fs->flags & CSYNC_VIO_FILE_FLAGS_HIDDEN)) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file excluded because it is a hidden file: %s", path); - excluded = CSYNC_FILE_EXCLUDE_HIDDEN; - } - } else { - /* File is ignored because it's matched by a user- or system exclude pattern. */ - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s excluded (%d)", path, excluded); - if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE) { - return 1; - } - if (excluded == CSYNC_FILE_SILENTLY_EXCLUDED) { - return 1; - } - } - - if (ctx->current == REMOTE_REPLICA && ctx->callbacks.checkSelectiveSyncBlackListHook) { - if (ctx->callbacks.checkSelectiveSyncBlackListHook(ctx->callbacks.update_callback_userdata, path)) { - return 1; - } - } - - h = _hash_of_file(ctx, file ); - if( h == 0 ) { - return -1; - } - size = sizeof(csync_file_stat_t) + len + 1; - - st = c_malloc(size); - - /* Set instruction by default to none */ - st->instruction = CSYNC_INSTRUCTION_NONE; - st->etag = NULL; - st->child_modified = 0; - st->has_ignored_files = 0; - if (type == CSYNC_FTW_TYPE_FILE ) { - if (fs->mtime == 0) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s - mtime is zero!", path); - } - } - - if (excluded > CSYNC_NOT_EXCLUDED || type == CSYNC_FTW_TYPE_SLINK) { - st->instruction = CSYNC_INSTRUCTION_IGNORE; - if (ctx->current_fs) { - ctx->current_fs->has_ignored_files = true; - } - - goto out; - } - - /* Update detection: Check if a database entry exists. - * If not, the file is either new or has been renamed. To see if it is - * renamed, the db gets queried by the inode of the file as that one - * does not change on rename. - */ - if (csync_get_statedb_exists(ctx)) { - tmp = csync_statedb_get_stat_by_hash(ctx, h); - - if(_last_db_return_error(ctx)) { - csync_file_stat_free(st); - csync_file_stat_free(tmp); - ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL; - return -1; - } - - if(tmp && tmp->phash == h ) { /* there is an entry in the database */ - /* we have an update! */ - CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "Database entry found, compare: %" PRId64 " <-> %" PRId64 - ", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64 - ", size: %" PRId64 " <-> %" PRId64 ", perms: %s <-> %s, ignore: %d", - ((int64_t) fs->mtime), ((int64_t) tmp->modtime), - fs->etag, tmp->etag, (uint64_t) fs->inode, (uint64_t) tmp->inode, - (uint64_t) fs->size, (uint64_t) tmp->size, fs->remotePerm, tmp->remotePerm, tmp->has_ignored_files ); - if (ctx->current == REMOTE_REPLICA && !c_streq(fs->etag, tmp->etag)) { - st->instruction = CSYNC_INSTRUCTION_EVAL; - - // Preserve the EVAL flag later on if the type has changed. - if (_csync_filetype_different(tmp, fs)) { - st->child_modified = 1; - } - - goto out; - } - if (ctx->current == LOCAL_REPLICA && - (!_csync_mtime_equal(fs->mtime, tmp->modtime) - // zero size in statedb can happen during migration - || (tmp->size != 0 && fs->size != tmp->size))) { - - // Checksum comparison at this stage is only enabled for .eml files, - // check #4754 #4755 - bool isEmlFile = csync_fnmatch("*.eml", file, FNM_CASEFOLD) == 0; - if (isEmlFile && fs->size == tmp->size && tmp->checksumHeader) { - if (ctx->callbacks.checksum_hook) { - st->checksumHeader = ctx->callbacks.checksum_hook( - file, tmp->checksumHeader, - ctx->callbacks.checksum_userdata); - } - bool checksumIdentical = false; - if (st->checksumHeader) { - checksumIdentical = strncmp(st->checksumHeader, tmp->checksumHeader, 1000) == 0; - } - if (checksumIdentical) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "NOTE: Checksums are identical, file did not actually change: %s", path); - st->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; - goto out; - } - } - - // Preserve the EVAL flag later on if the type has changed. - if (_csync_filetype_different(tmp, fs)) { - st->child_modified = 1; - } - - st->instruction = CSYNC_INSTRUCTION_EVAL; - goto out; - } - bool metadata_differ = (ctx->current == REMOTE_REPLICA && (!c_streq(fs->file_id, tmp->file_id) - || !c_streq(fs->remotePerm, tmp->remotePerm))) - || (ctx->current == LOCAL_REPLICA && fs->inode != tmp->inode); - if (type == CSYNC_FTW_TYPE_DIR && ctx->current == REMOTE_REPLICA - && !metadata_differ && ctx->read_remote_from_db) { - /* If both etag and file id are equal for a directory, read all contents from - * the database. - * The metadata comparison ensure that we fetch all the file id or permission when - * upgrading owncloud - */ - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Reading from database: %s", path); - ctx->remote.read_from_db = true; - } - /* If it was remembered in the db that the remote dir has ignored files, store - * that so that the reconciler can make advantage of. - */ - if( ctx->current == REMOTE_REPLICA ) { - st->has_ignored_files = tmp->has_ignored_files; - } - if (metadata_differ) { - /* file id or permissions has changed. Which means we need to update them in the DB. */ - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Need to update metadata for: %s", path); - st->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; - } else { - st->instruction = CSYNC_INSTRUCTION_NONE; - } - } else { - enum csync_vio_file_type_e tmp_vio_type = CSYNC_VIO_FILE_TYPE_UNKNOWN; - - /* tmp might point to malloc mem, so free it here before reusing tmp */ - csync_file_stat_free(tmp); - - /* check if it's a file and has been renamed */ - if (ctx->current == LOCAL_REPLICA) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Checking for rename based on inode # %" PRId64 "", (uint64_t) fs->inode); - - tmp = csync_statedb_get_stat_by_inode(ctx, fs->inode); - - if(_last_db_return_error(ctx)) { - csync_file_stat_free(st); - ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL; - return -1; - } - - /* translate the file type between the two stat types csync has. */ - if( tmp && tmp->type == CSYNC_FTW_TYPE_FILE ) { - tmp_vio_type = CSYNC_VIO_FILE_TYPE_REGULAR; - } else if( tmp && tmp->type == CSYNC_FTW_TYPE_DIR) { - tmp_vio_type = CSYNC_VIO_FILE_TYPE_DIRECTORY; - } else if( tmp && tmp->type == CSYNC_FTW_TYPE_SLINK ) { - tmp_vio_type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK; - } else { - tmp_vio_type = CSYNC_VIO_FILE_TYPE_UNKNOWN; - } - - // Default to NEW unless we're sure it's a rename. - st->instruction = CSYNC_INSTRUCTION_NEW; - - bool isRename = - tmp && tmp->inode == fs->inode && tmp_vio_type == fs->type - && (tmp->modtime == fs->mtime || fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) -#ifdef NO_RENAME_EXTENSION - && _csync_sameextension(tmp->path, path) -#endif - ; - - - // Verify the checksum where possible - if (isRename && tmp->checksumHeader && ctx->callbacks.checksum_hook - && fs->type == CSYNC_VIO_FILE_TYPE_REGULAR) { - st->checksumHeader = ctx->callbacks.checksum_hook( - file, tmp->checksumHeader, - ctx->callbacks.checksum_userdata); - if (st->checksumHeader) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "checking checksum of potential rename %s %s <-> %s", path, st->checksumHeader, tmp->checksumHeader); - isRename = strncmp(st->checksumHeader, tmp->checksumHeader, 1000) == 0; - } - } - - if (isRename) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "pot rename detected based on inode # %" PRId64 "", (uint64_t) fs->inode); - /* inode found so the file has been renamed */ - st->instruction = CSYNC_INSTRUCTION_EVAL_RENAME; - if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) { - csync_rename_record(ctx, tmp->path, path); - } - } - goto out; - - } else { - /* Remote Replica Rename check */ - tmp = csync_statedb_get_stat_by_file_id(ctx, fs->file_id); - - if(_last_db_return_error(ctx)) { - csync_file_stat_free(st); - ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL; - return -1; - } - if(tmp ) { /* tmp existing at all */ - if ( _csync_filetype_different(tmp, fs)) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "file types different is not!"); - st->instruction = CSYNC_INSTRUCTION_NEW; - goto out; - } - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "remote rename detected based on fileid %s %s", tmp->path, file); - st->instruction = CSYNC_INSTRUCTION_EVAL_RENAME; - if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) { - csync_rename_record(ctx, tmp->path, path); - } else { - if( !c_streq(tmp->etag, fs->etag) ) { - /* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "ETags are different!"); */ - /* File with different etag, don't do a rename, but download the file again */ - st->instruction = CSYNC_INSTRUCTION_NEW; - } - } - goto out; - - } else { - /* file not found in statedb */ - st->instruction = CSYNC_INSTRUCTION_NEW; - - if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY && ctx->current == REMOTE_REPLICA && ctx->callbacks.checkSelectiveSyncNewFolderHook) { - if (ctx->callbacks.checkSelectiveSyncNewFolderHook(ctx->callbacks.update_callback_userdata, path, fs->remotePerm)) { - csync_file_stat_free(st); - return 1; - } - } - goto out; - } - } - } - } else { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Unable to open statedb" ); - csync_file_stat_free(st); - ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL; - return -1; - } - -out: - - /* Set the ignored error string. */ - if (st->instruction == CSYNC_INSTRUCTION_IGNORE) { - if( type == CSYNC_FTW_TYPE_SLINK ) { - st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK; /* Symbolic links are ignored. */ - } else { - if (excluded == CSYNC_FILE_EXCLUDE_LIST) { - st->error_status = CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST; /* File listed on ignore list. */ - } else if (excluded == CSYNC_FILE_EXCLUDE_INVALID_CHAR) { - st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS; /* File contains invalid characters. */ - } else if (excluded == CSYNC_FILE_EXCLUDE_TRAILING_SPACE) { - st->error_status = CSYNC_STATUS_INDIVIDUAL_TRAILING_SPACE; /* File ends with a trailing space. */ - } else if (excluded == CSYNC_FILE_EXCLUDE_LONG_FILENAME) { - st->error_status = CSYNC_STATUS_INDIVIDUAL_EXCLUDE_LONG_FILENAME; /* File name is too long. */ - } else if (excluded == CSYNC_FILE_EXCLUDE_HIDDEN ) { - st->error_status = CSYNC_STATUS_INDIVIDUAL_EXCLUDE_HIDDEN; - } else if (excluded == CSYNC_FILE_EXCLUDE_STAT_FAILED) { - st->error_status = CSYNC_STATUS_INDIVIDUAL_STAT_FAILED; - } else if (excluded == CSYNC_FILE_EXCLUDE_CONFLICT) { - st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE; - } - } - } - if (st->instruction != CSYNC_INSTRUCTION_NONE - && st->instruction != CSYNC_INSTRUCTION_IGNORE - && st->instruction != CSYNC_INSTRUCTION_UPDATE_METADATA - && type != CSYNC_FTW_TYPE_DIR) { - st->child_modified = 1; - } - ctx->current_fs = st; - - csync_file_stat_free(tmp); - st->inode = fs->inode; - st->mode = fs->mode; - st->size = fs->size; - st->modtime = fs->mtime; - st->type = type; - st->etag = NULL; - if( fs->etag ) { - SAFE_FREE(st->etag); - st->etag = c_strdup(fs->etag); - } - csync_vio_set_file_id(st->file_id, fs->file_id); - if (fs->fields & CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADURL) { - SAFE_FREE(st->directDownloadUrl); - st->directDownloadUrl = c_strdup(fs->directDownloadUrl); - } - if (fs->fields & CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADCOOKIES) { - SAFE_FREE(st->directDownloadCookies); - st->directDownloadCookies = c_strdup(fs->directDownloadCookies); - } - if (fs->fields & CSYNC_VIO_FILE_STAT_FIELDS_PERM) { - strncpy(st->remotePerm, fs->remotePerm, REMOTE_PERM_BUF_SIZE); - } - - // For the remote: propagate the discovered checksum - if (fs->checksumHeader && ctx->current == REMOTE_REPLICA) { - st->checksumHeader = c_strdup(fs->checksumHeader); - } - - st->phash = h; - st->pathlen = len; - memcpy(st->path, (len ? path : ""), len + 1); - - switch (ctx->current) { - case LOCAL_REPLICA: - if (c_rbtree_insert(ctx->local.tree, (void *) st) < 0) { - csync_file_stat_free(st); - ctx->status_code = CSYNC_STATUS_TREE_ERROR; - return -1; - } - break; - case REMOTE_REPLICA: - if (c_rbtree_insert(ctx->remote.tree, (void *) st) < 0) { - csync_file_stat_free(st); - ctx->status_code = CSYNC_STATUS_TREE_ERROR; - return -1; - } - break; - default: - break; - } - CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "file: %s, instruction: %s <<=", st->path, - csync_instruction_str(st->instruction)); - - return 0; -} - -int csync_walker(CSYNC *ctx, const char *file, const csync_vio_file_stat_t *fs, - enum csync_ftw_flags_e flag) { - int rc = -1; - int type = CSYNC_FTW_TYPE_SKIP; - csync_file_stat_t *st = NULL; - uint64_t h; - - if (ctx->abort) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Aborted!"); - ctx->status_code = CSYNC_STATUS_ABORTED; - return -1; - } - - switch (flag) { - case CSYNC_FTW_FLAG_FILE: - if (ctx->current == REMOTE_REPLICA) { - if (fs->fields & CSYNC_VIO_FILE_STAT_FIELDS_SIZE) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [file_id=%s size=%" PRIu64 "]", file, fs->file_id, fs->size); - } else { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [file_id=%s size=UNKNOWN]", file, fs->file_id); - } - } else { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [inode=%" PRIu64 " size=%" PRIu64 "]", file, fs->inode, fs->size); - } - type = CSYNC_FTW_TYPE_FILE; - break; - case CSYNC_FTW_FLAG_DIR: /* enter directory */ - if (ctx->current == REMOTE_REPLICA) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "directory: %s [file_id=%s]", file, fs->file_id); - } else { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "directory: %s [inode=%" PRIu64 "]", file, fs->inode); - } - type = CSYNC_FTW_TYPE_DIR; - break; - case CSYNC_FTW_FLAG_NSTAT: /* not statable file */ - /* if file was here before and now is not longer stat-able, still - * add it to the db, otherwise not. */ - h = _hash_of_file( ctx, file ); - if( h == 0 ) { - return 0; - } - st = csync_statedb_get_stat_by_hash(ctx, h); - if( !st ) { - return 0; - } - csync_file_stat_free(st); - st = NULL; - - type = CSYNC_FTW_TYPE_SKIP; - break; - case CSYNC_FTW_FLAG_SLINK: - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "symlink: %s - not supported", file); - type = CSYNC_FTW_TYPE_SLINK; - break; - case CSYNC_FTW_FLAG_DNR: - case CSYNC_FTW_FLAG_DP: - case CSYNC_FTW_FLAG_SLN: - default: - return 0; - break; - } - - rc = _csync_detect_update(ctx, file, fs, type ); - - return rc; -} - -static bool fill_tree_from_db(CSYNC *ctx, const char *uri) -{ - if( csync_statedb_get_below_path(ctx, uri) < 0 ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "StateDB could not be read!"); - return false; - } - - return true; -} - -/* set the current item to an ignored state. - * If the item is set to ignored, the update phase continues, ie. its not a hard error */ -static bool mark_current_item_ignored( CSYNC *ctx, csync_file_stat_t *previous_fs, CSYNC_STATUS status ) -{ - if(!ctx) { - return false; - } - - if (ctx->current_fs) { - ctx->current_fs->instruction = CSYNC_INSTRUCTION_IGNORE; - ctx->current_fs->error_status = status; - /* If a directory has ignored files, put the flag on the parent directory as well */ - if( previous_fs ) { - previous_fs->has_ignored_files = true; - } - return true; - } - return false; -} - -/* File tree walker */ -int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn, - unsigned int depth) { - char *filename = NULL; - char *d_name = NULL; - csync_vio_handle_t *dh = NULL; - csync_vio_file_stat_t *dirent = NULL; - csync_file_stat_t *previous_fs = NULL; - int read_from_db = 0; - int rc = 0; - int res = 0; - - if (!depth) { - mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_INDIVIDUAL_TOO_DEEP); - goto done; - } - - bool do_read_from_db = (ctx->current == REMOTE_REPLICA && ctx->remote.read_from_db); - - read_from_db = ctx->remote.read_from_db; - - // if the etag of this dir is still the same, its content is restored from the - // database. - if( do_read_from_db ) { - if( ! fill_tree_from_db(ctx, uri) ) { - errno = ENOENT; - ctx->status_code = CSYNC_STATUS_OPENDIR_ERROR; - goto error; - } - goto done; - } - - if ((dh = csync_vio_opendir(ctx, uri)) == NULL) { - if (ctx->abort) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Aborted!"); - ctx->status_code = CSYNC_STATUS_ABORTED; - goto error; - } - int asp = 0; - /* permission denied */ - ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_OPENDIR_ERROR); - if (errno == EACCES) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Permission denied."); - if (mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_PERMISSION_DENIED)) { - goto done; - } - } else if(errno == ENOENT) { - asp = asprintf( &ctx->error_string, "%s", uri); - if (asp < 0) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "asprintf failed!"); - } - } - // 403 Forbidden can be sent by the server if the file firewall is active. - // A file or directory should be ignored and sync must continue. See #3490 - else if(errno == ERRNO_FORBIDDEN) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Directory access Forbidden (File Firewall?)"); - if( mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_FORBIDDEN) ) { - goto done; - } - /* if current_fs is not defined here, better throw an error */ - } - // The server usually replies with the custom "503 Storage not available" - // if some path is temporarily unavailable. But in some cases a standard 503 - // is returned too. Thus we can't distinguish the two and will treat any - // 503 as request to ignore the folder. See #3113 #2884. - else if(errno == ERRNO_STORAGE_UNAVAILABLE || errno == ERRNO_SERVICE_UNAVAILABLE) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Storage was not available!"); - if( mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_STORAGE_UNAVAILABLE ) ) { - goto done; - } - /* if current_fs is not defined here, better throw an error */ - } else { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "opendir failed for %s - errno %d", uri, errno); - } - goto error; - } - - while ((dirent = csync_vio_readdir(ctx, dh))) { - int flen; - int flag; - - /* Conversion error */ - if (dirent->name == NULL && dirent->original_name) { - ctx->status_code = CSYNC_STATUS_INVALID_CHARACTERS; - ctx->error_string = dirent->original_name; // take ownership - dirent->original_name = NULL; - goto error; - } - - d_name = dirent->name; - if (d_name == NULL) { - ctx->status_code = CSYNC_STATUS_READDIR_ERROR; - goto error; - } - - /* skip "." and ".." */ - if ( (d_name[0] == '.' && d_name[1] == '\0') - || (d_name[0] == '.' && d_name[1] == '.' && d_name[2] == '\0')) { - csync_vio_file_stat_destroy(dirent); - dirent = NULL; - continue; - } - - if (uri[0] == '\0') { - filename = c_strdup(d_name); - flen = strlen(d_name); - } else { - flen = asprintf(&filename, "%s/%s", uri, d_name); - } - if (flen < 0 || !filename) { - csync_vio_file_stat_destroy(dirent); - dirent = NULL; - ctx->status_code = CSYNC_STATUS_MEMORY_ERROR; - goto error; - } - - /* Only for the local replica we have to stat(), for the remote one we have all data already */ - if (ctx->replica == LOCAL_REPLICA) { - res = csync_vio_stat(ctx, filename, dirent); - } else { - res = 0; - } - - /* if the filename starts with a . we consider it a hidden file - * For windows, the hidden state is also discovered within the vio - * local stat function. - */ - if( d_name[0] == '.' ) { - if (strcmp(".sys.admin#recall#", d_name) != 0) { /* recall file shall not be ignored (#4420) */ - dirent->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN; - } - } - - if( res == 0) { - switch (dirent->type) { - case CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK: - flag = CSYNC_FTW_FLAG_SLINK; - break; - case CSYNC_VIO_FILE_TYPE_DIRECTORY: - flag = CSYNC_FTW_FLAG_DIR; - break; - case CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE: - case CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE: - case CSYNC_VIO_FILE_TYPE_SOCKET: - flag = CSYNC_FTW_FLAG_SPEC; - break; - case CSYNC_VIO_FILE_TYPE_FIFO: - flag = CSYNC_FTW_FLAG_SPEC; - break; - default: - flag = CSYNC_FTW_FLAG_FILE; - break; - }; - } else { - flag = CSYNC_FTW_FLAG_NSTAT; - } - - previous_fs = ctx->current_fs; - - /* Call walker function for each file */ - rc = fn(ctx, filename, dirent, flag); - /* this function may update ctx->current and ctx->read_from_db */ - - if (rc < 0) { - if (CSYNC_STATUS_IS_OK(ctx->status_code)) { - ctx->status_code = CSYNC_STATUS_UPDATE_ERROR; - } - - ctx->current_fs = previous_fs; - goto error; - } - - if (flag == CSYNC_FTW_FLAG_DIR && rc == 0 - && (!ctx->current_fs || ctx->current_fs->instruction != CSYNC_INSTRUCTION_IGNORE)) { - rc = csync_ftw(ctx, filename, fn, depth - 1); - if (rc < 0) { - ctx->current_fs = previous_fs; - goto error; - } - - if (ctx->current_fs && !ctx->current_fs->child_modified - && ctx->current_fs->instruction == CSYNC_INSTRUCTION_EVAL) { - if (ctx->current == REMOTE_REPLICA) { - ctx->current_fs->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; - } else { - ctx->current_fs->instruction = CSYNC_INSTRUCTION_NONE; - } - } - - if (ctx->current_fs && previous_fs && ctx->current_fs->has_ignored_files) { - /* If a directory has ignored files, put the flag on the parent directory as well */ - previous_fs->has_ignored_files = ctx->current_fs->has_ignored_files; - } - } - - if (ctx->current_fs && previous_fs && ctx->current_fs->child_modified) { - /* If a directory has modified files, put the flag on the parent directory as well */ - previous_fs->child_modified = ctx->current_fs->child_modified; - } - - ctx->current_fs = previous_fs; - ctx->remote.read_from_db = read_from_db; - SAFE_FREE(filename); - csync_vio_file_stat_destroy(dirent); - dirent = NULL; - } - - csync_vio_closedir(ctx, dh); - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, " <= Closing walk for %s with read_from_db %d", uri, read_from_db); - -done: - csync_vio_file_stat_destroy(dirent); - SAFE_FREE(filename); - return rc; -error: - ctx->remote.read_from_db = read_from_db; - if (dh != NULL) { - csync_vio_closedir(ctx, dh); - } - SAFE_FREE(filename); - return -1; -} - -/* vim: set ts=8 sw=2 et cindent: */ diff --git a/src/csync/csync_update.cpp b/src/csync/csync_update.cpp new file mode 100644 index 000000000..927290b1a --- /dev/null +++ b/src/csync/csync_update.cpp @@ -0,0 +1,865 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * Copyright (c) 2012-2013 by Klaas Freitag + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_csync.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include + +#include "c_lib.h" +#include "c_jhash.h" + +#include "csync_private.h" +#include "csync_exclude.h" +#include "csync_statedb.h" +#include "csync_update.h" +#include "csync_util.h" +#include "csync_misc.h" + +#include "vio/csync_vio.h" + +#define CSYNC_LOG_CATEGORY_NAME "csync.updater" +#include "csync_log.h" +#include "csync_rename.h" + +// Needed for PRIu64 on MinGW in C++ mode. +#define __STDC_FORMAT_MACROS +#include + +/* calculate the hash of a given uri */ +static uint64_t _hash_of_file(CSYNC *ctx, const char *file) { + const char *path; + int len; + uint64_t h = 0; + + if( ctx && file ) { + path = file; + if (ctx->current == LOCAL_REPLICA) { + if (strlen(path) <= strlen(ctx->local.uri)) { + return 0; + } + path += strlen(ctx->local.uri) + 1; + } + len = strlen(path); + h = c_jhash64((uint8_t *) path, len, 0); + } + return h; +} + +#ifdef NO_RENAME_EXTENSION +/* Return true if the two path have the same extension. false otherwise. */ +static bool _csync_sameextension(const char *p1, const char *p2) { + /* Find pointer to the extensions */ + const char *e1 = strrchr(p1, '.'); + const char *e2 = strrchr(p2, '.'); + + /* If the found extension contains a '/', it is because the . was in the folder name + * => no extensions */ + if (e1 && strchr(e1, '/')) e1 = NULL; + if (e2 && strchr(e2, '/')) e2 = NULL; + + /* If none have extension, it is the same extension */ + if (!e1 && !e2) + return true; + + /* c_streq takes care of the rest */ + return c_streq(e1, e2); +} +#endif + +static bool _last_db_return_error(CSYNC* ctx) { + return ctx->statedb.lastReturnValue != SQLITE_OK && ctx->statedb.lastReturnValue != SQLITE_DONE && ctx->statedb.lastReturnValue != SQLITE_ROW; +} + +/* + * This static method is needed because the type members of the two structs use + * different enum values. A direct comparion is not neccessarily correct. + * + * tmp is csync_file_stat_t + * fs is csync_vio_file_stat_t with this vio type: + * enum csync_vio_file_type_e { + * CSYNC_VIO_FILE_TYPE_UNKNOWN, + * CSYNC_VIO_FILE_TYPE_REGULAR, + * CSYNC_VIO_FILE_TYPE_DIRECTORY, + * CSYNC_VIO_FILE_TYPE_FIFO, + * CSYNC_VIO_FILE_TYPE_SOCKET, + * CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE, + * CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE, + * CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK + * }; + * + * csync_file_stat_t can be: + * CSYNC_FTW_TYPE_SKIP, CSYNC_FTW_TYPE_FILE + * CSYNC_FTW_TYPE_DIR, CSYNC_FTW_TYPE_SLINK + */ +static bool _csync_filetype_different( const csync_file_stat_t *tmp, const csync_vio_file_stat_t *fs) +{ + if( !(tmp && fs)) return false; + + if( tmp->type == CSYNC_FTW_TYPE_SKIP ) return true; + + if( tmp->type == CSYNC_FTW_TYPE_DIR && fs->type != CSYNC_VIO_FILE_TYPE_DIRECTORY ) + return true; + if( tmp->type == CSYNC_FTW_TYPE_FILE && fs->type != CSYNC_VIO_FILE_TYPE_REGULAR ) + return true; + if( tmp->type == CSYNC_FTW_TYPE_SLINK && fs->type != CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK ) + return true; + + return false; // both are NOT different. +} + +/* Return true if two mtime are considered equal + * We consider mtime that are one hour difference to be equal if they are one hour appart + * because on some system (FAT) the date is changing when the daylight saving is changing */ +static bool _csync_mtime_equal(time_t a, time_t b) +{ + if (a == b) + return true; + + /* 1h of difference +- 1 second because the accuracy of FAT is 2 seconds (#2438) */ + if (fabs(3600 - fabs(difftime(a, b))) < 2) + return true; + + return false; +} + +/** + * The main function of the discovery/update pass. + * + * It's called (indirectly) by csync_update(), once for each entity in the + * local filesystem and once for each entity in the server data. + * + * It has two main jobs: + * - figure out whether anything happened compared to the sync journal + * and set (primarily) the instruction flag accordingly + * - build the ctx->local.tree / ctx->remote.tree + * + * See doc/dev/sync-algorithm.md for an overview. + */ +static int _csync_detect_update(CSYNC *ctx, const char *file, + const csync_vio_file_stat_t *fs, enum csync_ftw_type_e type) { + uint64_t h = 0; + size_t len = 0; + size_t size = 0; + const char *path = NULL; + csync_file_stat_t *st = NULL; + csync_file_stat_t *tmp = NULL; + CSYNC_EXCLUDE_TYPE excluded; + + if ((file == NULL) || (fs == NULL)) { + errno = EINVAL; + ctx->status_code = CSYNC_STATUS_PARAM_ERROR; + return -1; + } + + path = file; + if (ctx->current == LOCAL_REPLICA) { + if (strlen(path) <= strlen(ctx->local.uri)) { + ctx->status_code = CSYNC_STATUS_PARAM_ERROR; + return -1; + } + path += strlen(ctx->local.uri) + 1; + } + + len = strlen(path); + + if (type == CSYNC_FTW_TYPE_SKIP) { + excluded =CSYNC_FILE_EXCLUDE_STAT_FAILED; + } else { + /* Check if file is excluded */ + excluded = csync_excluded_traversal(ctx->excludes, path, type); + } + + if( excluded == CSYNC_NOT_EXCLUDED ) { + /* Even if it is not excluded by a pattern, maybe it is to be ignored + * because it's a hidden file that should not be synced. + * This code should probably be in csync_exclude, but it does not have the fs parameter. + * Keep it here for now */ + if (ctx->ignore_hidden_files && (fs->flags & CSYNC_VIO_FILE_FLAGS_HIDDEN)) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file excluded because it is a hidden file: %s", path); + excluded = CSYNC_FILE_EXCLUDE_HIDDEN; + } + } else { + /* File is ignored because it's matched by a user- or system exclude pattern. */ + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s excluded (%d)", path, excluded); + if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE) { + return 1; + } + if (excluded == CSYNC_FILE_SILENTLY_EXCLUDED) { + return 1; + } + } + + if (ctx->current == REMOTE_REPLICA && ctx->callbacks.checkSelectiveSyncBlackListHook) { + if (ctx->callbacks.checkSelectiveSyncBlackListHook(ctx->callbacks.update_callback_userdata, path)) { + return 1; + } + } + + h = _hash_of_file(ctx, file ); + if( h == 0 ) { + return -1; + } + size = sizeof(csync_file_stat_t) + len + 1; + + st = static_cast(c_malloc(size)); + + /* Set instruction by default to none */ + st->instruction = CSYNC_INSTRUCTION_NONE; + st->etag = NULL; + st->child_modified = 0; + st->has_ignored_files = 0; + if (type == CSYNC_FTW_TYPE_FILE ) { + if (fs->mtime == 0) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s - mtime is zero!", path); + } + } + + if (excluded > CSYNC_NOT_EXCLUDED || type == CSYNC_FTW_TYPE_SLINK) { + st->instruction = CSYNC_INSTRUCTION_IGNORE; + if (ctx->current_fs) { + ctx->current_fs->has_ignored_files = true; + } + + goto out; + } + + /* Update detection: Check if a database entry exists. + * If not, the file is either new or has been renamed. To see if it is + * renamed, the db gets queried by the inode of the file as that one + * does not change on rename. + */ + if (csync_get_statedb_exists(ctx)) { + tmp = csync_statedb_get_stat_by_hash(ctx, h); + + if(_last_db_return_error(ctx)) { + csync_file_stat_free(st); + csync_file_stat_free(tmp); + ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL; + return -1; + } + + if(tmp && tmp->phash == h ) { /* there is an entry in the database */ + /* we have an update! */ + CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "Database entry found, compare: %" PRId64 " <-> %" PRId64 + ", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64 + ", size: %" PRId64 " <-> %" PRId64 ", perms: %s <-> %s, ignore: %d", + ((int64_t) fs->mtime), ((int64_t) tmp->modtime), + fs->etag, tmp->etag, (uint64_t) fs->inode, (uint64_t) tmp->inode, + (uint64_t) fs->size, (uint64_t) tmp->size, fs->remotePerm, tmp->remotePerm, tmp->has_ignored_files ); + if (ctx->current == REMOTE_REPLICA && !c_streq(fs->etag, tmp->etag)) { + st->instruction = CSYNC_INSTRUCTION_EVAL; + + // Preserve the EVAL flag later on if the type has changed. + if (_csync_filetype_different(tmp, fs)) { + st->child_modified = 1; + } + + goto out; + } + if (ctx->current == LOCAL_REPLICA && + (!_csync_mtime_equal(fs->mtime, tmp->modtime) + // zero size in statedb can happen during migration + || (tmp->size != 0 && fs->size != tmp->size))) { + + // Checksum comparison at this stage is only enabled for .eml files, + // check #4754 #4755 + bool isEmlFile = csync_fnmatch("*.eml", file, FNM_CASEFOLD) == 0; + if (isEmlFile && fs->size == tmp->size && tmp->checksumHeader) { + if (ctx->callbacks.checksum_hook) { + st->checksumHeader = ctx->callbacks.checksum_hook( + file, tmp->checksumHeader, + ctx->callbacks.checksum_userdata); + } + bool checksumIdentical = false; + if (st->checksumHeader) { + checksumIdentical = strncmp(st->checksumHeader, tmp->checksumHeader, 1000) == 0; + } + if (checksumIdentical) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "NOTE: Checksums are identical, file did not actually change: %s", path); + st->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; + goto out; + } + } + + // Preserve the EVAL flag later on if the type has changed. + if (_csync_filetype_different(tmp, fs)) { + st->child_modified = 1; + } + + st->instruction = CSYNC_INSTRUCTION_EVAL; + goto out; + } + bool metadata_differ = (ctx->current == REMOTE_REPLICA && (!c_streq(fs->file_id, tmp->file_id) + || !c_streq(fs->remotePerm, tmp->remotePerm))) + || (ctx->current == LOCAL_REPLICA && fs->inode != tmp->inode); + if (type == CSYNC_FTW_TYPE_DIR && ctx->current == REMOTE_REPLICA + && !metadata_differ && ctx->read_remote_from_db) { + /* If both etag and file id are equal for a directory, read all contents from + * the database. + * The metadata comparison ensure that we fetch all the file id or permission when + * upgrading owncloud + */ + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Reading from database: %s", path); + ctx->remote.read_from_db = true; + } + /* If it was remembered in the db that the remote dir has ignored files, store + * that so that the reconciler can make advantage of. + */ + if( ctx->current == REMOTE_REPLICA ) { + st->has_ignored_files = tmp->has_ignored_files; + } + if (metadata_differ) { + /* file id or permissions has changed. Which means we need to update them in the DB. */ + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Need to update metadata for: %s", path); + st->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; + } else { + st->instruction = CSYNC_INSTRUCTION_NONE; + } + } else { + enum csync_vio_file_type_e tmp_vio_type = CSYNC_VIO_FILE_TYPE_UNKNOWN; + + /* tmp might point to malloc mem, so free it here before reusing tmp */ + csync_file_stat_free(tmp); + + /* check if it's a file and has been renamed */ + if (ctx->current == LOCAL_REPLICA) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Checking for rename based on inode # %" PRId64 "", (uint64_t) fs->inode); + + tmp = csync_statedb_get_stat_by_inode(ctx, fs->inode); + + if(_last_db_return_error(ctx)) { + csync_file_stat_free(st); + ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL; + return -1; + } + + /* translate the file type between the two stat types csync has. */ + if( tmp && tmp->type == CSYNC_FTW_TYPE_FILE ) { + tmp_vio_type = CSYNC_VIO_FILE_TYPE_REGULAR; + } else if( tmp && tmp->type == CSYNC_FTW_TYPE_DIR) { + tmp_vio_type = CSYNC_VIO_FILE_TYPE_DIRECTORY; + } else if( tmp && tmp->type == CSYNC_FTW_TYPE_SLINK ) { + tmp_vio_type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK; + } else { + tmp_vio_type = CSYNC_VIO_FILE_TYPE_UNKNOWN; + } + + // Default to NEW unless we're sure it's a rename. + st->instruction = CSYNC_INSTRUCTION_NEW; + + bool isRename = + tmp && tmp->inode == fs->inode && tmp_vio_type == fs->type + && (tmp->modtime == fs->mtime || fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) +#ifdef NO_RENAME_EXTENSION + && _csync_sameextension(tmp->path, path) +#endif + ; + + + // Verify the checksum where possible + if (isRename && tmp->checksumHeader && ctx->callbacks.checksum_hook + && fs->type == CSYNC_VIO_FILE_TYPE_REGULAR) { + st->checksumHeader = ctx->callbacks.checksum_hook( + file, tmp->checksumHeader, + ctx->callbacks.checksum_userdata); + if (st->checksumHeader) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "checking checksum of potential rename %s %s <-> %s", path, st->checksumHeader, tmp->checksumHeader); + isRename = strncmp(st->checksumHeader, tmp->checksumHeader, 1000) == 0; + } + } + + if (isRename) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "pot rename detected based on inode # %" PRId64 "", (uint64_t) fs->inode); + /* inode found so the file has been renamed */ + st->instruction = CSYNC_INSTRUCTION_EVAL_RENAME; + if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) { + csync_rename_record(ctx, tmp->path, path); + } + } + goto out; + + } else { + /* Remote Replica Rename check */ + tmp = csync_statedb_get_stat_by_file_id(ctx, fs->file_id); + + if(_last_db_return_error(ctx)) { + csync_file_stat_free(st); + ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL; + return -1; + } + if(tmp ) { /* tmp existing at all */ + if ( _csync_filetype_different(tmp, fs)) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "file types different is not!"); + st->instruction = CSYNC_INSTRUCTION_NEW; + goto out; + } + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "remote rename detected based on fileid %s %s", tmp->path, file); + st->instruction = CSYNC_INSTRUCTION_EVAL_RENAME; + if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) { + csync_rename_record(ctx, tmp->path, path); + } else { + if( !c_streq(tmp->etag, fs->etag) ) { + /* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "ETags are different!"); */ + /* File with different etag, don't do a rename, but download the file again */ + st->instruction = CSYNC_INSTRUCTION_NEW; + } + } + goto out; + + } else { + /* file not found in statedb */ + st->instruction = CSYNC_INSTRUCTION_NEW; + + if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY && ctx->current == REMOTE_REPLICA && ctx->callbacks.checkSelectiveSyncNewFolderHook) { + if (ctx->callbacks.checkSelectiveSyncNewFolderHook(ctx->callbacks.update_callback_userdata, path, fs->remotePerm)) { + csync_file_stat_free(st); + return 1; + } + } + goto out; + } + } + } + } else { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Unable to open statedb" ); + csync_file_stat_free(st); + ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL; + return -1; + } + +out: + + /* Set the ignored error string. */ + if (st->instruction == CSYNC_INSTRUCTION_IGNORE) { + if( type == CSYNC_FTW_TYPE_SLINK ) { + st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK; /* Symbolic links are ignored. */ + } else { + if (excluded == CSYNC_FILE_EXCLUDE_LIST) { + st->error_status = CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST; /* File listed on ignore list. */ + } else if (excluded == CSYNC_FILE_EXCLUDE_INVALID_CHAR) { + st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS; /* File contains invalid characters. */ + } else if (excluded == CSYNC_FILE_EXCLUDE_TRAILING_SPACE) { + st->error_status = CSYNC_STATUS_INDIVIDUAL_TRAILING_SPACE; /* File ends with a trailing space. */ + } else if (excluded == CSYNC_FILE_EXCLUDE_LONG_FILENAME) { + st->error_status = CSYNC_STATUS_INDIVIDUAL_EXCLUDE_LONG_FILENAME; /* File name is too long. */ + } else if (excluded == CSYNC_FILE_EXCLUDE_HIDDEN ) { + st->error_status = CSYNC_STATUS_INDIVIDUAL_EXCLUDE_HIDDEN; + } else if (excluded == CSYNC_FILE_EXCLUDE_STAT_FAILED) { + st->error_status = CSYNC_STATUS_INDIVIDUAL_STAT_FAILED; + } else if (excluded == CSYNC_FILE_EXCLUDE_CONFLICT) { + st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE; + } + } + } + if (st->instruction != CSYNC_INSTRUCTION_NONE + && st->instruction != CSYNC_INSTRUCTION_IGNORE + && st->instruction != CSYNC_INSTRUCTION_UPDATE_METADATA + && type != CSYNC_FTW_TYPE_DIR) { + st->child_modified = 1; + } + ctx->current_fs = st; + + csync_file_stat_free(tmp); + st->inode = fs->inode; + st->mode = fs->mode; + st->size = fs->size; + st->modtime = fs->mtime; + st->type = type; + st->etag = NULL; + if( fs->etag ) { + SAFE_FREE(st->etag); + st->etag = c_strdup(fs->etag); + } + csync_vio_set_file_id(st->file_id, fs->file_id); + if (fs->fields & CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADURL) { + SAFE_FREE(st->directDownloadUrl); + st->directDownloadUrl = c_strdup(fs->directDownloadUrl); + } + if (fs->fields & CSYNC_VIO_FILE_STAT_FIELDS_DIRECTDOWNLOADCOOKIES) { + SAFE_FREE(st->directDownloadCookies); + st->directDownloadCookies = c_strdup(fs->directDownloadCookies); + } + if (fs->fields & CSYNC_VIO_FILE_STAT_FIELDS_PERM) { + strncpy(st->remotePerm, fs->remotePerm, REMOTE_PERM_BUF_SIZE); + } + + // For the remote: propagate the discovered checksum + if (fs->checksumHeader && ctx->current == REMOTE_REPLICA) { + st->checksumHeader = c_strdup(fs->checksumHeader); + } + + st->phash = h; + st->pathlen = len; + memcpy(st->path, (len ? path : ""), len + 1); + + switch (ctx->current) { + case LOCAL_REPLICA: + if (c_rbtree_insert(ctx->local.tree, (void *) st) < 0) { + csync_file_stat_free(st); + ctx->status_code = CSYNC_STATUS_TREE_ERROR; + return -1; + } + break; + case REMOTE_REPLICA: + if (c_rbtree_insert(ctx->remote.tree, (void *) st) < 0) { + csync_file_stat_free(st); + ctx->status_code = CSYNC_STATUS_TREE_ERROR; + return -1; + } + break; + default: + break; + } + CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "file: %s, instruction: %s <<=", st->path, + csync_instruction_str(st->instruction)); + + return 0; +} + +int csync_walker(CSYNC *ctx, const char *file, const csync_vio_file_stat_t *fs, + int flag) { + int rc = -1; + enum csync_ftw_type_e type = CSYNC_FTW_TYPE_SKIP; + csync_file_stat_t *st = NULL; + uint64_t h; + + if (ctx->abort) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Aborted!"); + ctx->status_code = CSYNC_STATUS_ABORTED; + return -1; + } + + switch (flag) { + case CSYNC_FTW_FLAG_FILE: + if (ctx->current == REMOTE_REPLICA) { + if (fs->fields & CSYNC_VIO_FILE_STAT_FIELDS_SIZE) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [file_id=%s size=%" PRIu64 "]", file, fs->file_id, fs->size); + } else { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [file_id=%s size=UNKNOWN]", file, fs->file_id); + } + } else { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [inode=%" PRIu64 " size=%" PRIu64 "]", file, fs->inode, fs->size); + } + type = CSYNC_FTW_TYPE_FILE; + break; + case CSYNC_FTW_FLAG_DIR: /* enter directory */ + if (ctx->current == REMOTE_REPLICA) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "directory: %s [file_id=%s]", file, fs->file_id); + } else { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "directory: %s [inode=%" PRIu64 "]", file, fs->inode); + } + type = CSYNC_FTW_TYPE_DIR; + break; + case CSYNC_FTW_FLAG_NSTAT: /* not statable file */ + /* if file was here before and now is not longer stat-able, still + * add it to the db, otherwise not. */ + h = _hash_of_file( ctx, file ); + if( h == 0 ) { + return 0; + } + st = csync_statedb_get_stat_by_hash(ctx, h); + if( !st ) { + return 0; + } + csync_file_stat_free(st); + st = NULL; + + type = CSYNC_FTW_TYPE_SKIP; + break; + case CSYNC_FTW_FLAG_SLINK: + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "symlink: %s - not supported", file); + type = CSYNC_FTW_TYPE_SLINK; + break; + case CSYNC_FTW_FLAG_DNR: + case CSYNC_FTW_FLAG_DP: + case CSYNC_FTW_FLAG_SLN: + default: + return 0; + break; + } + + rc = _csync_detect_update(ctx, file, fs, type ); + + return rc; +} + +static bool fill_tree_from_db(CSYNC *ctx, const char *uri) +{ + if( csync_statedb_get_below_path(ctx, uri) < 0 ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "StateDB could not be read!"); + return false; + } + + return true; +} + +/* set the current item to an ignored state. + * If the item is set to ignored, the update phase continues, ie. its not a hard error */ +static bool mark_current_item_ignored( CSYNC *ctx, csync_file_stat_t *previous_fs, CSYNC_STATUS status ) +{ + if(!ctx) { + return false; + } + + if (ctx->current_fs) { + ctx->current_fs->instruction = CSYNC_INSTRUCTION_IGNORE; + ctx->current_fs->error_status = status; + /* If a directory has ignored files, put the flag on the parent directory as well */ + if( previous_fs ) { + previous_fs->has_ignored_files = true; + } + return true; + } + return false; +} + +/* File tree walker */ +int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn, + unsigned int depth) { + char *filename = NULL; + char *d_name = NULL; + csync_vio_handle_t *dh = NULL; + csync_vio_file_stat_t *dirent = NULL; + csync_file_stat_t *previous_fs = NULL; + int read_from_db = 0; + int rc = 0; + int res = 0; + + bool do_read_from_db = (ctx->current == REMOTE_REPLICA && ctx->remote.read_from_db); + + if (!depth) { + mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_INDIVIDUAL_TOO_DEEP); + goto done; + } + + read_from_db = ctx->remote.read_from_db; + + // if the etag of this dir is still the same, its content is restored from the + // database. + if( do_read_from_db ) { + if( ! fill_tree_from_db(ctx, uri) ) { + errno = ENOENT; + ctx->status_code = CSYNC_STATUS_OPENDIR_ERROR; + goto error; + } + goto done; + } + + if ((dh = csync_vio_opendir(ctx, uri)) == NULL) { + if (ctx->abort) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Aborted!"); + ctx->status_code = CSYNC_STATUS_ABORTED; + goto error; + } + int asp = 0; + /* permission denied */ + ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_OPENDIR_ERROR); + if (errno == EACCES) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Permission denied."); + if (mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_PERMISSION_DENIED)) { + goto done; + } + } else if(errno == ENOENT) { + asp = asprintf( &ctx->error_string, "%s", uri); + if (asp < 0) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "asprintf failed!"); + } + } + // 403 Forbidden can be sent by the server if the file firewall is active. + // A file or directory should be ignored and sync must continue. See #3490 + else if(errno == ERRNO_FORBIDDEN) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Directory access Forbidden (File Firewall?)"); + if( mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_FORBIDDEN) ) { + goto done; + } + /* if current_fs is not defined here, better throw an error */ + } + // The server usually replies with the custom "503 Storage not available" + // if some path is temporarily unavailable. But in some cases a standard 503 + // is returned too. Thus we can't distinguish the two and will treat any + // 503 as request to ignore the folder. See #3113 #2884. + else if(errno == ERRNO_STORAGE_UNAVAILABLE || errno == ERRNO_SERVICE_UNAVAILABLE) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Storage was not available!"); + if( mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_STORAGE_UNAVAILABLE ) ) { + goto done; + } + /* if current_fs is not defined here, better throw an error */ + } else { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "opendir failed for %s - errno %d", uri, errno); + } + goto error; + } + + while ((dirent = csync_vio_readdir(ctx, dh))) { + int flen; + int flag; + + /* Conversion error */ + if (dirent->name == NULL && dirent->original_name) { + ctx->status_code = CSYNC_STATUS_INVALID_CHARACTERS; + ctx->error_string = dirent->original_name; // take ownership + dirent->original_name = NULL; + goto error; + } + + d_name = dirent->name; + if (d_name == NULL) { + ctx->status_code = CSYNC_STATUS_READDIR_ERROR; + goto error; + } + + /* skip "." and ".." */ + if ( (d_name[0] == '.' && d_name[1] == '\0') + || (d_name[0] == '.' && d_name[1] == '.' && d_name[2] == '\0')) { + csync_vio_file_stat_destroy(dirent); + dirent = NULL; + continue; + } + + if (uri[0] == '\0') { + filename = c_strdup(d_name); + flen = strlen(d_name); + } else { + flen = asprintf(&filename, "%s/%s", uri, d_name); + } + if (flen < 0 || !filename) { + csync_vio_file_stat_destroy(dirent); + dirent = NULL; + ctx->status_code = CSYNC_STATUS_MEMORY_ERROR; + goto error; + } + + /* Only for the local replica we have to stat(), for the remote one we have all data already */ + if (ctx->replica == LOCAL_REPLICA) { + res = csync_vio_stat(ctx, filename, dirent); + } else { + res = 0; + } + + /* if the filename starts with a . we consider it a hidden file + * For windows, the hidden state is also discovered within the vio + * local stat function. + */ + if( d_name[0] == '.' ) { + if (strcmp(".sys.admin#recall#", d_name) != 0) { /* recall file shall not be ignored (#4420) */ + dirent->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN; + } + } + + if( res == 0) { + switch (dirent->type) { + case CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK: + flag = CSYNC_FTW_FLAG_SLINK; + break; + case CSYNC_VIO_FILE_TYPE_DIRECTORY: + flag = CSYNC_FTW_FLAG_DIR; + break; + case CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE: + case CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE: + case CSYNC_VIO_FILE_TYPE_SOCKET: + flag = CSYNC_FTW_FLAG_SPEC; + break; + case CSYNC_VIO_FILE_TYPE_FIFO: + flag = CSYNC_FTW_FLAG_SPEC; + break; + default: + flag = CSYNC_FTW_FLAG_FILE; + break; + }; + } else { + flag = CSYNC_FTW_FLAG_NSTAT; + } + + previous_fs = ctx->current_fs; + + /* Call walker function for each file */ + rc = fn(ctx, filename, dirent, flag); + /* this function may update ctx->current and ctx->read_from_db */ + + if (rc < 0) { + if (CSYNC_STATUS_IS_OK(ctx->status_code)) { + ctx->status_code = CSYNC_STATUS_UPDATE_ERROR; + } + + ctx->current_fs = previous_fs; + goto error; + } + + if (flag == CSYNC_FTW_FLAG_DIR && rc == 0 + && (!ctx->current_fs || ctx->current_fs->instruction != CSYNC_INSTRUCTION_IGNORE)) { + rc = csync_ftw(ctx, filename, fn, depth - 1); + if (rc < 0) { + ctx->current_fs = previous_fs; + goto error; + } + + if (ctx->current_fs && !ctx->current_fs->child_modified + && ctx->current_fs->instruction == CSYNC_INSTRUCTION_EVAL) { + if (ctx->current == REMOTE_REPLICA) { + ctx->current_fs->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; + } else { + ctx->current_fs->instruction = CSYNC_INSTRUCTION_NONE; + } + } + + if (ctx->current_fs && previous_fs && ctx->current_fs->has_ignored_files) { + /* If a directory has ignored files, put the flag on the parent directory as well */ + previous_fs->has_ignored_files = ctx->current_fs->has_ignored_files; + } + } + + if (ctx->current_fs && previous_fs && ctx->current_fs->child_modified) { + /* If a directory has modified files, put the flag on the parent directory as well */ + previous_fs->child_modified = ctx->current_fs->child_modified; + } + + ctx->current_fs = previous_fs; + ctx->remote.read_from_db = read_from_db; + SAFE_FREE(filename); + csync_vio_file_stat_destroy(dirent); + dirent = NULL; + } + + csync_vio_closedir(ctx, dh); + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, " <= Closing walk for %s with read_from_db %d", uri, read_from_db); + +done: + csync_vio_file_stat_destroy(dirent); + SAFE_FREE(filename); + return rc; +error: + ctx->remote.read_from_db = read_from_db; + if (dh != NULL) { + csync_vio_closedir(ctx, dh); + } + SAFE_FREE(filename); + return -1; +} + +/* vim: set ts=8 sw=2 et cindent: */ diff --git a/src/csync/csync_update.h b/src/csync/csync_update.h index 4a7495e3a..d83ee91e8 100644 --- a/src/csync/csync_update.h +++ b/src/csync/csync_update.h @@ -53,7 +53,7 @@ enum csync_ftw_flags_e { }; typedef int (*csync_walker_fn) (CSYNC *ctx, const char *file, - const csync_vio_file_stat_t *fs, enum csync_ftw_flags_e flag); + const csync_vio_file_stat_t *fs, int flag); /** * @brief The walker function to use in the file tree walker. @@ -68,8 +68,7 @@ typedef int (*csync_walker_fn) (CSYNC *ctx, const char *file, * * @return 0 on success, < 0 on error. */ -int csync_walker(CSYNC *ctx, const char *file, const csync_vio_file_stat_t *fs, - enum csync_ftw_flags_e flag); +int csync_walker(CSYNC *ctx, const char *file, const csync_vio_file_stat_t *fs, int flag); /** * @brief The file tree walker. diff --git a/src/csync/csync_util.c b/src/csync/csync_util.c deleted file mode 100644 index 1bc09c35c..000000000 --- a/src/csync/csync_util.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * Copyright (c) 2012-2013 by Klaas Freitag - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config_csync.h" - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include - -#include "c_jhash.h" -#include "csync_util.h" -#include "vio/csync_vio.h" - -#define CSYNC_LOG_CATEGORY_NAME "csync.util" -#include "csync_log.h" -#include "csync_statedb.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_EVAL_RENAME", CSYNC_INSTRUCTION_EVAL_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_TYPE_CHANGE", CSYNC_INSTRUCTION_TYPE_CHANGE }, - { "INSTRUCTION_UPDATE_METADATA", CSYNC_INSTRUCTION_UPDATE_METADATA }, - { 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); -} - -bool (*csync_file_locked_or_open_ext) (const char*) = 0; // filled in by library user -void set_csync_file_locked_or_open_ext(bool (*f) (const char*)); -void set_csync_file_locked_or_open_ext(bool (*f) (const char*)) { - csync_file_locked_or_open_ext = f; -} - -bool csync_file_locked_or_open( const char *dir, const char *fname) { - char *tmp_uri = NULL; - bool ret; - if (!csync_file_locked_or_open_ext) { - return false; - } - if (asprintf(&tmp_uri, "%s/%s", dir, fname) < 0) { - return -1; - } - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "csync_file_locked_or_open %s", tmp_uri); - ret = csync_file_locked_or_open_ext(tmp_uri); - SAFE_FREE(tmp_uri); - return ret; -} - -#ifndef HAVE_TIMEGM -#ifdef _WIN32 -static int is_leap(unsigned y) { - y += 1900; - return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0); -} - -static time_t timegm(struct tm *tm) { - static const unsigned ndays[2][12] = { - {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; - - time_t res = 0; - int i; - - for (i = 70; i < tm->tm_year; ++i) - res += is_leap(i) ? 366 : 365; - - for (i = 0; i < tm->tm_mon; ++i) - res += ndays[is_leap(tm->tm_year)][i]; - res += tm->tm_mday - 1; - res *= 24; - res += tm->tm_hour; - res *= 60; - res += tm->tm_min; - res *= 60; - res += tm->tm_sec; - return res; -} -#else -/* A hopefully portable version of timegm */ -static time_t timegm(struct tm *tm ) { - time_t ret; - char *tz; - - tz = getenv("TZ"); - setenv("TZ", "", 1); - tzset(); - ret = mktime(tm); - if (tz) - setenv("TZ", tz, 1); - else - unsetenv("TZ"); - tzset(); - return ret; -} -#endif /* Platform switch */ -#endif /* HAVE_TIMEGM */ - -#define RFC1123_FORMAT "%3s, %02d %3s %4d %02d:%02d:%02d GMT" -static const char short_months[12][4] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; -/* - * This function is borrowed from libneon's ne_httpdate_parse. - * Unfortunately that one converts to local time but here UTC is - * needed. - * This one uses timegm instead, which returns UTC. - */ -time_t oc_httpdate_parse( const char *date ) { - struct tm gmt; - char wkday[4], mon[4]; - int n; - time_t result = 0; - - memset(&gmt, 0, sizeof(struct tm)); - - /* it goes: Sun, 06 Nov 1994 08:49:37 GMT */ - n = sscanf(date, RFC1123_FORMAT, - wkday, &gmt.tm_mday, mon, &gmt.tm_year, &gmt.tm_hour, - &gmt.tm_min, &gmt.tm_sec); - /* Is it portable to check n==7 here? */ - gmt.tm_year -= 1900; - for (n=0; n<12; n++) - if (strcmp(mon, short_months[n]) == 0) - break; - /* tm_mon comes out as 12 if the month is corrupt, which is desired, - * since the mktime will then fail */ - gmt.tm_mon = n; - gmt.tm_isdst = -1; - result = timegm(&gmt); - return result; -} diff --git a/src/csync/csync_util.cpp b/src/csync/csync_util.cpp new file mode 100644 index 000000000..4fc13a471 --- /dev/null +++ b/src/csync/csync_util.cpp @@ -0,0 +1,214 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * Copyright (c) 2012-2013 by Klaas Freitag + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_csync.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include + +#include "c_jhash.h" +#include "csync_util.h" +#include "vio/csync_vio.h" + +#define CSYNC_LOG_CATEGORY_NAME "csync.util" +#include "csync_log.h" +#include "csync_statedb.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_EVAL_RENAME", CSYNC_INSTRUCTION_EVAL_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_TYPE_CHANGE", CSYNC_INSTRUCTION_TYPE_CHANGE }, + { "INSTRUCTION_UPDATE_METADATA", CSYNC_INSTRUCTION_UPDATE_METADATA }, + { 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); +} + +bool (*csync_file_locked_or_open_ext) (const char*) = 0; // filled in by library user +void set_csync_file_locked_or_open_ext(bool (*f) (const char*)); +void set_csync_file_locked_or_open_ext(bool (*f) (const char*)) { + csync_file_locked_or_open_ext = f; +} + +bool csync_file_locked_or_open( const char *dir, const char *fname) { + char *tmp_uri = NULL; + bool ret; + if (!csync_file_locked_or_open_ext) { + return false; + } + if (asprintf(&tmp_uri, "%s/%s", dir, fname) < 0) { + return -1; + } + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "csync_file_locked_or_open %s", tmp_uri); + ret = csync_file_locked_or_open_ext(tmp_uri); + SAFE_FREE(tmp_uri); + return ret; +} + +#ifndef HAVE_TIMEGM +#ifdef _WIN32 +static int is_leap(unsigned y) { + y += 1900; + return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0); +} + +static time_t timegm(struct tm *tm) { + static const unsigned ndays[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; + + time_t res = 0; + int i; + + for (i = 70; i < tm->tm_year; ++i) + res += is_leap(i) ? 366 : 365; + + for (i = 0; i < tm->tm_mon; ++i) + res += ndays[is_leap(tm->tm_year)][i]; + res += tm->tm_mday - 1; + res *= 24; + res += tm->tm_hour; + res *= 60; + res += tm->tm_min; + res *= 60; + res += tm->tm_sec; + return res; +} +#else +/* A hopefully portable version of timegm */ +static time_t timegm(struct tm *tm ) { + time_t ret; + char *tz; + + tz = getenv("TZ"); + setenv("TZ", "", 1); + tzset(); + ret = mktime(tm); + if (tz) + setenv("TZ", tz, 1); + else + unsetenv("TZ"); + tzset(); + return ret; +} +#endif /* Platform switch */ +#endif /* HAVE_TIMEGM */ + +#define RFC1123_FORMAT "%3s, %02d %3s %4d %02d:%02d:%02d GMT" +static const char short_months[12][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +/* + * This function is borrowed from libneon's ne_httpdate_parse. + * Unfortunately that one converts to local time but here UTC is + * needed. + * This one uses timegm instead, which returns UTC. + */ +time_t oc_httpdate_parse( const char *date ) { + struct tm gmt; + char wkday[4], mon[4]; + int n; + time_t result = 0; + + memset(&gmt, 0, sizeof(struct tm)); + + /* it goes: Sun, 06 Nov 1994 08:49:37 GMT */ + n = sscanf(date, RFC1123_FORMAT, + wkday, &gmt.tm_mday, mon, &gmt.tm_year, &gmt.tm_hour, + &gmt.tm_min, &gmt.tm_sec); + /* Is it portable to check n==7 here? */ + gmt.tm_year -= 1900; + for (n=0; n<12; n++) + if (strcmp(mon, short_months[n]) == 0) + break; + /* tm_mon comes out as 12 if the month is corrupt, which is desired, + * since the mktime will then fail */ + gmt.tm_mon = n; + gmt.tm_isdst = -1; + result = timegm(&gmt); + return result; +} diff --git a/src/csync/csync_version.h.in b/src/csync/csync_version.h.in index 9471734b8..3bf379db9 100644 --- a/src/csync/csync_version.h.in +++ b/src/csync/csync_version.h.in @@ -21,17 +21,9 @@ #ifndef _CSYNC_VERSION_H #define _CSYNC_VERSION_H -#ifdef __cplusplus -extern "C" { -#endif - #define CSYNC_STRINGIFY(s) CSYNC_TOSTRING(s) #define CSYNC_TOSTRING(s) #s #define MIRALL_VERSION @MIRALL_VERSION@ -#ifdef __cplusplus -} -#endif - #endif // _CSYNC_VERSION_H diff --git a/src/csync/std/asprintf.h b/src/csync/std/asprintf.h index d6dd2e859..21693ca89 100644 --- a/src/csync/std/asprintf.h +++ b/src/csync/std/asprintf.h @@ -34,6 +34,10 @@ #ifndef ASPRINTF_H #define ASPRINTF_H 1 +#ifdef __cplusplus +extern "C" { +#endif + #include /** @@ -56,5 +60,9 @@ vasprintf (char **, const char *, va_list); int asprintf (char **, const char *, ...); +#ifdef __cplusplus +} +#endif + #endif #endif diff --git a/src/csync/std/c_alloc.h b/src/csync/std/c_alloc.h index 6eeccd69d..8ff2eb08b 100644 --- a/src/csync/std/c_alloc.h +++ b/src/csync/std/c_alloc.h @@ -34,6 +34,10 @@ #ifndef _C_ALLOC_H #define _C_ALLOC_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include "c_macro.h" @@ -113,4 +117,9 @@ char *c_strndup(const char *str, size_t size); /** * }@ */ + +#ifdef __cplusplus +} +#endif + #endif /* _C_ALLOC_H */ diff --git a/src/csync/std/c_path.h b/src/csync/std/c_path.h index 291b1b8d3..f17c5f480 100644 --- a/src/csync/std/c_path.h +++ b/src/csync/std/c_path.h @@ -32,6 +32,10 @@ #ifndef _C_PATH_H #define _C_PATH_H +#ifdef __cplusplus +extern "C" { +#endif + #include "c_macro.h" #include "c_private.h" @@ -139,4 +143,9 @@ mbchar_t* c_utf8_path_to_locale(const char *str); /** * }@ */ + +#ifdef __cplusplus +} +#endif + #endif /* _C_PATH_H */ diff --git a/src/csync/std/c_rbtree.h b/src/csync/std/c_rbtree.h index 635458fd2..69b4c0d73 100644 --- a/src/csync/std/c_rbtree.h +++ b/src/csync/std/c_rbtree.h @@ -66,6 +66,10 @@ #ifndef _C_RBTREE_H #define _C_RBTREE_H +#ifdef __cplusplus +extern "C" { +#endif + /* Forward declarations */ struct c_rbtree_s; typedef struct c_rbtree_s c_rbtree_t; struct c_rbnode_s; typedef struct c_rbnode_s c_rbnode_t; @@ -306,4 +310,9 @@ int c_rbtree_check_sanity(c_rbtree_t *tree); /** * }@ */ + +#ifdef __cplusplus +} +#endif + #endif /* _C_RBTREE_H */ diff --git a/src/csync/std/c_string.h b/src/csync/std/c_string.h index 304c598a2..4d7d9b599 100644 --- a/src/csync/std/c_string.h +++ b/src/csync/std/c_string.h @@ -32,6 +32,10 @@ #ifndef _C_STR_H #define _C_STR_H +#ifdef __cplusplus +extern "C" { +#endif + #include "c_private.h" #include "c_macro.h" @@ -217,5 +221,10 @@ mbchar_t* c_utf8_string_to_locale(const char *wstr); /** * }@ */ + +#ifdef __cplusplus +} +#endif + #endif /* _C_STR_H */ diff --git a/src/csync/std/c_time.c b/src/csync/std/c_time.c index ad43fed02..561cd4763 100644 --- a/src/csync/std/c_time.c +++ b/src/csync/std/c_time.c @@ -25,10 +25,6 @@ #include "c_path.h" #include "c_time.h" -#ifndef _WIN32 -#include -#endif - struct timespec c_tspecdiff(struct timespec time1, struct timespec time0) { struct timespec ret; int xsec = 0; diff --git a/src/csync/std/c_time.h b/src/csync/std/c_time.h index aa8ef2f16..67e7fcbad 100644 --- a/src/csync/std/c_time.h +++ b/src/csync/std/c_time.h @@ -21,7 +21,15 @@ #ifndef _C_TIME_H #define _C_TIME_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 #include +#else +#include +#endif /** * @brief Calculate time difference @@ -52,4 +60,8 @@ double c_secdiff(struct timespec clock1, struct timespec clock0); int c_utimes(const char *uri, const struct timeval *times); +#ifdef __cplusplus +} +#endif + #endif /* _C_TIME_H */ diff --git a/src/csync/vio/csync_vio.c b/src/csync/vio/csync_vio.c deleted file mode 100644 index 909117836..000000000 --- a/src/csync/vio/csync_vio.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include - -#include "csync_private.h" -#include "csync_util.h" -#include "vio/csync_vio.h" -#include "vio/csync_vio_local.h" -#include "csync_statedb.h" -#include "std/c_jhash.h" - -#define CSYNC_LOG_CATEGORY_NAME "csync.vio.main" - -#include "csync_log.h" - -csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name) { - switch(ctx->replica) { - case REMOTE_REPLICA: - if(ctx->remote.read_from_db) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Read from db flag is true, should not!" ); - } - return ctx->callbacks.remote_opendir_hook(name, ctx->callbacks.vio_userdata); - break; - case LOCAL_REPLICA: - if( ctx->callbacks.update_callback ) { - ctx->callbacks.update_callback(ctx->replica, name, ctx->callbacks.update_callback_userdata); - } - return csync_vio_local_opendir(name); - break; - default: - CSYNC_LOG(CSYNC_LOG_PRIORITY_ALERT, "Invalid replica (%d)", (int)ctx->replica); - break; - } - return NULL; -} - -int csync_vio_closedir(CSYNC *ctx, csync_vio_handle_t *dhandle) { - int rc = -1; - - if (dhandle == NULL) { - errno = EBADF; - return -1; - } - - switch(ctx->replica) { - case REMOTE_REPLICA: - if( ctx->remote.read_from_db ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Remote ReadFromDb is true, should not!"); - } - ctx->callbacks.remote_closedir_hook(dhandle, ctx->callbacks.vio_userdata); - rc = 0; - break; - case LOCAL_REPLICA: - rc = csync_vio_local_closedir(dhandle); - break; - default: - CSYNC_LOG(CSYNC_LOG_PRIORITY_ALERT, "Invalid replica (%d)", (int)ctx->replica); - break; - } - return rc; -} - -csync_vio_file_stat_t *csync_vio_readdir(CSYNC *ctx, csync_vio_handle_t *dhandle) { - switch(ctx->replica) { - case REMOTE_REPLICA: - if( ctx->remote.read_from_db ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Remote readfromdb is true, should not!"); - } - return ctx->callbacks.remote_readdir_hook(dhandle, ctx->callbacks.vio_userdata); - break; - case LOCAL_REPLICA: - return csync_vio_local_readdir(dhandle); - break; - default: - CSYNC_LOG(CSYNC_LOG_PRIORITY_ALERT, "Invalid replica (%d)", (int)ctx->replica); - break; - } - - return NULL; -} - - -int csync_vio_stat(CSYNC *ctx, const char *uri, csync_vio_file_stat_t *buf) { - int rc = -1; - - switch(ctx->replica) { - case REMOTE_REPLICA: - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "ERROR: Cannot call remote stat, not implemented"); - assert(ctx->replica != REMOTE_REPLICA); - break; - case LOCAL_REPLICA: - rc = csync_vio_local_stat(uri, buf); - if (rc < 0) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Local stat failed, errno %d for %s", errno, uri); - } - break; - default: - break; - } - - return rc; -} - -char *csync_vio_get_status_string(CSYNC *ctx) { - if(ctx->error_string) { - return ctx->error_string; - } - return 0; -} diff --git a/src/csync/vio/csync_vio.cpp b/src/csync/vio/csync_vio.cpp new file mode 100644 index 000000000..909117836 --- /dev/null +++ b/src/csync/vio/csync_vio.cpp @@ -0,0 +1,133 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include + +#include "csync_private.h" +#include "csync_util.h" +#include "vio/csync_vio.h" +#include "vio/csync_vio_local.h" +#include "csync_statedb.h" +#include "std/c_jhash.h" + +#define CSYNC_LOG_CATEGORY_NAME "csync.vio.main" + +#include "csync_log.h" + +csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name) { + switch(ctx->replica) { + case REMOTE_REPLICA: + if(ctx->remote.read_from_db) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Read from db flag is true, should not!" ); + } + return ctx->callbacks.remote_opendir_hook(name, ctx->callbacks.vio_userdata); + break; + case LOCAL_REPLICA: + if( ctx->callbacks.update_callback ) { + ctx->callbacks.update_callback(ctx->replica, name, ctx->callbacks.update_callback_userdata); + } + return csync_vio_local_opendir(name); + break; + default: + CSYNC_LOG(CSYNC_LOG_PRIORITY_ALERT, "Invalid replica (%d)", (int)ctx->replica); + break; + } + return NULL; +} + +int csync_vio_closedir(CSYNC *ctx, csync_vio_handle_t *dhandle) { + int rc = -1; + + if (dhandle == NULL) { + errno = EBADF; + return -1; + } + + switch(ctx->replica) { + case REMOTE_REPLICA: + if( ctx->remote.read_from_db ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Remote ReadFromDb is true, should not!"); + } + ctx->callbacks.remote_closedir_hook(dhandle, ctx->callbacks.vio_userdata); + rc = 0; + break; + case LOCAL_REPLICA: + rc = csync_vio_local_closedir(dhandle); + break; + default: + CSYNC_LOG(CSYNC_LOG_PRIORITY_ALERT, "Invalid replica (%d)", (int)ctx->replica); + break; + } + return rc; +} + +csync_vio_file_stat_t *csync_vio_readdir(CSYNC *ctx, csync_vio_handle_t *dhandle) { + switch(ctx->replica) { + case REMOTE_REPLICA: + if( ctx->remote.read_from_db ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Remote readfromdb is true, should not!"); + } + return ctx->callbacks.remote_readdir_hook(dhandle, ctx->callbacks.vio_userdata); + break; + case LOCAL_REPLICA: + return csync_vio_local_readdir(dhandle); + break; + default: + CSYNC_LOG(CSYNC_LOG_PRIORITY_ALERT, "Invalid replica (%d)", (int)ctx->replica); + break; + } + + return NULL; +} + + +int csync_vio_stat(CSYNC *ctx, const char *uri, csync_vio_file_stat_t *buf) { + int rc = -1; + + switch(ctx->replica) { + case REMOTE_REPLICA: + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "ERROR: Cannot call remote stat, not implemented"); + assert(ctx->replica != REMOTE_REPLICA); + break; + case LOCAL_REPLICA: + rc = csync_vio_local_stat(uri, buf); + if (rc < 0) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Local stat failed, errno %d for %s", errno, uri); + } + break; + default: + break; + } + + return rc; +} + +char *csync_vio_get_status_string(CSYNC *ctx) { + if(ctx->error_string) { + return ctx->error_string; + } + return 0; +} diff --git a/src/csync/vio/csync_vio_file_stat.c b/src/csync/vio/csync_vio_file_stat.c deleted file mode 100644 index 8ebb64f33..000000000 --- a/src/csync/vio/csync_vio_file_stat.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "c_lib.h" -#include "csync.h" -#include "csync_log.h" - -csync_vio_file_stat_t *csync_vio_file_stat_new(void) { - csync_vio_file_stat_t *file_stat = (csync_vio_file_stat_t *) c_malloc(sizeof(csync_vio_file_stat_t)); - ZERO_STRUCTP(file_stat); - return file_stat; -} - -csync_vio_file_stat_t* csync_vio_file_stat_copy(csync_vio_file_stat_t *file_stat) { - csync_vio_file_stat_t *file_stat_cpy = csync_vio_file_stat_new(); - memcpy(file_stat_cpy, file_stat, sizeof(csync_vio_file_stat_t)); - if (file_stat_cpy->fields & CSYNC_VIO_FILE_STAT_FIELDS_ETAG) { - file_stat_cpy->etag = c_strdup(file_stat_cpy->etag); - } - if (file_stat_cpy->directDownloadCookies) { - file_stat_cpy->directDownloadCookies = c_strdup(file_stat_cpy->directDownloadCookies); - } - if (file_stat_cpy->directDownloadUrl) { - file_stat_cpy->directDownloadUrl = c_strdup(file_stat_cpy->directDownloadUrl); - } - if (file_stat_cpy->checksumHeader) { - file_stat_cpy->checksumHeader = c_strdup(file_stat_cpy->checksumHeader); - } - file_stat_cpy->name = c_strdup(file_stat_cpy->name); - return file_stat_cpy; -} - -void csync_vio_file_stat_destroy(csync_vio_file_stat_t *file_stat) { - /* FIXME: free rest */ - if (file_stat == NULL) { - return; - } - - if (file_stat->fields & CSYNC_VIO_FILE_STAT_FIELDS_ETAG) { - SAFE_FREE(file_stat->etag); - } - SAFE_FREE(file_stat->directDownloadUrl); - SAFE_FREE(file_stat->directDownloadCookies); - SAFE_FREE(file_stat->name); - SAFE_FREE(file_stat->original_name); - SAFE_FREE(file_stat->checksumHeader); - SAFE_FREE(file_stat); -} - -void csync_vio_file_stat_set_file_id( csync_vio_file_stat_t *dst, const char* src ) { - - csync_vio_set_file_id( dst->file_id, src ); - if( c_streq( dst->file_id, "" )) { - return; - } - dst->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FILE_ID; -} - -void csync_vio_set_file_id( char* dst, const char *src ) { - if( src && dst ) { - if( strlen(src) > FILE_ID_BUF_SIZE ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Ignoring file_id because it is too long: %s", src); - strcpy(dst, ""); - } else { - strcpy(dst, src); - } - } -} diff --git a/src/csync/vio/csync_vio_file_stat.cpp b/src/csync/vio/csync_vio_file_stat.cpp new file mode 100644 index 000000000..8ebb64f33 --- /dev/null +++ b/src/csync/vio/csync_vio_file_stat.cpp @@ -0,0 +1,85 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "c_lib.h" +#include "csync.h" +#include "csync_log.h" + +csync_vio_file_stat_t *csync_vio_file_stat_new(void) { + csync_vio_file_stat_t *file_stat = (csync_vio_file_stat_t *) c_malloc(sizeof(csync_vio_file_stat_t)); + ZERO_STRUCTP(file_stat); + return file_stat; +} + +csync_vio_file_stat_t* csync_vio_file_stat_copy(csync_vio_file_stat_t *file_stat) { + csync_vio_file_stat_t *file_stat_cpy = csync_vio_file_stat_new(); + memcpy(file_stat_cpy, file_stat, sizeof(csync_vio_file_stat_t)); + if (file_stat_cpy->fields & CSYNC_VIO_FILE_STAT_FIELDS_ETAG) { + file_stat_cpy->etag = c_strdup(file_stat_cpy->etag); + } + if (file_stat_cpy->directDownloadCookies) { + file_stat_cpy->directDownloadCookies = c_strdup(file_stat_cpy->directDownloadCookies); + } + if (file_stat_cpy->directDownloadUrl) { + file_stat_cpy->directDownloadUrl = c_strdup(file_stat_cpy->directDownloadUrl); + } + if (file_stat_cpy->checksumHeader) { + file_stat_cpy->checksumHeader = c_strdup(file_stat_cpy->checksumHeader); + } + file_stat_cpy->name = c_strdup(file_stat_cpy->name); + return file_stat_cpy; +} + +void csync_vio_file_stat_destroy(csync_vio_file_stat_t *file_stat) { + /* FIXME: free rest */ + if (file_stat == NULL) { + return; + } + + if (file_stat->fields & CSYNC_VIO_FILE_STAT_FIELDS_ETAG) { + SAFE_FREE(file_stat->etag); + } + SAFE_FREE(file_stat->directDownloadUrl); + SAFE_FREE(file_stat->directDownloadCookies); + SAFE_FREE(file_stat->name); + SAFE_FREE(file_stat->original_name); + SAFE_FREE(file_stat->checksumHeader); + SAFE_FREE(file_stat); +} + +void csync_vio_file_stat_set_file_id( csync_vio_file_stat_t *dst, const char* src ) { + + csync_vio_set_file_id( dst->file_id, src ); + if( c_streq( dst->file_id, "" )) { + return; + } + dst->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FILE_ID; +} + +void csync_vio_set_file_id( char* dst, const char *src ) { + if( src && dst ) { + if( strlen(src) > FILE_ID_BUF_SIZE ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Ignoring file_id because it is too long: %s", src); + strcpy(dst, ""); + } else { + strcpy(dst, src); + } + } +} diff --git a/src/csync/vio/csync_vio_local_unix.c b/src/csync/vio/csync_vio_local_unix.c deleted file mode 100644 index 5d5666688..000000000 --- a/src/csync/vio/csync_vio_local_unix.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * Copyright (c) 2013- by Klaas Freitag - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include - -#include "c_private.h" -#include "c_lib.h" -#include "c_string.h" -#include "csync_util.h" -#include "csync_log.h" -#include "csync_vio.h" - -#include "vio/csync_vio_local.h" - -/* - * directory functions - */ - -typedef struct dhandle_s { - DIR *dh; - char *path; -} dhandle_t; - -csync_vio_handle_t *csync_vio_local_opendir(const char *name) { - dhandle_t *handle = NULL; - mbchar_t *dirname = NULL; - - handle = c_malloc(sizeof(dhandle_t)); - - dirname = c_utf8_path_to_locale(name); - - handle->dh = _topendir( dirname ); - if (handle->dh == NULL) { - c_free_locale_string(dirname); - SAFE_FREE(handle); - return NULL; - } - - handle->path = c_strdup(name); - c_free_locale_string(dirname); - - return (csync_vio_handle_t *) handle; -} - -int csync_vio_local_closedir(csync_vio_handle_t *dhandle) { - dhandle_t *handle = NULL; - int rc = -1; - - if (dhandle == NULL) { - errno = EBADF; - return -1; - } - - handle = (dhandle_t *) dhandle; - rc = _tclosedir(handle->dh); - - SAFE_FREE(handle->path); - SAFE_FREE(handle); - - return rc; -} - -csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) { - - dhandle_t *handle = NULL; - csync_vio_file_stat_t *file_stat = NULL; - - handle = (dhandle_t *) dhandle; - - errno = 0; - file_stat = csync_vio_file_stat_new(); - if (file_stat == NULL) { - goto err; - } - file_stat->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; - - struct _tdirent *dirent = NULL; - - dirent = _treaddir(handle->dh); - if (dirent == NULL) { - goto err; - } - file_stat->name = c_utf8_from_locale(dirent->d_name); - if (file_stat->name == NULL) { - //file_stat->original_name = c_strdup(dirent->d_name); - if (asprintf(&file_stat->original_name, "%s/%s", handle->path, dirent->d_name) < 0) { - goto err; - } - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Invalid characters in file/directory name, please rename: \"%s\" (%s)", - dirent->d_name, handle->path); - } - - /* Check for availability of d_type, see manpage. */ -#if defined(_DIRENT_HAVE_D_TYPE) || defined(__APPLE__) - switch (dirent->d_type) { - case DT_FIFO: - case DT_SOCK: - case DT_CHR: - case DT_BLK: - break; - case DT_DIR: - case DT_REG: - file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; - if (dirent->d_type == DT_DIR) { - file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY; - } else { - file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR; - } - break; - case DT_UNKNOWN: - file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; - file_stat->type = CSYNC_VIO_FILE_TYPE_UNKNOWN; - default: - break; - } -#endif - - return file_stat; - -err: - csync_vio_file_stat_destroy(file_stat); - - return NULL; -} - - -int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) { - csync_stat_t sb; - - mbchar_t *wuri = c_utf8_path_to_locale( uri ); - - if( _tstat(wuri, &sb) < 0) { - c_free_locale_string(wuri); - return -1; - } - - buf->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; - - switch(sb.st_mode & S_IFMT) { - case S_IFBLK: - buf->type = CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE; - break; - case S_IFCHR: - buf->type = CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE; - break; - case S_IFDIR: - buf->type = CSYNC_VIO_FILE_TYPE_DIRECTORY; - break; - case S_IFIFO: - buf->type = CSYNC_VIO_FILE_TYPE_FIFO; - break; - case S_IFREG: - buf->type = CSYNC_VIO_FILE_TYPE_REGULAR; - break; - case S_IFLNK: - buf->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK; - break; - case S_IFSOCK: - buf->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK; - break; - default: - buf->type = CSYNC_VIO_FILE_TYPE_UNKNOWN; - break; - } - buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; - - buf->mode = sb.st_mode; - buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MODE; - - if (buf->type == CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK) { - /* FIXME: handle symlink */ - buf->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK; - } else { - buf->flags = CSYNC_VIO_FILE_FLAGS_NONE; - } -#ifdef __APPLE__ - if (sb.st_flags & UF_HIDDEN) { - buf->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN; - } -#endif - buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS; - - buf->inode = sb.st_ino; - buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE; - - buf->atime = sb.st_atime; - buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME; - - buf->mtime = sb.st_mtime; - buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; - - buf->ctime = sb.st_ctime; - buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME; - - buf->size = sb.st_size; - buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; - - c_free_locale_string(wuri); - return 0; -} diff --git a/src/csync/vio/csync_vio_local_unix.cpp b/src/csync/vio/csync_vio_local_unix.cpp new file mode 100644 index 000000000..40e4794c3 --- /dev/null +++ b/src/csync/vio/csync_vio_local_unix.cpp @@ -0,0 +1,222 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * Copyright (c) 2013- by Klaas Freitag + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +#include "c_private.h" +#include "c_lib.h" +#include "c_string.h" +#include "csync_util.h" +#include "csync_log.h" +#include "csync_vio.h" + +#include "vio/csync_vio_local.h" + +/* + * directory functions + */ + +typedef struct dhandle_s { + DIR *dh; + char *path; +} dhandle_t; + +csync_vio_handle_t *csync_vio_local_opendir(const char *name) { + dhandle_t *handle = NULL; + mbchar_t *dirname = NULL; + + handle = (dhandle_t*)c_malloc(sizeof(dhandle_t)); + + dirname = c_utf8_path_to_locale(name); + + handle->dh = _topendir( dirname ); + if (handle->dh == NULL) { + c_free_locale_string(dirname); + SAFE_FREE(handle); + return NULL; + } + + handle->path = c_strdup(name); + c_free_locale_string(dirname); + + return (csync_vio_handle_t *) handle; +} + +int csync_vio_local_closedir(csync_vio_handle_t *dhandle) { + dhandle_t *handle = NULL; + int rc = -1; + + if (dhandle == NULL) { + errno = EBADF; + return -1; + } + + handle = (dhandle_t *) dhandle; + rc = _tclosedir(handle->dh); + + SAFE_FREE(handle->path); + SAFE_FREE(handle); + + return rc; +} + +csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) { + + dhandle_t *handle = NULL; + csync_vio_file_stat_t *file_stat = NULL; + + handle = (dhandle_t *) dhandle; + struct _tdirent *dirent = NULL; + + errno = 0; + file_stat = csync_vio_file_stat_new(); + if (file_stat == NULL) { + goto err; + } + file_stat->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; + + dirent = _treaddir(handle->dh); + if (dirent == NULL) { + goto err; + } + file_stat->name = c_utf8_from_locale(dirent->d_name); + if (file_stat->name == NULL) { + //file_stat->original_name = c_strdup(dirent->d_name); + if (asprintf(&file_stat->original_name, "%s/%s", handle->path, dirent->d_name) < 0) { + goto err; + } + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Invalid characters in file/directory name, please rename: \"%s\" (%s)", + dirent->d_name, handle->path); + } + + /* Check for availability of d_type, see manpage. */ +#if defined(_DIRENT_HAVE_D_TYPE) || defined(__APPLE__) + switch (dirent->d_type) { + case DT_FIFO: + case DT_SOCK: + case DT_CHR: + case DT_BLK: + break; + case DT_DIR: + case DT_REG: + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; + if (dirent->d_type == DT_DIR) { + file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY; + } else { + file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR; + } + break; + case DT_UNKNOWN: + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; + file_stat->type = CSYNC_VIO_FILE_TYPE_UNKNOWN; + default: + break; + } +#endif + + return file_stat; + +err: + csync_vio_file_stat_destroy(file_stat); + + return NULL; +} + + +int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) { + csync_stat_t sb; + + mbchar_t *wuri = c_utf8_path_to_locale( uri ); + + if( _tstat(wuri, &sb) < 0) { + c_free_locale_string(wuri); + return -1; + } + + buf->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; + + switch(sb.st_mode & S_IFMT) { + case S_IFBLK: + buf->type = CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE; + break; + case S_IFCHR: + buf->type = CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE; + break; + case S_IFDIR: + buf->type = CSYNC_VIO_FILE_TYPE_DIRECTORY; + break; + case S_IFIFO: + buf->type = CSYNC_VIO_FILE_TYPE_FIFO; + break; + case S_IFREG: + buf->type = CSYNC_VIO_FILE_TYPE_REGULAR; + break; + case S_IFLNK: + buf->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK; + break; + case S_IFSOCK: + buf->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK; + break; + default: + buf->type = CSYNC_VIO_FILE_TYPE_UNKNOWN; + break; + } + buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; + + buf->mode = sb.st_mode; + buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MODE; + + if (buf->type == CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK) { + /* FIXME: handle symlink */ + buf->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK; + } else { + buf->flags = CSYNC_VIO_FILE_FLAGS_NONE; + } +#ifdef __APPLE__ + if (sb.st_flags & UF_HIDDEN) { + buf->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN; + } +#endif + buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS; + + buf->inode = sb.st_ino; + buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE; + + buf->atime = sb.st_atime; + buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME; + + buf->mtime = sb.st_mtime; + buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; + + buf->ctime = sb.st_ctime; + buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME; + + buf->size = sb.st_size; + buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; + + c_free_locale_string(wuri); + return 0; +} diff --git a/src/csync/vio/csync_vio_local_win.c b/src/csync/vio/csync_vio_local_win.c deleted file mode 100644 index fc4eea512..000000000 --- a/src/csync/vio/csync_vio_local_win.c +++ /dev/null @@ -1,275 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * Copyright (c) 2013- by Klaas Freitag - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include - -#include "windows.h" - -#include "c_private.h" -#include "c_lib.h" -#include "c_string.h" -#include "csync_util.h" -#include "csync_log.h" -#include "csync_vio.h" - -#include "vio/csync_vio_local.h" - - -/* - * directory functions - */ - -typedef struct dhandle_s { - WIN32_FIND_DATA ffd; - HANDLE hFind; - int firstFind; - char *path; -} dhandle_t; - -csync_vio_handle_t *csync_vio_local_opendir(const char *name) { - dhandle_t *handle = NULL; - mbchar_t *dirname = NULL; - - handle = c_malloc(sizeof(dhandle_t)); - - // the file wildcard has to be attached - int len_name = strlen(name); - if( len_name ) { - char *h = NULL; - - // alloc an enough large buffer to take the name + '/*' + the closing zero. - h = c_malloc(len_name+3); - strncpy( h, name, 1+len_name); - strncat(h, "/*", 2); - - dirname = c_utf8_path_to_locale(h); - SAFE_FREE(h); - } - - if( dirname ) { - handle->hFind = FindFirstFile(dirname, &(handle->ffd)); - } - - if (!dirname || handle->hFind == INVALID_HANDLE_VALUE) { - int retcode = GetLastError(); - if( retcode == ERROR_FILE_NOT_FOUND ) { - errno = ENOENT; - } else { - errno = EACCES; - } - SAFE_FREE(handle); - return NULL; - } - - handle->firstFind = 1; // Set a flag that there first fileinfo is available. - - handle->path = c_strdup(name); - c_free_locale_string(dirname); - - return (csync_vio_handle_t *) handle; -} - -int csync_vio_local_closedir(csync_vio_handle_t *dhandle) { - dhandle_t *handle = NULL; - int rc = -1; - - if (dhandle == NULL) { - errno = EBADF; - return -1; - } - - handle = (dhandle_t *) dhandle; - // FindClose returns non-zero on success - if( FindClose(handle->hFind) != 0 ) { - rc = 0; - } else { - // error case, set errno - errno = EBADF; - } - - SAFE_FREE(handle->path); - SAFE_FREE(handle); - - return rc; -} - - -static time_t FileTimeToUnixTime(FILETIME *filetime, DWORD *remainder) -{ - long long int t = filetime->dwHighDateTime; - t <<= 32; - t += (UINT32)filetime->dwLowDateTime; - t -= 116444736000000000LL; - if (t < 0) - { - if (remainder) *remainder = 9999999 - (-t - 1) % 10000000; - return -1 - ((-t - 1) / 10000000); - } - else - { - if (remainder) *remainder = t % 10000000; - return t / 10000000; - } -} - -csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) { - - dhandle_t *handle = NULL; - csync_vio_file_stat_t *file_stat = NULL; - DWORD rem; - - handle = (dhandle_t *) dhandle; - - errno = 0; - file_stat = csync_vio_file_stat_new(); - if (file_stat == NULL) { - errno = ENOMEM; - goto err; - } - file_stat->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; - - // the win32 functions get the first valid entry with the opendir - // thus we must not jump to next entry if it was the first find. - if( handle->firstFind ) { - handle->firstFind = 0; - } else { - if( FindNextFile(handle->hFind, &(handle->ffd)) == 0 ) { - // might be error, check! - int dwError = GetLastError(); - if (dwError != ERROR_NO_MORE_FILES) { - errno = EACCES; // no more files is fine. Otherwise EACCESS - } - goto err; - } - } - file_stat->name = c_utf8_from_locale(handle->ffd.cFileName); - - file_stat->flags = CSYNC_VIO_FILE_FLAGS_NONE; - file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; - if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - // Detect symlinks, and treat junctions as symlinks too. - if (handle->ffd.dwReserved0 == IO_REPARSE_TAG_SYMLINK - || handle->ffd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) { - file_stat->flags |= CSYNC_VIO_FILE_FLAGS_SYMLINK; - file_stat->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK; - } else { - // The SIS and DEDUP reparse points should be treated as - // regular files. We don't know about the other ones yet, - // but will also treat them normally for now. - file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR; - } - } else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE - || handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE - || handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) { - file_stat->type = CSYNC_VIO_FILE_TYPE_UNKNOWN; - } else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY; - } else { - file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR; - } - - /* Check for the hidden flag */ - if( handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) { - file_stat->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN; - } - - file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS; - file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; - - file_stat->size = (handle->ffd.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + handle->ffd.nFileSizeLow; - file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; - - file_stat->atime = FileTimeToUnixTime(&handle->ffd.ftLastAccessTime, &rem); - file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME; - - file_stat->mtime = FileTimeToUnixTime(&handle->ffd.ftLastWriteTime, &rem); - /* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Local File MTime: %llu", (unsigned long long) buf->mtime ); */ - file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; - - file_stat->ctime = FileTimeToUnixTime(&handle->ffd.ftCreationTime, &rem); - file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME; - - return file_stat; - -err: - SAFE_FREE(file_stat); - - return NULL; -} - - - -int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) { - /* Almost nothing to do since csync_vio_local_readdir already filled up most of the information - But we still need to fetch the file ID. - Possible optimisation: only fetch the file id when we need it (for new files) - */ - - HANDLE h; - BY_HANDLE_FILE_INFORMATION fileInfo; - ULARGE_INTEGER FileIndex; - mbchar_t *wuri = c_utf8_path_to_locale( uri ); - - h = CreateFileW( wuri, 0, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, - NULL ); - if( h == INVALID_HANDLE_VALUE ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_CRIT, "CreateFileW failed on %s", uri ); - errno = GetLastError(); - c_free_locale_string(wuri); - return -1; - } - - if(!GetFileInformationByHandle( h, &fileInfo ) ) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_CRIT, "GetFileInformationByHandle failed on %s", uri ); - errno = GetLastError(); - c_free_locale_string(wuri); - CloseHandle(h); - return -1; - } - - /* Get the Windows file id as an inode replacement. */ - FileIndex.HighPart = fileInfo.nFileIndexHigh; - FileIndex.LowPart = fileInfo.nFileIndexLow; - FileIndex.QuadPart &= 0x0000FFFFFFFFFFFF; - /* printf("Index: %I64i\n", FileIndex.QuadPart); */ - buf->inode = FileIndex.QuadPart; - - if (!(buf->fields & CSYNC_VIO_FILE_STAT_FIELDS_SIZE)) { - buf->size = (fileInfo.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + fileInfo.nFileSizeLow; - buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; - } - if (!(buf->fields & CSYNC_VIO_FILE_STAT_FIELDS_MTIME)) { - DWORD rem; - buf->mtime = FileTimeToUnixTime(&fileInfo.ftLastWriteTime, &rem); - /* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Local File MTime: %llu", (unsigned long long) buf->mtime ); */ - buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; - } - - c_free_locale_string(wuri); - CloseHandle(h); - return 0; -} diff --git a/src/csync/vio/csync_vio_local_win.cpp b/src/csync/vio/csync_vio_local_win.cpp new file mode 100644 index 000000000..e9d2ba566 --- /dev/null +++ b/src/csync/vio/csync_vio_local_win.cpp @@ -0,0 +1,275 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * Copyright (c) 2013- by Klaas Freitag + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include "windows.h" + +#include "c_private.h" +#include "c_lib.h" +#include "c_string.h" +#include "csync_util.h" +#include "csync_log.h" +#include "csync_vio.h" + +#include "vio/csync_vio_local.h" + + +/* + * directory functions + */ + +typedef struct dhandle_s { + WIN32_FIND_DATA ffd; + HANDLE hFind; + int firstFind; + char *path; +} dhandle_t; + +csync_vio_handle_t *csync_vio_local_opendir(const char *name) { + dhandle_t *handle = NULL; + mbchar_t *dirname = NULL; + + handle = (dhandle_t*)c_malloc(sizeof(dhandle_t)); + + // the file wildcard has to be attached + int len_name = strlen(name); + if( len_name ) { + char *h = NULL; + + // alloc an enough large buffer to take the name + '/*' + the closing zero. + h = (char*)c_malloc(len_name+3); + strncpy( h, name, 1+len_name); + strncat(h, "/*", 2); + + dirname = c_utf8_path_to_locale(h); + SAFE_FREE(h); + } + + if( dirname ) { + handle->hFind = FindFirstFile(dirname, &(handle->ffd)); + } + + if (!dirname || handle->hFind == INVALID_HANDLE_VALUE) { + int retcode = GetLastError(); + if( retcode == ERROR_FILE_NOT_FOUND ) { + errno = ENOENT; + } else { + errno = EACCES; + } + SAFE_FREE(handle); + return NULL; + } + + handle->firstFind = 1; // Set a flag that there first fileinfo is available. + + handle->path = c_strdup(name); + c_free_locale_string(dirname); + + return (csync_vio_handle_t *) handle; +} + +int csync_vio_local_closedir(csync_vio_handle_t *dhandle) { + dhandle_t *handle = NULL; + int rc = -1; + + if (dhandle == NULL) { + errno = EBADF; + return -1; + } + + handle = (dhandle_t *) dhandle; + // FindClose returns non-zero on success + if( FindClose(handle->hFind) != 0 ) { + rc = 0; + } else { + // error case, set errno + errno = EBADF; + } + + SAFE_FREE(handle->path); + SAFE_FREE(handle); + + return rc; +} + + +static time_t FileTimeToUnixTime(FILETIME *filetime, DWORD *remainder) +{ + long long int t = filetime->dwHighDateTime; + t <<= 32; + t += (UINT32)filetime->dwLowDateTime; + t -= 116444736000000000LL; + if (t < 0) + { + if (remainder) *remainder = 9999999 - (-t - 1) % 10000000; + return -1 - ((-t - 1) / 10000000); + } + else + { + if (remainder) *remainder = t % 10000000; + return t / 10000000; + } +} + +csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) { + + dhandle_t *handle = NULL; + csync_vio_file_stat_t *file_stat = NULL; + DWORD rem; + + handle = (dhandle_t *) dhandle; + + errno = 0; + file_stat = csync_vio_file_stat_new(); + if (file_stat == NULL) { + errno = ENOMEM; + goto err; + } + file_stat->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; + + // the win32 functions get the first valid entry with the opendir + // thus we must not jump to next entry if it was the first find. + if( handle->firstFind ) { + handle->firstFind = 0; + } else { + if( FindNextFile(handle->hFind, &(handle->ffd)) == 0 ) { + // might be error, check! + int dwError = GetLastError(); + if (dwError != ERROR_NO_MORE_FILES) { + errno = EACCES; // no more files is fine. Otherwise EACCESS + } + goto err; + } + } + file_stat->name = c_utf8_from_locale(handle->ffd.cFileName); + + file_stat->flags = CSYNC_VIO_FILE_FLAGS_NONE; + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; + if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + // Detect symlinks, and treat junctions as symlinks too. + if (handle->ffd.dwReserved0 == IO_REPARSE_TAG_SYMLINK + || handle->ffd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) { + file_stat->flags |= CSYNC_VIO_FILE_FLAGS_SYMLINK; + file_stat->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK; + } else { + // The SIS and DEDUP reparse points should be treated as + // regular files. We don't know about the other ones yet, + // but will also treat them normally for now. + file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR; + } + } else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE + || handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE + || handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) { + file_stat->type = CSYNC_VIO_FILE_TYPE_UNKNOWN; + } else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY; + } else { + file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR; + } + + /* Check for the hidden flag */ + if( handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) { + file_stat->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN; + } + + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS; + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; + + file_stat->size = (handle->ffd.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + handle->ffd.nFileSizeLow; + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; + + file_stat->atime = FileTimeToUnixTime(&handle->ffd.ftLastAccessTime, &rem); + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME; + + file_stat->mtime = FileTimeToUnixTime(&handle->ffd.ftLastWriteTime, &rem); + /* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Local File MTime: %llu", (unsigned long long) buf->mtime ); */ + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; + + file_stat->ctime = FileTimeToUnixTime(&handle->ffd.ftCreationTime, &rem); + file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME; + + return file_stat; + +err: + SAFE_FREE(file_stat); + + return NULL; +} + + + +int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) { + /* Almost nothing to do since csync_vio_local_readdir already filled up most of the information + But we still need to fetch the file ID. + Possible optimisation: only fetch the file id when we need it (for new files) + */ + + HANDLE h; + BY_HANDLE_FILE_INFORMATION fileInfo; + ULARGE_INTEGER FileIndex; + mbchar_t *wuri = c_utf8_path_to_locale( uri ); + + h = CreateFileW( wuri, 0, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + NULL ); + if( h == INVALID_HANDLE_VALUE ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_CRIT, "CreateFileW failed on %s", uri ); + errno = GetLastError(); + c_free_locale_string(wuri); + return -1; + } + + if(!GetFileInformationByHandle( h, &fileInfo ) ) { + CSYNC_LOG(CSYNC_LOG_PRIORITY_CRIT, "GetFileInformationByHandle failed on %s", uri ); + errno = GetLastError(); + c_free_locale_string(wuri); + CloseHandle(h); + return -1; + } + + /* Get the Windows file id as an inode replacement. */ + FileIndex.HighPart = fileInfo.nFileIndexHigh; + FileIndex.LowPart = fileInfo.nFileIndexLow; + FileIndex.QuadPart &= 0x0000FFFFFFFFFFFF; + /* printf("Index: %I64i\n", FileIndex.QuadPart); */ + buf->inode = FileIndex.QuadPart; + + if (!(buf->fields & CSYNC_VIO_FILE_STAT_FIELDS_SIZE)) { + buf->size = (fileInfo.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + fileInfo.nFileSizeLow; + buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; + } + if (!(buf->fields & CSYNC_VIO_FILE_STAT_FIELDS_MTIME)) { + DWORD rem; + buf->mtime = FileTimeToUnixTime(&fileInfo.ftLastWriteTime, &rem); + /* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Local File MTime: %llu", (unsigned long long) buf->mtime ); */ + buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; + } + + c_free_locale_string(wuri); + CloseHandle(h); + return 0; +} diff --git a/src/libsync/excludedfiles.cpp b/src/libsync/excludedfiles.cpp index 074494ff8..289164efb 100644 --- a/src/libsync/excludedfiles.cpp +++ b/src/libsync/excludedfiles.cpp @@ -18,11 +18,9 @@ #include -extern "C" { #include "std/c_string.h" #include "csync.h" #include "csync_exclude.h" -} using namespace OCC; diff --git a/src/libsync/excludedfiles.h b/src/libsync/excludedfiles.h index f8a18cd8d..d090f8aa6 100644 --- a/src/libsync/excludedfiles.h +++ b/src/libsync/excludedfiles.h @@ -20,11 +20,9 @@ #include #include -extern "C" { #include "std/c_string.h" #include "csync.h" #include "csync_exclude.h" // for CSYNC_EXCLUDE_TYPE -} namespace OCC { diff --git a/src/libsync/filesystem.cpp b/src/libsync/filesystem.cpp index 18e6f800f..8299fcc04 100644 --- a/src/libsync/filesystem.cpp +++ b/src/libsync/filesystem.cpp @@ -39,14 +39,11 @@ // We use some internals of csync: extern "C" int c_utimes(const char *, const struct timeval *); -extern "C" void csync_win32_set_file_hidden(const char *file, bool h); -extern "C" { #include "csync.h" #include "vio/csync_vio_local.h" #include "std/c_path.h" #include "std/c_string.h" -} namespace OCC { diff --git a/src/libsync/owncloudpropagator.h b/src/libsync/owncloudpropagator.h index 02145fa19..b9b60034f 100644 --- a/src/libsync/owncloudpropagator.h +++ b/src/libsync/owncloudpropagator.h @@ -25,6 +25,7 @@ #include #include +#include "csync_util.h" #include "syncfileitem.h" #include "syncjournaldb.h" #include "bandwidthmanager.h" @@ -35,8 +36,6 @@ namespace OCC { Q_DECLARE_LOGGING_CATEGORY(lcPropagator) -extern "C" const char *csync_instruction_str(enum csync_instructions_e instr); - /** Free disk space threshold below which syncs will abort and not even start. */ qint64 criticalFreeSpaceLimit(); diff --git a/src/libsync/syncjournaldb.cpp b/src/libsync/syncjournaldb.cpp index 203c14988..e46d35db6 100644 --- a/src/libsync/syncjournaldb.cpp +++ b/src/libsync/syncjournaldb.cpp @@ -21,8 +21,6 @@ #include "ownsql.h" -#include - #include "syncjournaldb.h" #include "syncjournalfilerecord.h" #include "utility.h" diff --git a/test/csync/CMakeLists.txt b/test/csync/CMakeLists.txt index e92c15e2d..a27505d12 100644 --- a/test/csync/CMakeLists.txt +++ b/test/csync/CMakeLists.txt @@ -29,28 +29,28 @@ add_cmocka_test(check_std_c_time std_tests/check_std_c_time.c ${TEST_TARGET_LIBR # csync tests # This will be rewritten soon anyway. -#add_cmocka_test(check_logger log_tests/check_log.c ${TEST_TARGET_LIBRARIES}) +#add_cmocka_test(check_logger log_tests/check_log.cpp ${TEST_TARGET_LIBRARIES}) -add_cmocka_test(check_csync_create csync_tests/check_csync_create.c ${TEST_TARGET_LIBRARIES}) -add_cmocka_test(check_csync_log csync_tests/check_csync_log.c ${TEST_TARGET_LIBRARIES}) -add_cmocka_test(check_csync_exclude csync_tests/check_csync_exclude.c ${TEST_TARGET_LIBRARIES}) -add_cmocka_test(check_csync_statedb_load csync_tests/check_csync_statedb_load.c ${TEST_TARGET_LIBRARIES}) -add_cmocka_test(check_csync_util csync_tests/check_csync_util.c ${TEST_TARGET_LIBRARIES}) -add_cmocka_test(check_csync_misc csync_tests/check_csync_misc.c ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_csync_create csync_tests/check_csync_create.cpp ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_csync_log csync_tests/check_csync_log.cpp ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_csync_exclude csync_tests/check_csync_exclude.cpp ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_csync_statedb_load csync_tests/check_csync_statedb_load.cpp ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_csync_util csync_tests/check_csync_util.cpp ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_csync_misc csync_tests/check_csync_misc.cpp ${TEST_TARGET_LIBRARIES}) # csync tests which require init -add_cmocka_test(check_csync_init csync_tests/check_csync_init.c ${TEST_TARGET_LIBRARIES}) -add_cmocka_test(check_csync_statedb_query csync_tests/check_csync_statedb_query.c ${TEST_TARGET_LIBRARIES}) -add_cmocka_test(check_csync_commit csync_tests/check_csync_commit.c ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_csync_init csync_tests/check_csync_init.cpp ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_csync_statedb_query csync_tests/check_csync_statedb_query.cpp ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_csync_commit csync_tests/check_csync_commit.cpp ${TEST_TARGET_LIBRARIES}) # vio -add_cmocka_test(check_vio_file_stat vio_tests/check_vio_file_stat.c ${TEST_TARGET_LIBRARIES}) -add_cmocka_test(check_vio vio_tests/check_vio.c ${TEST_TARGET_LIBRARIES}) -add_cmocka_test(check_vio_ext vio_tests/check_vio_ext.c ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_vio_file_stat vio_tests/check_vio_file_stat.cpp ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_vio vio_tests/check_vio.cpp ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_vio_ext vio_tests/check_vio_ext.cpp ${TEST_TARGET_LIBRARIES}) # sync -add_cmocka_test(check_csync_update csync_tests/check_csync_update.c ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_csync_update csync_tests/check_csync_update.cpp ${TEST_TARGET_LIBRARIES}) # encoding -add_cmocka_test(check_encoding_functions encoding_tests/check_encoding.c ${TEST_TARGET_LIBRARIES}) +add_cmocka_test(check_encoding_functions encoding_tests/check_encoding.cpp ${TEST_TARGET_LIBRARIES}) diff --git a/test/csync/csync_tests/check_csync_commit.c b/test/csync/csync_tests/check_csync_commit.c deleted file mode 100644 index 16c89bf9e..000000000 --- a/test/csync/csync_tests/check_csync_commit.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include - -#include "torture.h" - -#include "csync_private.h" - -static int setup(void **state) { - CSYNC *csync; - int rc; - - rc = system("mkdir -p /tmp/check_csync1"); - assert_int_equal(rc, 0); - - csync_create(&csync, "/tmp/check_csync1"); - - *state = csync; - - return 0; -} - -static int setup_module(void **state) { - CSYNC *csync; - int rc; - - rc = system("mkdir -p /tmp/check_csync1"); - assert_int_equal(rc, 0); - - csync_create(&csync, "/tmp/check_csync1"); - - csync_init(csync, "foo"); - *state = csync; - - return 0; -} - -static int teardown(void **state) { - CSYNC *csync = *state; - int rc; - - rc = csync_destroy(csync); - - rc = system("rm -rf /tmp/check_csync"); - assert_int_equal(rc, 0); - - rc = system("rm -rf /tmp/check_csync1"); - assert_int_equal(rc, 0); - - *state = NULL; - - return 0; -} - -static void check_csync_commit(void **state) -{ - CSYNC *csync = *state; - int rc; - - rc = csync_commit(csync); - assert_int_equal(rc, 0); - - assert_int_equal(csync->status & CSYNC_STATUS_INIT, 1); -} - -static void check_csync_commit_dummy(void **state) -{ - CSYNC *csync = *state; - int rc; - - rc = csync_commit(csync); - assert_int_equal(rc, 0); - - assert_int_equal(csync->status & CSYNC_STATUS_INIT, 1); - -} - -int torture_run_tests(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(check_csync_commit, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_commit_dummy, setup_module, teardown), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/csync/csync_tests/check_csync_commit.cpp b/test/csync/csync_tests/check_csync_commit.cpp new file mode 100644 index 000000000..b3d77913b --- /dev/null +++ b/test/csync/csync_tests/check_csync_commit.cpp @@ -0,0 +1,103 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "torture.h" + +#include "csync_private.h" + +static int setup(void **state) { + CSYNC *csync; + int rc; + + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + + csync_create(&csync, "/tmp/check_csync1"); + + *state = csync; + + return 0; +} + +static int setup_module(void **state) { + CSYNC *csync; + int rc; + + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + + csync_create(&csync, "/tmp/check_csync1"); + + csync_init(csync, "foo"); + *state = csync; + + return 0; +} + +static int teardown(void **state) { + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_destroy(csync); + + rc = system("rm -rf /tmp/check_csync"); + assert_int_equal(rc, 0); + + rc = system("rm -rf /tmp/check_csync1"); + assert_int_equal(rc, 0); + + *state = NULL; + + return 0; +} + +static void check_csync_commit(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_commit(csync); + assert_int_equal(rc, 0); + + assert_int_equal(csync->status & CSYNC_STATUS_INIT, 1); +} + +static void check_csync_commit_dummy(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_commit(csync); + assert_int_equal(rc, 0); + + assert_int_equal(csync->status & CSYNC_STATUS_INIT, 1); + +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(check_csync_commit, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_commit_dummy, setup_module, teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/test/csync/csync_tests/check_csync_create.c b/test/csync/csync_tests/check_csync_create.c deleted file mode 100644 index 082ef26b8..000000000 --- a/test/csync/csync_tests/check_csync_create.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include -#include - -#include "torture.h" - -#include "csync_private.h" - - -static void check_csync_destroy_null(void **state) -{ - int rc; - - (void) state; /* unused */ - - rc = csync_destroy(NULL); - assert_int_equal(rc, -1); -} - -static void check_csync_create(void **state) -{ - CSYNC *csync; - int rc; - - (void) state; /* unused */ - - csync_create(&csync, "/tmp/csync1"); - - rc = csync_destroy(csync); - assert_int_equal(rc, 0); -} - -int torture_run_tests(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(check_csync_destroy_null), - cmocka_unit_test(check_csync_create), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} - diff --git a/test/csync/csync_tests/check_csync_create.cpp b/test/csync/csync_tests/check_csync_create.cpp new file mode 100644 index 000000000..082ef26b8 --- /dev/null +++ b/test/csync/csync_tests/check_csync_create.cpp @@ -0,0 +1,60 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include + +#include "torture.h" + +#include "csync_private.h" + + +static void check_csync_destroy_null(void **state) +{ + int rc; + + (void) state; /* unused */ + + rc = csync_destroy(NULL); + assert_int_equal(rc, -1); +} + +static void check_csync_create(void **state) +{ + CSYNC *csync; + int rc; + + (void) state; /* unused */ + + csync_create(&csync, "/tmp/csync1"); + + rc = csync_destroy(csync); + assert_int_equal(rc, 0); +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(check_csync_destroy_null), + cmocka_unit_test(check_csync_create), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} + diff --git a/test/csync/csync_tests/check_csync_exclude.c b/test/csync/csync_tests/check_csync_exclude.c deleted file mode 100644 index 1d145b240..000000000 --- a/test/csync/csync_tests/check_csync_exclude.c +++ /dev/null @@ -1,419 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "config_csync.h" -#include -#include -#include - -#include "torture.h" - -#define CSYNC_TEST 1 -#include "csync_exclude.c" - -#define EXCLUDE_LIST_FILE SOURCEDIR"/../../sync-exclude.lst" - -static int setup(void **state) { - CSYNC *csync; - - csync_create(&csync, "/tmp/check_csync1"); - - *state = csync; - return 0; -} - -static int setup_init(void **state) { - CSYNC *csync; - int rc; - - csync_create(&csync, "/tmp/check_csync1"); - - rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes)); - assert_int_equal(rc, 0); - - /* and add some unicode stuff */ - rc = _csync_exclude_add(&(csync->excludes), "*.💩"); - assert_int_equal(rc, 0); - rc = _csync_exclude_add(&(csync->excludes), "пятницы.*"); - assert_int_equal(rc, 0); - rc = _csync_exclude_add(&(csync->excludes), "*/*.out"); - assert_int_equal(rc, 0); - rc = _csync_exclude_add(&(csync->excludes), "latex*/*.run.xml"); - assert_int_equal(rc, 0); - rc = _csync_exclude_add(&(csync->excludes), "latex/*/*.tex.tmp"); - assert_int_equal(rc, 0); - - *state = csync; - return 0; -} - -static int teardown(void **state) { - CSYNC *csync = *state; - int rc; - - rc = csync_destroy(csync); - assert_int_equal(rc, 0); - - rc = system("rm -rf /tmp/check_csync1"); - assert_int_equal(rc, 0); - rc = system("rm -rf /tmp/check_csync2"); - assert_int_equal(rc, 0); - - *state = NULL; - - return 0; -} - -static void check_csync_exclude_add(void **state) -{ - CSYNC *csync = *state; - _csync_exclude_add(&(csync->excludes), "/tmp/check_csync1/*"); - assert_string_equal(csync->excludes->vector[0], "/tmp/check_csync1/*"); -} - -static void check_csync_exclude_load(void **state) -{ - CSYNC *csync = *state; - int rc; - - rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes) ); - assert_int_equal(rc, 0); - - assert_string_equal(csync->excludes->vector[0], "*~"); - assert_int_not_equal(csync->excludes->count, 0); -} - -static void check_csync_excluded(void **state) -{ - CSYNC *csync = *state; - int rc; - - rc = csync_excluded_no_ctx(csync->excludes, "", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, "/", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - rc = csync_excluded_no_ctx(csync->excludes, "krawel_krawel", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, ".kde/share/config/kwin.eventsrc", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, ".directory/cache-maximegalon/cache1.txt", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - rc = csync_excluded_no_ctx(csync->excludes, "mozilla/.directory", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - /* - * Test for patterns in subdirs. '.beagle' is defined as a pattern and has - * to be found in top dir as well as in directories underneath. - */ - rc = csync_excluded_no_ctx(csync->excludes, ".apdisk", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - rc = csync_excluded_no_ctx(csync->excludes, "foo/.apdisk", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - rc = csync_excluded_no_ctx(csync->excludes, "foo/bar/.apdisk", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_no_ctx(csync->excludes, ".java", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - /* Files in the ignored dir .java will also be ignored. */ - rc = csync_excluded_no_ctx(csync->excludes, ".apdisk/totally_amazing.jar", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - /* and also in subdirs */ - rc = csync_excluded_no_ctx(csync->excludes, "projects/.apdisk/totally_amazing.jar", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - /* csync-journal is ignored in general silently. */ - rc = csync_excluded_no_ctx(csync->excludes, ".csync_journal.db", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, ".csync_journal.db.ctmp", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, "subdir/.csync_journal.db", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - - /* also the new form of the database name */ - rc = csync_excluded_no_ctx(csync->excludes, "._sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, "._sync_5bdd60bdfcfa.db.ctmp", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, "._sync_5bdd60bdfcfa.db-shm", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, "subdir/._sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - - rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db.ctmp", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db-shm", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, "subdir/.sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); - - - /* pattern ]*.directory - ignore and remove */ - rc = csync_excluded_no_ctx(csync->excludes, "my.~directory", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_AND_REMOVE); - - rc = csync_excluded_no_ctx(csync->excludes, "/a_folder/my.~directory", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_AND_REMOVE); - - /* Not excluded because the pattern .netscape/cache requires directory. */ - rc = csync_excluded_no_ctx(csync->excludes, ".netscape/cache", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - /* Not excluded */ - rc = csync_excluded_no_ctx(csync->excludes, "unicode/中文.hé", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - /* excluded */ - rc = csync_excluded_no_ctx(csync->excludes, "unicode/пятницы.txt", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - rc = csync_excluded_no_ctx(csync->excludes, "unicode/中文.💩", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - /* path wildcards */ - rc = csync_excluded_no_ctx(csync->excludes, "foobar/my_manuscript.out", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_no_ctx(csync->excludes, "latex_tmp/my_manuscript.run.xml", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_no_ctx(csync->excludes, "word_tmp/my_manuscript.run.xml", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - rc = csync_excluded_no_ctx(csync->excludes, "latex/my_manuscript.tex.tmp", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - rc = csync_excluded_no_ctx(csync->excludes, "latex/songbook/my_manuscript.tex.tmp", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - -#ifdef _WIN32 - rc = csync_excluded_no_ctx(csync->excludes, "file_trailing_space ", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_TRAILING_SPACE); - - rc = csync_excluded_no_ctx(csync->excludes, "file_trailing_dot.", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_INVALID_CHAR); - - rc = csync_excluded_no_ctx(csync->excludes, "AUX", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_INVALID_CHAR); - - rc = csync_excluded_no_ctx(csync->excludes, "file_invalid_char<", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_INVALID_CHAR); -#endif -} - -static void check_csync_excluded_traversal(void **state) -{ - CSYNC *csync = *state; - int rc; - - _csync_exclude_add( &(csync->excludes), "/exclude" ); - - /* Check toplevel dir, the pattern only works for toplevel dir. */ - rc = csync_excluded_traversal(csync->excludes, "/exclude", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_traversal(csync->excludes, "/foo/exclude", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - /* check for a file called exclude. Must still work */ - rc = csync_excluded_traversal(csync->excludes, "/exclude", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_traversal(csync->excludes, "/foo/exclude", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - /* Add an exclude for directories only: excl/ */ - _csync_exclude_add( &(csync->excludes), "excl/" ); - rc = csync_excluded_traversal(csync->excludes, "/excl", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_traversal(csync->excludes, "meep/excl", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_traversal(csync->excludes, "meep/excl/file", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); // because leading dirs aren't checked! - - rc = csync_excluded_traversal(csync->excludes, "/excl", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - _csync_exclude_add(&csync->excludes, "/excludepath/withsubdir"); - - rc = csync_excluded_traversal(csync->excludes, "/excludepath/withsubdir", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_traversal(csync->excludes, "/excludepath/withsubdir", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_traversal(csync->excludes, "/excludepath/withsubdir2", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - rc = csync_excluded_traversal(csync->excludes, "/excludepath/withsubdir/foo", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); // because leading dirs aren't checked! -} - -static void check_csync_pathes(void **state) -{ - CSYNC *csync = *state; - int rc; - - _csync_exclude_add( &(csync->excludes), "/exclude" ); - - /* Check toplevel dir, the pattern only works for toplevel dir. */ - rc = csync_excluded_no_ctx(csync->excludes, "/exclude", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_no_ctx(csync->excludes, "/foo/exclude", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - /* check for a file called exclude. Must still work */ - rc = csync_excluded_no_ctx(csync->excludes, "/exclude", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_no_ctx(csync->excludes, "/foo/exclude", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - /* Add an exclude for directories only: excl/ */ - _csync_exclude_add( &(csync->excludes), "excl/" ); - rc = csync_excluded_no_ctx(csync->excludes, "/excl", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_no_ctx(csync->excludes, "meep/excl", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_no_ctx(csync->excludes, "meep/excl/file", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_no_ctx(csync->excludes, "/excl", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - _csync_exclude_add(&csync->excludes, "/excludepath/withsubdir"); - - rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir", CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - - rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir2", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - - rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir/foo", CSYNC_FTW_TYPE_DIR); - assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); -} - -static void check_csync_is_windows_reserved_word() { - assert_true(csync_is_windows_reserved_word("CON")); - assert_true(csync_is_windows_reserved_word("con")); - assert_true(csync_is_windows_reserved_word("CON.")); - assert_true(csync_is_windows_reserved_word("con.")); - assert_true(csync_is_windows_reserved_word("CON.ference")); - assert_false(csync_is_windows_reserved_word("CONference")); - assert_false(csync_is_windows_reserved_word("conference")); - assert_false(csync_is_windows_reserved_word("conf.erence")); - assert_false(csync_is_windows_reserved_word("co")); - assert_true(csync_is_windows_reserved_word("A:")); - assert_true(csync_is_windows_reserved_word("a:")); - assert_true(csync_is_windows_reserved_word("z:")); - assert_true(csync_is_windows_reserved_word("Z:")); - assert_true(csync_is_windows_reserved_word("M:")); - assert_true(csync_is_windows_reserved_word("m:")); -} - -static void check_csync_excluded_performance(void **state) -{ - CSYNC *csync = *state; - - const int N = 10000; - int totalRc = 0; - int i = 0; - - // Being able to use QElapsedTimer for measurement would be nice... - { - struct timeval before, after; - gettimeofday(&before, 0); - - for (i = 0; i < N; ++i) { - totalRc += csync_excluded_no_ctx(csync->excludes, "/this/is/quite/a/long/path/with/many/components", CSYNC_FTW_TYPE_DIR); - totalRc += csync_excluded_no_ctx(csync->excludes, "/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/29", CSYNC_FTW_TYPE_FILE); - } - assert_int_equal(totalRc, CSYNC_NOT_EXCLUDED); // mainly to avoid optimization - - gettimeofday(&after, 0); - - const double total = (after.tv_sec - before.tv_sec) - + (after.tv_usec - before.tv_usec) / 1.0e6; - const double perCallMs = total / 2 / N * 1000; - printf("csync_excluded: %f ms per call\n", perCallMs); - } - - { - struct timeval before, after; - gettimeofday(&before, 0); - - for (i = 0; i < N; ++i) { - totalRc += csync_excluded_traversal(csync->excludes, "/this/is/quite/a/long/path/with/many/components", CSYNC_FTW_TYPE_DIR); - totalRc += csync_excluded_traversal(csync->excludes, "/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/29", CSYNC_FTW_TYPE_FILE); - } - assert_int_equal(totalRc, CSYNC_NOT_EXCLUDED); // mainly to avoid optimization - - gettimeofday(&after, 0); - - const double total = (after.tv_sec - before.tv_sec) - + (after.tv_usec - before.tv_usec) / 1.0e6; - const double perCallMs = total / 2 / N * 1000; - printf("csync_excluded_traversal: %f ms per call\n", perCallMs); - } -} - -static void check_csync_exclude_expand_escapes(void **state) -{ - (void)state; - - const char *str = csync_exclude_expand_escapes( - "keep \\' \\\" \\? \\\\ \\a \\b \\f \\n \\r \\t \\v \\z"); - assert_true(0 == strcmp( - str, "keep ' \" ? \\ \a \b \f \n \r \t \v \\z")); - SAFE_FREE(str); - - str = csync_exclude_expand_escapes(""); - assert_true(0 == strcmp(str, "")); - SAFE_FREE(str); - - str = csync_exclude_expand_escapes("\\"); - assert_true(0 == strcmp(str, "\\")); - SAFE_FREE(str); -} - -int torture_run_tests(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(check_csync_exclude_add, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_exclude_load, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_excluded, setup_init, teardown), - cmocka_unit_test_setup_teardown(check_csync_excluded_traversal, setup_init, teardown), - cmocka_unit_test_setup_teardown(check_csync_pathes, setup_init, teardown), - cmocka_unit_test_setup_teardown(check_csync_is_windows_reserved_word, setup_init, teardown), - cmocka_unit_test_setup_teardown(check_csync_excluded_performance, setup_init, teardown), - cmocka_unit_test(check_csync_exclude_expand_escapes), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/csync/csync_tests/check_csync_exclude.cpp b/test/csync/csync_tests/check_csync_exclude.cpp new file mode 100644 index 000000000..5841eddf0 --- /dev/null +++ b/test/csync/csync_tests/check_csync_exclude.cpp @@ -0,0 +1,419 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "config_csync.h" +#include +#include +#include + +#include "torture.h" + +#define CSYNC_TEST 1 +#include "csync_exclude.cpp" + +#define EXCLUDE_LIST_FILE SOURCEDIR"/../../sync-exclude.lst" + +static int setup(void **state) { + CSYNC *csync; + + csync_create(&csync, "/tmp/check_csync1"); + + *state = csync; + return 0; +} + +static int setup_init(void **state) { + CSYNC *csync; + int rc; + + csync_create(&csync, "/tmp/check_csync1"); + + rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes)); + assert_int_equal(rc, 0); + + /* and add some unicode stuff */ + rc = _csync_exclude_add(&(csync->excludes), "*.💩"); + assert_int_equal(rc, 0); + rc = _csync_exclude_add(&(csync->excludes), "пятницы.*"); + assert_int_equal(rc, 0); + rc = _csync_exclude_add(&(csync->excludes), "*/*.out"); + assert_int_equal(rc, 0); + rc = _csync_exclude_add(&(csync->excludes), "latex*/*.run.xml"); + assert_int_equal(rc, 0); + rc = _csync_exclude_add(&(csync->excludes), "latex/*/*.tex.tmp"); + assert_int_equal(rc, 0); + + *state = csync; + return 0; +} + +static int teardown(void **state) { + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_destroy(csync); + assert_int_equal(rc, 0); + + rc = system("rm -rf /tmp/check_csync1"); + assert_int_equal(rc, 0); + rc = system("rm -rf /tmp/check_csync2"); + assert_int_equal(rc, 0); + + *state = NULL; + + return 0; +} + +static void check_csync_exclude_add(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + _csync_exclude_add(&(csync->excludes), "/tmp/check_csync1/*"); + assert_string_equal(csync->excludes->vector[0], "/tmp/check_csync1/*"); +} + +static void check_csync_exclude_load(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes) ); + assert_int_equal(rc, 0); + + assert_string_equal(csync->excludes->vector[0], "*~"); + assert_int_not_equal(csync->excludes->count, 0); +} + +static void check_csync_excluded(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_excluded_no_ctx(csync->excludes, "", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, "/", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + rc = csync_excluded_no_ctx(csync->excludes, "krawel_krawel", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, ".kde/share/config/kwin.eventsrc", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, ".directory/cache-maximegalon/cache1.txt", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + rc = csync_excluded_no_ctx(csync->excludes, "mozilla/.directory", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + /* + * Test for patterns in subdirs. '.beagle' is defined as a pattern and has + * to be found in top dir as well as in directories underneath. + */ + rc = csync_excluded_no_ctx(csync->excludes, ".apdisk", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + rc = csync_excluded_no_ctx(csync->excludes, "foo/.apdisk", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + rc = csync_excluded_no_ctx(csync->excludes, "foo/bar/.apdisk", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_no_ctx(csync->excludes, ".java", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + /* Files in the ignored dir .java will also be ignored. */ + rc = csync_excluded_no_ctx(csync->excludes, ".apdisk/totally_amazing.jar", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + /* and also in subdirs */ + rc = csync_excluded_no_ctx(csync->excludes, "projects/.apdisk/totally_amazing.jar", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + /* csync-journal is ignored in general silently. */ + rc = csync_excluded_no_ctx(csync->excludes, ".csync_journal.db", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, ".csync_journal.db.ctmp", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, "subdir/.csync_journal.db", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + + /* also the new form of the database name */ + rc = csync_excluded_no_ctx(csync->excludes, "._sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, "._sync_5bdd60bdfcfa.db.ctmp", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, "._sync_5bdd60bdfcfa.db-shm", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, "subdir/._sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + + rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db.ctmp", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db-shm", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + rc = csync_excluded_no_ctx(csync->excludes, "subdir/.sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED); + + + /* pattern ]*.directory - ignore and remove */ + rc = csync_excluded_no_ctx(csync->excludes, "my.~directory", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_AND_REMOVE); + + rc = csync_excluded_no_ctx(csync->excludes, "/a_folder/my.~directory", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_AND_REMOVE); + + /* Not excluded because the pattern .netscape/cache requires directory. */ + rc = csync_excluded_no_ctx(csync->excludes, ".netscape/cache", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + /* Not excluded */ + rc = csync_excluded_no_ctx(csync->excludes, "unicode/中文.hé", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + /* excluded */ + rc = csync_excluded_no_ctx(csync->excludes, "unicode/пятницы.txt", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + rc = csync_excluded_no_ctx(csync->excludes, "unicode/中文.💩", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + /* path wildcards */ + rc = csync_excluded_no_ctx(csync->excludes, "foobar/my_manuscript.out", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_no_ctx(csync->excludes, "latex_tmp/my_manuscript.run.xml", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_no_ctx(csync->excludes, "word_tmp/my_manuscript.run.xml", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + rc = csync_excluded_no_ctx(csync->excludes, "latex/my_manuscript.tex.tmp", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + rc = csync_excluded_no_ctx(csync->excludes, "latex/songbook/my_manuscript.tex.tmp", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + +#ifdef _WIN32 + rc = csync_excluded_no_ctx(csync->excludes, "file_trailing_space ", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_TRAILING_SPACE); + + rc = csync_excluded_no_ctx(csync->excludes, "file_trailing_dot.", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_INVALID_CHAR); + + rc = csync_excluded_no_ctx(csync->excludes, "AUX", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_INVALID_CHAR); + + rc = csync_excluded_no_ctx(csync->excludes, "file_invalid_char<", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_INVALID_CHAR); +#endif +} + +static void check_csync_excluded_traversal(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + _csync_exclude_add( &(csync->excludes), "/exclude" ); + + /* Check toplevel dir, the pattern only works for toplevel dir. */ + rc = csync_excluded_traversal(csync->excludes, "/exclude", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_traversal(csync->excludes, "/foo/exclude", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + /* check for a file called exclude. Must still work */ + rc = csync_excluded_traversal(csync->excludes, "/exclude", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_traversal(csync->excludes, "/foo/exclude", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + /* Add an exclude for directories only: excl/ */ + _csync_exclude_add( &(csync->excludes), "excl/" ); + rc = csync_excluded_traversal(csync->excludes, "/excl", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_traversal(csync->excludes, "meep/excl", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_traversal(csync->excludes, "meep/excl/file", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); // because leading dirs aren't checked! + + rc = csync_excluded_traversal(csync->excludes, "/excl", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + _csync_exclude_add(&csync->excludes, "/excludepath/withsubdir"); + + rc = csync_excluded_traversal(csync->excludes, "/excludepath/withsubdir", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_traversal(csync->excludes, "/excludepath/withsubdir", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_traversal(csync->excludes, "/excludepath/withsubdir2", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + rc = csync_excluded_traversal(csync->excludes, "/excludepath/withsubdir/foo", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); // because leading dirs aren't checked! +} + +static void check_csync_pathes(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + _csync_exclude_add( &(csync->excludes), "/exclude" ); + + /* Check toplevel dir, the pattern only works for toplevel dir. */ + rc = csync_excluded_no_ctx(csync->excludes, "/exclude", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_no_ctx(csync->excludes, "/foo/exclude", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + /* check for a file called exclude. Must still work */ + rc = csync_excluded_no_ctx(csync->excludes, "/exclude", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_no_ctx(csync->excludes, "/foo/exclude", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + /* Add an exclude for directories only: excl/ */ + _csync_exclude_add( &(csync->excludes), "excl/" ); + rc = csync_excluded_no_ctx(csync->excludes, "/excl", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_no_ctx(csync->excludes, "meep/excl", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_no_ctx(csync->excludes, "meep/excl/file", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_no_ctx(csync->excludes, "/excl", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + _csync_exclude_add(&csync->excludes, "/excludepath/withsubdir"); + + rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir", CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); + + rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir2", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_NOT_EXCLUDED); + + rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir/foo", CSYNC_FTW_TYPE_DIR); + assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); +} + +static void check_csync_is_windows_reserved_word(void **) { + assert_true(csync_is_windows_reserved_word("CON")); + assert_true(csync_is_windows_reserved_word("con")); + assert_true(csync_is_windows_reserved_word("CON.")); + assert_true(csync_is_windows_reserved_word("con.")); + assert_true(csync_is_windows_reserved_word("CON.ference")); + assert_false(csync_is_windows_reserved_word("CONference")); + assert_false(csync_is_windows_reserved_word("conference")); + assert_false(csync_is_windows_reserved_word("conf.erence")); + assert_false(csync_is_windows_reserved_word("co")); + assert_true(csync_is_windows_reserved_word("A:")); + assert_true(csync_is_windows_reserved_word("a:")); + assert_true(csync_is_windows_reserved_word("z:")); + assert_true(csync_is_windows_reserved_word("Z:")); + assert_true(csync_is_windows_reserved_word("M:")); + assert_true(csync_is_windows_reserved_word("m:")); +} + +static void check_csync_excluded_performance(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + + const int N = 10000; + int totalRc = 0; + int i = 0; + + // Being able to use QElapsedTimer for measurement would be nice... + { + struct timeval before, after; + gettimeofday(&before, 0); + + for (i = 0; i < N; ++i) { + totalRc += csync_excluded_no_ctx(csync->excludes, "/this/is/quite/a/long/path/with/many/components", CSYNC_FTW_TYPE_DIR); + totalRc += csync_excluded_no_ctx(csync->excludes, "/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/29", CSYNC_FTW_TYPE_FILE); + } + assert_int_equal(totalRc, CSYNC_NOT_EXCLUDED); // mainly to avoid optimization + + gettimeofday(&after, 0); + + const double total = (after.tv_sec - before.tv_sec) + + (after.tv_usec - before.tv_usec) / 1.0e6; + const double perCallMs = total / 2 / N * 1000; + printf("csync_excluded: %f ms per call\n", perCallMs); + } + + { + struct timeval before, after; + gettimeofday(&before, 0); + + for (i = 0; i < N; ++i) { + totalRc += csync_excluded_traversal(csync->excludes, "/this/is/quite/a/long/path/with/many/components", CSYNC_FTW_TYPE_DIR); + totalRc += csync_excluded_traversal(csync->excludes, "/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/29", CSYNC_FTW_TYPE_FILE); + } + assert_int_equal(totalRc, CSYNC_NOT_EXCLUDED); // mainly to avoid optimization + + gettimeofday(&after, 0); + + const double total = (after.tv_sec - before.tv_sec) + + (after.tv_usec - before.tv_usec) / 1.0e6; + const double perCallMs = total / 2 / N * 1000; + printf("csync_excluded_traversal: %f ms per call\n", perCallMs); + } +} + +static void check_csync_exclude_expand_escapes(void **state) +{ + (void)state; + + const char *str = csync_exclude_expand_escapes( + "keep \\' \\\" \\? \\\\ \\a \\b \\f \\n \\r \\t \\v \\z"); + assert_true(0 == strcmp( + str, "keep ' \" ? \\ \a \b \f \n \r \t \v \\z")); + SAFE_FREE(str); + + str = csync_exclude_expand_escapes(""); + assert_true(0 == strcmp(str, "")); + SAFE_FREE(str); + + str = csync_exclude_expand_escapes("\\"); + assert_true(0 == strcmp(str, "\\")); + SAFE_FREE(str); +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(check_csync_exclude_add, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_exclude_load, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_excluded, setup_init, teardown), + cmocka_unit_test_setup_teardown(check_csync_excluded_traversal, setup_init, teardown), + cmocka_unit_test_setup_teardown(check_csync_pathes, setup_init, teardown), + cmocka_unit_test_setup_teardown(check_csync_is_windows_reserved_word, setup_init, teardown), + cmocka_unit_test_setup_teardown(check_csync_excluded_performance, setup_init, teardown), + cmocka_unit_test(check_csync_exclude_expand_escapes), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/test/csync/csync_tests/check_csync_init.c b/test/csync/csync_tests/check_csync_init.c deleted file mode 100644 index 41876d638..000000000 --- a/test/csync/csync_tests/check_csync_init.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include - -#include "torture.h" - -#include "csync_private.h" - -static int setup(void **state) { - CSYNC *csync; - int rc; - - rc = system("mkdir -p /tmp/check_csync1"); - assert_int_equal(rc, 0); - - csync_create(&csync, "/tmp/check_csync1"); - - *state = csync; - return 0; -} - -static int setup_module(void **state) { - CSYNC *csync; - int rc; - - rc = system("mkdir -p /tmp/check_csync1"); - assert_int_equal(rc, 0); - - csync_create(&csync, "/tmp/check_csync1"); - - *state = csync; - return 0; -} - -static int teardown(void **state) { - CSYNC *csync = *state; - int rc; - - rc = csync_destroy(csync); - - rc = system("rm -rf /tmp/check_csync"); - assert_int_equal(rc, 0); - - rc = system("rm -rf /tmp/check_csync1"); - assert_int_equal(rc, 0); - - *state = NULL; - - return 0; -} - -static void check_csync_init(void **state) -{ - CSYNC *csync = *state; - - csync_init(csync, ""); - - assert_int_equal(csync->status & CSYNC_STATUS_INIT, 1); - -} - -int torture_run_tests(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(check_csync_init, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_init, setup_module, teardown), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} - diff --git a/test/csync/csync_tests/check_csync_init.cpp b/test/csync/csync_tests/check_csync_init.cpp new file mode 100644 index 000000000..c0b4cdd6b --- /dev/null +++ b/test/csync/csync_tests/check_csync_init.cpp @@ -0,0 +1,88 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "torture.h" + +#include "csync_private.h" + +static int setup(void **state) { + CSYNC *csync; + int rc; + + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + + csync_create(&csync, "/tmp/check_csync1"); + + *state = csync; + return 0; +} + +static int setup_module(void **state) { + CSYNC *csync; + int rc; + + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + + csync_create(&csync, "/tmp/check_csync1"); + + *state = csync; + return 0; +} + +static int teardown(void **state) { + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_destroy(csync); + + rc = system("rm -rf /tmp/check_csync"); + assert_int_equal(rc, 0); + + rc = system("rm -rf /tmp/check_csync1"); + assert_int_equal(rc, 0); + + *state = NULL; + + return 0; +} + +static void check_csync_init(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + + csync_init(csync, ""); + + assert_int_equal(csync->status & CSYNC_STATUS_INIT, 1); + +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(check_csync_init, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_init, setup_module, teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} + diff --git a/test/csync/csync_tests/check_csync_log.c b/test/csync/csync_tests/check_csync_log.c deleted file mode 100644 index 9523d9627..000000000 --- a/test/csync/csync_tests/check_csync_log.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include -#include - -#include "torture.h" - -#include "csync.h" -#include "csync_log.c" -#include "c_private.h" - -static int setup(void **state) { - CSYNC *csync; - int rc; - - rc = system("mkdir -p /tmp/check_csync1"); - assert_int_equal(rc, 0); - - csync_create(&csync, "/tmp/check_csync1"); - - *state = csync; - - return 0; -} - -static int teardown(void **state) { - CSYNC *csync = *state; - int rc; - - rc = csync_destroy(csync); - - rc = system("rm -rf /tmp/check_csync"); - assert_int_equal(rc, 0); - - rc = system("rm -rf /tmp/check_csync1"); - assert_int_equal(rc, 0); - - *state = NULL; - - return 0; -} - -static void check_log_callback(int verbosity, - const char *function, - const char *buffer) -{ - int rc; - - assert_true(verbosity >= 0); - assert_non_null(function); - assert_false(function[0] == '\0'); - assert_non_null(buffer); - assert_false(buffer[0] == '\0'); - - rc = system("touch /tmp/check_csync1/cb_called"); - assert_int_equal(rc, 0); -} - -static void check_set_log_level(void **state) -{ - int rc; - - (void) state; - - rc = csync_set_log_level(-5); - assert_int_equal(rc, -1); - - rc = csync_set_log_level(5); - assert_int_equal(rc, 0); - - rc = csync_get_log_level(); - assert_int_equal(rc, 5); -} - -static void check_set_auth_callback(void **state) -{ - csync_log_callback log_fn; - int rc; - - (void) state; - - rc = csync_set_log_callback(NULL); - assert_int_equal(rc, -1); - - rc = csync_set_log_callback(check_log_callback); - assert_int_equal(rc, 0); - - log_fn = csync_get_log_callback(); - assert_non_null(log_fn); - assert_true(log_fn == &check_log_callback); -} - -static void check_logging(void **state) -{ - int rc; - csync_stat_t sb; - mbchar_t *path; - path = c_utf8_path_to_locale("/tmp/check_csync1/cb_called"); - - (void) state; /* unused */ - - assert_non_null(path); - - rc = csync_set_log_level(1); - assert_int_equal(rc, 0); - - rc = csync_set_log_callback(check_log_callback); - assert_int_equal(rc, 0); - - csync_log(1, __func__, "rc = %d", rc); - - rc = _tstat(path, &sb); - - c_free_locale_string(path); - - assert_int_equal(rc, 0); -} - -int torture_run_tests(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(check_set_log_level), - cmocka_unit_test(check_set_auth_callback), - cmocka_unit_test_setup_teardown(check_logging, setup, teardown), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/csync/csync_tests/check_csync_log.cpp b/test/csync/csync_tests/check_csync_log.cpp new file mode 100644 index 000000000..c5f312ff5 --- /dev/null +++ b/test/csync/csync_tests/check_csync_log.cpp @@ -0,0 +1,145 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include + +#include "torture.h" + +#include "csync.h" +#include "csync_log.cpp" +#include "c_private.h" + +static int setup(void **state) { + CSYNC *csync; + int rc; + + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + + csync_create(&csync, "/tmp/check_csync1"); + + *state = csync; + + return 0; +} + +static int teardown(void **state) { + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_destroy(csync); + + rc = system("rm -rf /tmp/check_csync"); + assert_int_equal(rc, 0); + + rc = system("rm -rf /tmp/check_csync1"); + assert_int_equal(rc, 0); + + *state = NULL; + + return 0; +} + +static void check_log_callback(int verbosity, + const char *function, + const char *buffer) +{ + int rc; + + assert_true(verbosity >= 0); + assert_non_null(function); + assert_false(function[0] == '\0'); + assert_non_null(buffer); + assert_false(buffer[0] == '\0'); + + rc = system("touch /tmp/check_csync1/cb_called"); + assert_int_equal(rc, 0); +} + +static void check_set_log_level(void **state) +{ + int rc; + + (void) state; + + rc = csync_set_log_level(-5); + assert_int_equal(rc, -1); + + rc = csync_set_log_level(5); + assert_int_equal(rc, 0); + + rc = csync_get_log_level(); + assert_int_equal(rc, 5); +} + +static void check_set_auth_callback(void **state) +{ + csync_log_callback log_fn; + int rc; + + (void) state; + + rc = csync_set_log_callback(NULL); + assert_int_equal(rc, -1); + + rc = csync_set_log_callback(check_log_callback); + assert_int_equal(rc, 0); + + log_fn = csync_get_log_callback(); + assert_non_null(log_fn); + assert_true(log_fn == &check_log_callback); +} + +static void check_logging(void **state) +{ + int rc; + csync_stat_t sb; + mbchar_t *path; + path = c_utf8_path_to_locale("/tmp/check_csync1/cb_called"); + + (void) state; /* unused */ + + assert_non_null(path); + + rc = csync_set_log_level(1); + assert_int_equal(rc, 0); + + rc = csync_set_log_callback(check_log_callback); + assert_int_equal(rc, 0); + + csync_log(1, __func__, "rc = %d", rc); + + rc = _tstat(path, &sb); + + c_free_locale_string(path); + + assert_int_equal(rc, 0); +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(check_set_log_level), + cmocka_unit_test(check_set_auth_callback), + cmocka_unit_test_setup_teardown(check_logging, setup, teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/test/csync/csync_tests/check_csync_misc.c b/test/csync/csync_tests/check_csync_misc.c deleted file mode 100644 index 80736b825..000000000 --- a/test/csync/csync_tests/check_csync_misc.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "torture.h" - -#include "csync_misc.h" -#include - -static void check_csync_normalize_etag(void **state) -{ - char *str; - - (void) state; /* unused */ - -#define CHECK_NORMALIZE_ETAG(TEST, EXPECT) \ - str = csync_normalize_etag(TEST); \ - assert_string_equal(str, EXPECT); \ - free(str); - - - CHECK_NORMALIZE_ETAG("foo", "foo"); - CHECK_NORMALIZE_ETAG("\"foo\"", "foo"); - CHECK_NORMALIZE_ETAG("\"nar123\"", "nar123"); - CHECK_NORMALIZE_ETAG("", ""); - CHECK_NORMALIZE_ETAG("\"\"", ""); - - /* Test with -gzip (all combinaison) */ - CHECK_NORMALIZE_ETAG("foo-gzip", "foo"); - CHECK_NORMALIZE_ETAG("\"foo\"-gzip", "foo"); - CHECK_NORMALIZE_ETAG("\"foo-gzip\"", "foo"); -} - -int torture_run_tests(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(check_csync_normalize_etag), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} - diff --git a/test/csync/csync_tests/check_csync_misc.cpp b/test/csync/csync_tests/check_csync_misc.cpp new file mode 100644 index 000000000..80736b825 --- /dev/null +++ b/test/csync/csync_tests/check_csync_misc.cpp @@ -0,0 +1,57 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "torture.h" + +#include "csync_misc.h" +#include + +static void check_csync_normalize_etag(void **state) +{ + char *str; + + (void) state; /* unused */ + +#define CHECK_NORMALIZE_ETAG(TEST, EXPECT) \ + str = csync_normalize_etag(TEST); \ + assert_string_equal(str, EXPECT); \ + free(str); + + + CHECK_NORMALIZE_ETAG("foo", "foo"); + CHECK_NORMALIZE_ETAG("\"foo\"", "foo"); + CHECK_NORMALIZE_ETAG("\"nar123\"", "nar123"); + CHECK_NORMALIZE_ETAG("", ""); + CHECK_NORMALIZE_ETAG("\"\"", ""); + + /* Test with -gzip (all combinaison) */ + CHECK_NORMALIZE_ETAG("foo-gzip", "foo"); + CHECK_NORMALIZE_ETAG("\"foo\"-gzip", "foo"); + CHECK_NORMALIZE_ETAG("\"foo-gzip\"", "foo"); +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(check_csync_normalize_etag), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} + diff --git a/test/csync/csync_tests/check_csync_statedb_load.c b/test/csync/csync_tests/check_csync_statedb_load.c deleted file mode 100644 index c4b34b500..000000000 --- a/test/csync/csync_tests/check_csync_statedb_load.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include - -#include "torture.h" - -#define CSYNC_TEST 1 -#include "csync_statedb.c" - -#define TESTDB "/tmp/check_csync1/test.db" - -static int setup(void **state) { - CSYNC *csync; - int rc; - - rc = system("rm -rf /tmp/check_csync1"); - assert_int_equal(rc, 0); - - rc = system("mkdir -p /tmp/check_csync1"); - assert_int_equal(rc, 0); - - csync_create(&csync, "/tmp/check_csync1"); - - csync->statedb.file = c_strdup( TESTDB ); - *state = csync; - - sqlite3 *db = NULL; - rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL); - assert_int_equal(rc, SQLITE_OK); - - rc = sqlite3_close(db); - assert_int_equal(rc, SQLITE_OK); - - return 0; -} - -static int teardown(void **state) { - CSYNC *csync = *state; - int rc; - - rc = csync_destroy(csync); - assert_int_equal(rc, 0); - - rc = system("rm -rf /tmp/check_csync1"); - assert_int_equal(rc, 0); - - *state = NULL; - - return 0; -} - -static void check_csync_statedb_load(void **state) -{ - CSYNC *csync = *state; - int rc; - - rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db); - assert_int_equal(rc, 0); - - sqlite3_close(csync->statedb.db); -} - -static void check_csync_statedb_close(void **state) -{ - CSYNC *csync = *state; - csync_stat_t sb; - time_t modtime; - mbchar_t *testdb = c_utf8_path_to_locale(TESTDB); - int rc; - - /* statedb not written */ - csync_statedb_load(csync, TESTDB, &csync->statedb.db); - - rc = _tstat(testdb, &sb); - assert_int_equal(rc, 0); - modtime = sb.st_mtime; - - rc = csync_statedb_close(csync); - assert_int_equal(rc, 0); - - rc = _tstat(testdb, &sb); - assert_int_equal(rc, 0); - assert_int_equal(modtime, sb.st_mtime); - - csync_statedb_load(csync, TESTDB, &csync->statedb.db); - - rc = _tstat(testdb, &sb); - assert_int_equal(rc, 0); - modtime = sb.st_mtime; - - /* wait a sec or the modtime will be the same */ - sleep(1); - - /* statedb written */ - rc = csync_statedb_close(csync); - assert_int_equal(rc, 0); - - rc = _tstat(testdb, &sb); - assert_int_equal(rc, 0); - - c_free_locale_string(testdb); -} - -int torture_run_tests(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(check_csync_statedb_load, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_statedb_close, setup, teardown), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} - diff --git a/test/csync/csync_tests/check_csync_statedb_load.cpp b/test/csync/csync_tests/check_csync_statedb_load.cpp new file mode 100644 index 000000000..a7bccd7ea --- /dev/null +++ b/test/csync/csync_tests/check_csync_statedb_load.cpp @@ -0,0 +1,130 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "torture.h" + +#define CSYNC_TEST 1 +#include "csync_statedb.cpp" + +#define TESTDB "/tmp/check_csync1/test.db" + +static int setup(void **state) { + CSYNC *csync; + int rc; + + rc = system("rm -rf /tmp/check_csync1"); + assert_int_equal(rc, 0); + + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + + csync_create(&csync, "/tmp/check_csync1"); + + csync->statedb.file = c_strdup( TESTDB ); + *state = csync; + + sqlite3 *db = NULL; + rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL); + assert_int_equal(rc, SQLITE_OK); + + rc = sqlite3_close(db); + assert_int_equal(rc, SQLITE_OK); + + return 0; +} + +static int teardown(void **state) { + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_destroy(csync); + assert_int_equal(rc, 0); + + rc = system("rm -rf /tmp/check_csync1"); + assert_int_equal(rc, 0); + + *state = NULL; + + return 0; +} + +static void check_csync_statedb_load(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db); + assert_int_equal(rc, 0); + + sqlite3_close(csync->statedb.db); +} + +static void check_csync_statedb_close(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_stat_t sb; + time_t modtime; + mbchar_t *testdb = c_utf8_path_to_locale(TESTDB); + int rc; + + /* statedb not written */ + csync_statedb_load(csync, TESTDB, &csync->statedb.db); + + rc = _tstat(testdb, &sb); + assert_int_equal(rc, 0); + modtime = sb.st_mtime; + + rc = csync_statedb_close(csync); + assert_int_equal(rc, 0); + + rc = _tstat(testdb, &sb); + assert_int_equal(rc, 0); + assert_int_equal(modtime, sb.st_mtime); + + csync_statedb_load(csync, TESTDB, &csync->statedb.db); + + rc = _tstat(testdb, &sb); + assert_int_equal(rc, 0); + modtime = sb.st_mtime; + + /* wait a sec or the modtime will be the same */ + sleep(1); + + /* statedb written */ + rc = csync_statedb_close(csync); + assert_int_equal(rc, 0); + + rc = _tstat(testdb, &sb); + assert_int_equal(rc, 0); + + c_free_locale_string(testdb); +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(check_csync_statedb_load, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_statedb_close, setup, teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} + diff --git a/test/csync/csync_tests/check_csync_statedb_query.c b/test/csync/csync_tests/check_csync_statedb_query.c deleted file mode 100644 index 603195e8c..000000000 --- a/test/csync/csync_tests/check_csync_statedb_query.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "torture.h" - -#define CSYNC_TEST 1 -#include "csync_statedb.c" - -#define TESTDB "/tmp/check_csync1/test.db" -#define TESTDBTMP "/tmp/check_csync1/test.db.ctmp" - - - -static int setup(void **state) -{ - CSYNC *csync; - int rc = 0; - - rc = system("rm -rf /tmp/check_csync1"); - assert_int_equal(rc, 0); - rc = system("mkdir -p /tmp/check_csync1"); - assert_int_equal(rc, 0); - rc = system("mkdir -p /tmp/check_csync"); - assert_int_equal(rc, 0); - csync_create(&csync, "/tmp/check_csync1"); - csync_init(csync, TESTDB); - - sqlite3 *db = NULL; - rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL); - assert_int_equal(rc, SQLITE_OK); - rc = sqlite3_close(db); - assert_int_equal(rc, SQLITE_OK); - - rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db); - assert_int_equal(rc, 0); - - *state = csync; - - return 0; -} - -static int setup_db(void **state) -{ - char *errmsg; - int rc = 0; - sqlite3 *db = NULL; - - const char *sql = "CREATE TABLE IF NOT EXISTS metadata (" - "phash INTEGER(8)," - "pathlen INTEGER," - "path VARCHAR(4096)," - "inode INTEGER," - "uid INTEGER," - "gid INTEGER," - "mode INTEGER," - "modtime INTEGER(8)," - "type INTEGER," - "md5 VARCHAR(32)," - "PRIMARY KEY(phash)" - ");"; - - const char *sql2 = "INSERT INTO metadata" - "(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5) VALUES" - "(42, 42, 'Its funny stuff', 23, 42, 43, 55, 66, 2, 54);"; - - - setup(state); - rc = sqlite3_open( TESTDB, &db); - assert_int_equal(rc, SQLITE_OK); - - rc = sqlite3_exec( db, sql, NULL, NULL, &errmsg ); - assert_int_equal(rc, SQLITE_OK); - - rc = sqlite3_exec( db, sql2, NULL, NULL, &errmsg ); - assert_int_equal(rc, SQLITE_OK); - - sqlite3_close(db); - - return 0; - -} - -static int teardown(void **state) { - CSYNC *csync = *state; - int rc = 0; - - rc = csync_destroy(csync); - assert_int_equal(rc, 0); - rc = system("rm -rf /tmp/check_csync"); - assert_int_equal(rc, 0); - rc = system("rm -rf /tmp/check_csync1"); - assert_int_equal(rc, 0); - - *state = NULL; - - return 0; -} - - -static void check_csync_statedb_query_statement(void **state) -{ - CSYNC *csync = *state; - c_strlist_t *result; - - result = csync_statedb_query(csync->statedb.db, ""); - assert_null(result); - if (result != NULL) { - c_strlist_destroy(result); - } - - result = csync_statedb_query(csync->statedb.db, "SELECT;"); - assert_null(result); - if (result != NULL) { - c_strlist_destroy(result); - } -} - -static void check_csync_statedb_drop_tables(void **state) -{ - // CSYNC *csync = *state; - int rc = 0; - (void) state; - - // rc = csync_statedb_drop_tables(csync->statedb.db); - assert_int_equal(rc, 0); - // rc = csync_statedb_create_tables(csync->statedb.db); - assert_int_equal(rc, 0); - // rc = csync_statedb_drop_tables(csync->statedb.db); - assert_int_equal(rc, 0); -} - -static void check_csync_statedb_insert_metadata(void **state) -{ - CSYNC *csync = *state; - csync_file_stat_t *st; - int i, rc = 0; - - // rc = csync_statedb_create_tables(csync->statedb.db); - assert_int_equal(rc, 0); - - for (i = 0; i < 100; i++) { - st = c_malloc(sizeof(csync_file_stat_t) + 30 ); - snprintf(st->path, 29, "file_%d" , i ); - st->phash = i; - - rc = c_rbtree_insert(csync->local.tree, (void *) st); - assert_int_equal(rc, 0); - } - - // rc = csync_statedb_insert_metadata(csync, csync->statedb.db); - assert_int_equal(rc, 0); -} - -static void check_csync_statedb_write(void **state) -{ - CSYNC *csync = *state; - csync_file_stat_t *st; - int i, rc; - - for (i = 0; i < 100; i++) { - st = c_malloc(sizeof(csync_file_stat_t) + 30); - snprintf(st->path, 29, "file_%d" , i ); - st->phash = i; - - rc = c_rbtree_insert(csync->local.tree, (void *) st); - assert_int_equal(rc, 0); - } - - // rc = csync_statedb_write(csync, csync->statedb.db); - assert_int_equal(rc, 0); -} - - -static void check_csync_statedb_get_stat_by_hash_not_found(void **state) -{ - CSYNC *csync = *state; - csync_file_stat_t *tmp; - - tmp = csync_statedb_get_stat_by_hash(csync, (uint64_t) 666); - assert_null(tmp); - - free(tmp); -} - - -static void check_csync_statedb_get_stat_by_inode_not_found(void **state) -{ - CSYNC *csync = *state; - csync_file_stat_t *tmp; - - tmp = csync_statedb_get_stat_by_inode(csync, (ino_t) 666); - assert_null(tmp); -} - -int torture_run_tests(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(check_csync_statedb_query_statement, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_statedb_drop_tables, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_statedb_insert_metadata, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_statedb_write, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_statedb_get_stat_by_hash_not_found, setup_db, teardown), - cmocka_unit_test_setup_teardown(check_csync_statedb_get_stat_by_inode_not_found, setup_db, teardown), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} - diff --git a/test/csync/csync_tests/check_csync_statedb_query.cpp b/test/csync/csync_tests/check_csync_statedb_query.cpp new file mode 100644 index 000000000..843900ec2 --- /dev/null +++ b/test/csync/csync_tests/check_csync_statedb_query.cpp @@ -0,0 +1,224 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "torture.h" + +#define CSYNC_TEST 1 +#include "csync_statedb.cpp" + +#define TESTDB "/tmp/check_csync1/test.db" +#define TESTDBTMP "/tmp/check_csync1/test.db.ctmp" + + + +static int setup(void **state) +{ + CSYNC *csync; + int rc = 0; + + rc = system("rm -rf /tmp/check_csync1"); + assert_int_equal(rc, 0); + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + rc = system("mkdir -p /tmp/check_csync"); + assert_int_equal(rc, 0); + csync_create(&csync, "/tmp/check_csync1"); + csync_init(csync, TESTDB); + + sqlite3 *db = NULL; + rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL); + assert_int_equal(rc, SQLITE_OK); + rc = sqlite3_close(db); + assert_int_equal(rc, SQLITE_OK); + + rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db); + assert_int_equal(rc, 0); + + *state = csync; + + return 0; +} + +static int setup_db(void **state) +{ + char *errmsg; + int rc = 0; + sqlite3 *db = NULL; + + const char *sql = "CREATE TABLE IF NOT EXISTS metadata (" + "phash INTEGER(8)," + "pathlen INTEGER," + "path VARCHAR(4096)," + "inode INTEGER," + "uid INTEGER," + "gid INTEGER," + "mode INTEGER," + "modtime INTEGER(8)," + "type INTEGER," + "md5 VARCHAR(32)," + "PRIMARY KEY(phash)" + ");"; + + const char *sql2 = "INSERT INTO metadata" + "(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5) VALUES" + "(42, 42, 'Its funny stuff', 23, 42, 43, 55, 66, 2, 54);"; + + + setup(state); + rc = sqlite3_open( TESTDB, &db); + assert_int_equal(rc, SQLITE_OK); + + rc = sqlite3_exec( db, sql, NULL, NULL, &errmsg ); + assert_int_equal(rc, SQLITE_OK); + + rc = sqlite3_exec( db, sql2, NULL, NULL, &errmsg ); + assert_int_equal(rc, SQLITE_OK); + + sqlite3_close(db); + + return 0; + +} + +static int teardown(void **state) { + CSYNC *csync = (CSYNC*)*state; + int rc = 0; + + rc = csync_destroy(csync); + assert_int_equal(rc, 0); + rc = system("rm -rf /tmp/check_csync"); + assert_int_equal(rc, 0); + rc = system("rm -rf /tmp/check_csync1"); + assert_int_equal(rc, 0); + + *state = NULL; + + return 0; +} + + +static void check_csync_statedb_query_statement(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + c_strlist_t *result; + + result = csync_statedb_query(csync->statedb.db, ""); + assert_null(result); + if (result != NULL) { + c_strlist_destroy(result); + } + + result = csync_statedb_query(csync->statedb.db, "SELECT;"); + assert_null(result); + if (result != NULL) { + c_strlist_destroy(result); + } +} + +static void check_csync_statedb_drop_tables(void **state) +{ + // CSYNC *csync = (CSYNC*)*state; + int rc = 0; + (void) state; + + // rc = csync_statedb_drop_tables(csync->statedb.db); + assert_int_equal(rc, 0); + // rc = csync_statedb_create_tables(csync->statedb.db); + assert_int_equal(rc, 0); + // rc = csync_statedb_drop_tables(csync->statedb.db); + assert_int_equal(rc, 0); +} + +static void check_csync_statedb_insert_metadata(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *st; + int i, rc = 0; + + // rc = csync_statedb_create_tables(csync->statedb.db); + assert_int_equal(rc, 0); + + for (i = 0; i < 100; i++) { + st = (csync_file_stat_t*)c_malloc(sizeof(csync_file_stat_t) + 30 ); + snprintf(st->path, 29, "file_%d" , i ); + st->phash = i; + + rc = c_rbtree_insert(csync->local.tree, (void *) st); + assert_int_equal(rc, 0); + } + + // rc = csync_statedb_insert_metadata(csync, csync->statedb.db); + assert_int_equal(rc, 0); +} + +static void check_csync_statedb_write(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *st; + int i, rc; + + for (i = 0; i < 100; i++) { + st = (csync_file_stat_t*)c_malloc(sizeof(csync_file_stat_t) + 30); + snprintf(st->path, 29, "file_%d" , i ); + st->phash = i; + + rc = c_rbtree_insert(csync->local.tree, (void *) st); + assert_int_equal(rc, 0); + } + + // rc = csync_statedb_write(csync, csync->statedb.db); + assert_int_equal(rc, 0); +} + + +static void check_csync_statedb_get_stat_by_hash_not_found(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *tmp; + + tmp = csync_statedb_get_stat_by_hash(csync, (uint64_t) 666); + assert_null(tmp); + + free(tmp); +} + + +static void check_csync_statedb_get_stat_by_inode_not_found(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *tmp; + + tmp = csync_statedb_get_stat_by_inode(csync, (ino_t) 666); + assert_null(tmp); +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(check_csync_statedb_query_statement, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_statedb_drop_tables, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_statedb_insert_metadata, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_statedb_write, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_statedb_get_stat_by_hash_not_found, setup_db, teardown), + cmocka_unit_test_setup_teardown(check_csync_statedb_get_stat_by_inode_not_found, setup_db, teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} + diff --git a/test/csync/csync_tests/check_csync_update.c b/test/csync/csync_tests/check_csync_update.c deleted file mode 100644 index 695616b2c..000000000 --- a/test/csync/csync_tests/check_csync_update.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "torture.h" - -#include "csync_update.c" - -#define TESTDB "/tmp/check_csync/journal.db" - -static int firstrun = 1; - -static void statedb_create_metadata_table(sqlite3 *db) -{ - int rc = 0; - - if( db ) { - const char *sql = "CREATE TABLE IF NOT EXISTS metadata(" - "phash INTEGER(8)," - "pathlen INTEGER," - "path VARCHAR(4096)," - "inode INTEGER," - "uid INTEGER," - "gid INTEGER," - "mode INTEGER," - "modtime INTEGER(8)," - "type INTEGER," - "md5 VARCHAR(32)," - "fileid VARCHAR(128)," - "remotePerm VARCHAR(128)," - "filesize BIGINT," - "ignoredChildrenRemote INT," - "contentChecksum TEXT," - "contentChecksumTypeId INTEGER," - "PRIMARY KEY(phash));"; - - rc = sqlite3_exec(db, sql, NULL, NULL, NULL); - //const char *msg = sqlite3_errmsg(db); - assert_int_equal( rc, SQLITE_OK ); - - sql = "CREATE TABLE IF NOT EXISTS checksumtype(" - "id INTEGER PRIMARY KEY," - "name TEXT UNIQUE" - ");"; - rc = sqlite3_exec(db, sql, NULL, NULL, NULL); - assert_int_equal( rc, SQLITE_OK ); - } -} - -static void statedb_insert_metadata(sqlite3 *db) -{ - int rc = 0; - - if( db ) { - char *stmt = sqlite3_mprintf("INSERT INTO metadata" - "(phash, pathlen, path, inode, uid, gid, mode, modtime,type,md5) VALUES" - "(%lld, %d, '%q', %d, %d, %d, %d, %lld, %d, '%q');", - (long long signed int)42, - 42, - "I_was_wurst_before_I_became_wurstsalat", - 619070, - 42, - 42, - 42, - (long long signed int)42, - 0, - "4711"); - - char *errmsg; - rc = sqlite3_exec(db, stmt, NULL, NULL, &errmsg); - sqlite3_free(stmt); - assert_int_equal( rc, SQLITE_OK ); - } -} - -static int setup(void **state) -{ - CSYNC *csync; - int rc; - - unlink(TESTDB); - rc = system("mkdir -p /tmp/check_csync"); - assert_int_equal(rc, 0); - rc = system("mkdir -p /tmp/check_csync1"); - assert_int_equal(rc, 0); - csync_create(&csync, "/tmp/check_csync1"); - csync_init(csync, TESTDB); - - /* Create a new db with metadata */ - sqlite3 *db; - csync->statedb.file = c_strdup(TESTDB); - rc = sqlite3_open(csync->statedb.file, &db); - statedb_create_metadata_table(db); - if( firstrun ) { - statedb_insert_metadata(db); - firstrun = 0; - } - sqlite3_close(db); - - rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db); - assert_int_equal(rc, 0); - - *state = csync; - - return 0; -} - -static int setup_ftw(void **state) -{ - CSYNC *csync; - int rc; - - rc = system("mkdir -p /tmp/check_csync"); - assert_int_equal(rc, 0); - rc = system("mkdir -p /tmp/check_csync1"); - assert_int_equal(rc, 0); - csync_create(&csync, "/tmp"); - csync_init(csync, TESTDB); - - sqlite3 *db = NULL; - rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL); - assert_int_equal(rc, SQLITE_OK); - statedb_create_metadata_table(db); - rc = sqlite3_close(db); - assert_int_equal(rc, SQLITE_OK); - - rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db); - assert_int_equal(rc, 0); - - csync->statedb.file = c_strdup( TESTDB ); - *state = csync; - - return 0; -} - -static int teardown(void **state) -{ - CSYNC *csync = *state; - int rc; - - unlink( csync->statedb.file); - rc = csync_destroy(csync); - assert_int_equal(rc, 0); - - *state = NULL; - - return 0; -} - -static int teardown_rm(void **state) { - int rc; - - teardown(state); - - rc = system("rm -rf /tmp/check_csync"); - assert_int_equal(rc, 0); - rc = system("rm -rf /tmp/check_csync1"); - assert_int_equal(rc, 0); - - return 0; -} - -/* create a file stat, caller must free memory */ -static csync_vio_file_stat_t* create_fstat(const char *name, - ino_t inode, - time_t mtime) -{ - csync_vio_file_stat_t *fs = NULL; - time_t t; - - fs = csync_vio_file_stat_new(); - if (fs == NULL) { - return NULL; - } - - if (name && *name) { - fs->name = c_strdup(name); - } else { - fs->name = c_strdup("file.txt"); - } - - if (fs->name == NULL) { - csync_vio_file_stat_destroy(fs); - return NULL; - } - - fs->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; - - fs->type = CSYNC_VIO_FILE_TYPE_REGULAR; - fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; - - - if (inode == 0) { - fs->inode = 619070; - } else { - fs->inode = inode; - } - fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE; - - - fs->size = 157459; - fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; - - - - if (mtime == 0) { - fs->atime = fs->ctime = fs->mtime = time(&t); - } else { - fs->atime = fs->ctime = fs->mtime = mtime; - } - fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME; - fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME; - fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; - - return fs; -} - -static int failing_fn(CSYNC *ctx, - const char *file, - const csync_vio_file_stat_t *fs, - enum csync_ftw_flags_e flag) -{ - (void) ctx; - (void) file; - (void) fs; - (void) flag; - - return -1; -} - -/* detect a new file */ -static void check_csync_detect_update(void **state) -{ - CSYNC *csync = *state; - csync_file_stat_t *st; - csync_vio_file_stat_t *fs; - int rc; - - fs = create_fstat("file.txt", 0, 1217597845); - assert_non_null(fs); - - rc = _csync_detect_update(csync, - "/tmp/check_csync1/file.txt", - fs, - CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, 0); - - /* the instruction should be set to new */ - st = c_rbtree_node_data(csync->local.tree->root); - assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); - - /* create a statedb */ - csync_set_status(csync, 0xFFFF); - - csync_vio_file_stat_destroy(fs); -} - -/* Test behaviour in case no db is there. For that its important that the - * test before this one uses teardown_rm. - */ -static void check_csync_detect_update_db_none(void **state) -{ - CSYNC *csync = *state; - csync_file_stat_t *st; - csync_vio_file_stat_t *fs; - int rc; - - fs = create_fstat("file.txt", 0, 1217597845); - assert_non_null(fs); - - rc = _csync_detect_update(csync, - "/tmp/check_csync1/file.txt", - fs, - CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, 0); - - /* the instruction should be set to new */ - st = c_rbtree_node_data(csync->local.tree->root); - assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); - - - /* create a statedb */ - csync_set_status(csync, 0xFFFF); - - csync_vio_file_stat_destroy(fs); -} - -static void check_csync_detect_update_db_eval(void **state) -{ - CSYNC *csync = *state; - csync_file_stat_t *st; - csync_vio_file_stat_t *fs; - int rc; - - fs = create_fstat("file.txt", 0, 42); - assert_non_null(fs); - - rc = _csync_detect_update(csync, - "/tmp/check_csync1/file.txt", - fs, - CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, 0); - - /* the instruction should be set to new */ - st = c_rbtree_node_data(csync->local.tree->root); - assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); - - /* create a statedb */ - csync_set_status(csync, 0xFFFF); - - csync_vio_file_stat_destroy(fs); -} - - -static void check_csync_detect_update_db_rename(void **state) -{ - CSYNC *csync = *state; - // csync_file_stat_t *st; - - csync_vio_file_stat_t *fs; - int rc = 0; - - fs = create_fstat("wurst.txt", 0, 42); - assert_non_null(fs); - csync_set_statedb_exists(csync, 1); - - rc = _csync_detect_update(csync, - "/tmp/check_csync1/wurst.txt", - fs, - CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, 0); - - /* the instruction should be set to rename */ - /* - * temporarily broken. - st = c_rbtree_node_data(csync->local.tree->root); - assert_int_equal(st->instruction, CSYNC_INSTRUCTION_RENAME); - - st->instruction = CSYNC_INSTRUCTION_UPDATED; - */ - /* create a statedb */ - csync_set_status(csync, 0xFFFF); - - csync_vio_file_stat_destroy(fs); -} - -static void check_csync_detect_update_db_new(void **state) -{ - CSYNC *csync = *state; - csync_file_stat_t *st; - csync_vio_file_stat_t *fs; - int rc; - - fs = create_fstat("file.txt", 42000, 0); - assert_non_null(fs); - - rc = _csync_detect_update(csync, - "/tmp/check_csync1/file.txt", - fs, - CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, 0); - - /* the instruction should be set to new */ - st = c_rbtree_node_data(csync->local.tree->root); - assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); - - - /* create a statedb */ - csync_set_status(csync, 0xFFFF); - - csync_vio_file_stat_destroy(fs); -} - -static void check_csync_detect_update_null(void **state) -{ - CSYNC *csync = *state; - csync_vio_file_stat_t *fs; - int rc; - - fs = create_fstat("file.txt", 0, 0); - assert_non_null(fs); - - rc = _csync_detect_update(csync, - NULL, - fs, - CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, -1); - - rc = _csync_detect_update(csync, - "/tmp/check_csync1/file.txt", - NULL, - CSYNC_FTW_TYPE_FILE); - assert_int_equal(rc, -1); - - csync_vio_file_stat_destroy(fs); -} - -static void check_csync_ftw(void **state) -{ - CSYNC *csync = *state; - int rc; - - rc = csync_ftw(csync, "/tmp", csync_walker, MAX_DEPTH); - assert_int_equal(rc, 0); -} - -static void check_csync_ftw_empty_uri(void **state) -{ - CSYNC *csync = *state; - int rc; - - rc = csync_ftw(csync, "", csync_walker, MAX_DEPTH); - assert_int_equal(rc, -1); -} - -static void check_csync_ftw_failing_fn(void **state) -{ - CSYNC *csync = *state; - int rc; - - rc = csync_ftw(csync, "/tmp", failing_fn, MAX_DEPTH); - assert_int_equal(rc, -1); -} - -int torture_run_tests(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(check_csync_detect_update, setup, teardown_rm), - cmocka_unit_test_setup_teardown(check_csync_detect_update_db_none, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_detect_update_db_eval, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_detect_update_db_rename, setup, teardown), - cmocka_unit_test_setup_teardown(check_csync_detect_update_db_new, setup, teardown_rm), - cmocka_unit_test_setup_teardown(check_csync_detect_update_null, setup, teardown_rm), - - cmocka_unit_test_setup_teardown(check_csync_ftw, setup_ftw, teardown_rm), - cmocka_unit_test_setup_teardown(check_csync_ftw_empty_uri, setup_ftw, teardown_rm), - cmocka_unit_test_setup_teardown(check_csync_ftw_failing_fn, setup_ftw, teardown_rm), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} - diff --git a/test/csync/csync_tests/check_csync_update.cpp b/test/csync/csync_tests/check_csync_update.cpp new file mode 100644 index 000000000..237687e4c --- /dev/null +++ b/test/csync/csync_tests/check_csync_update.cpp @@ -0,0 +1,456 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "torture.h" + +#include "csync_update.cpp" + +#define TESTDB "/tmp/check_csync/journal.db" + +static int firstrun = 1; + +static void statedb_create_metadata_table(sqlite3 *db) +{ + int rc = 0; + + if( db ) { + const char *sql = "CREATE TABLE IF NOT EXISTS metadata(" + "phash INTEGER(8)," + "pathlen INTEGER," + "path VARCHAR(4096)," + "inode INTEGER," + "uid INTEGER," + "gid INTEGER," + "mode INTEGER," + "modtime INTEGER(8)," + "type INTEGER," + "md5 VARCHAR(32)," + "fileid VARCHAR(128)," + "remotePerm VARCHAR(128)," + "filesize BIGINT," + "ignoredChildrenRemote INT," + "contentChecksum TEXT," + "contentChecksumTypeId INTEGER," + "PRIMARY KEY(phash));"; + + rc = sqlite3_exec(db, sql, NULL, NULL, NULL); + //const char *msg = sqlite3_errmsg(db); + assert_int_equal( rc, SQLITE_OK ); + + sql = "CREATE TABLE IF NOT EXISTS checksumtype(" + "id INTEGER PRIMARY KEY," + "name TEXT UNIQUE" + ");"; + rc = sqlite3_exec(db, sql, NULL, NULL, NULL); + assert_int_equal( rc, SQLITE_OK ); + } +} + +static void statedb_insert_metadata(sqlite3 *db) +{ + int rc = 0; + + if( db ) { + char *stmt = sqlite3_mprintf("INSERT INTO metadata" + "(phash, pathlen, path, inode, uid, gid, mode, modtime,type,md5) VALUES" + "(%lld, %d, '%q', %d, %d, %d, %d, %lld, %d, '%q');", + (long long signed int)42, + 42, + "I_was_wurst_before_I_became_wurstsalat", + 619070, + 42, + 42, + 42, + (long long signed int)42, + 0, + "4711"); + + char *errmsg; + rc = sqlite3_exec(db, stmt, NULL, NULL, &errmsg); + sqlite3_free(stmt); + assert_int_equal( rc, SQLITE_OK ); + } +} + +static int setup(void **state) +{ + CSYNC *csync; + int rc; + + unlink(TESTDB); + rc = system("mkdir -p /tmp/check_csync"); + assert_int_equal(rc, 0); + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + csync_create(&csync, "/tmp/check_csync1"); + csync_init(csync, TESTDB); + + /* Create a new db with metadata */ + sqlite3 *db; + csync->statedb.file = c_strdup(TESTDB); + rc = sqlite3_open(csync->statedb.file, &db); + statedb_create_metadata_table(db); + if( firstrun ) { + statedb_insert_metadata(db); + firstrun = 0; + } + sqlite3_close(db); + + rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db); + assert_int_equal(rc, 0); + + *state = csync; + + return 0; +} + +static int setup_ftw(void **state) +{ + CSYNC *csync; + int rc; + + rc = system("mkdir -p /tmp/check_csync"); + assert_int_equal(rc, 0); + rc = system("mkdir -p /tmp/check_csync1"); + assert_int_equal(rc, 0); + csync_create(&csync, "/tmp"); + csync_init(csync, TESTDB); + + sqlite3 *db = NULL; + rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL); + assert_int_equal(rc, SQLITE_OK); + statedb_create_metadata_table(db); + rc = sqlite3_close(db); + assert_int_equal(rc, SQLITE_OK); + + rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db); + assert_int_equal(rc, 0); + + csync->statedb.file = c_strdup( TESTDB ); + *state = csync; + + return 0; +} + +static int teardown(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + unlink( csync->statedb.file); + rc = csync_destroy(csync); + assert_int_equal(rc, 0); + + *state = NULL; + + return 0; +} + +static int teardown_rm(void **state) { + int rc; + + teardown(state); + + rc = system("rm -rf /tmp/check_csync"); + assert_int_equal(rc, 0); + rc = system("rm -rf /tmp/check_csync1"); + assert_int_equal(rc, 0); + + return 0; +} + +/* create a file stat, caller must free memory */ +static csync_vio_file_stat_t* create_fstat(const char *name, + ino_t inode, + time_t mtime) +{ + csync_vio_file_stat_t *fs = NULL; + time_t t; + + fs = csync_vio_file_stat_new(); + if (fs == NULL) { + return NULL; + } + + if (name && *name) { + fs->name = c_strdup(name); + } else { + fs->name = c_strdup("file.txt"); + } + + if (fs->name == NULL) { + csync_vio_file_stat_destroy(fs); + return NULL; + } + + fs->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; + + fs->type = CSYNC_VIO_FILE_TYPE_REGULAR; + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; + + + if (inode == 0) { + fs->inode = 619070; + } else { + fs->inode = inode; + } + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE; + + + fs->size = 157459; + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; + + + + if (mtime == 0) { + fs->atime = fs->ctime = fs->mtime = time(&t); + } else { + fs->atime = fs->ctime = fs->mtime = mtime; + } + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME; + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME; + fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; + + return fs; +} + +static int failing_fn(CSYNC *ctx, + const char *file, + const csync_vio_file_stat_t *fs, + int flag) +{ + (void) ctx; + (void) file; + (void) fs; + (void) flag; + + return -1; +} + +/* detect a new file */ +static void check_csync_detect_update(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *st; + csync_vio_file_stat_t *fs; + int rc; + + fs = create_fstat("file.txt", 0, 1217597845); + assert_non_null(fs); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/file.txt", + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, 0); + + /* the instruction should be set to new */ + st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root); + assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); + + /* create a statedb */ + csync_set_status(csync, 0xFFFF); + + csync_vio_file_stat_destroy(fs); +} + +/* Test behaviour in case no db is there. For that its important that the + * test before this one uses teardown_rm. + */ +static void check_csync_detect_update_db_none(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *st; + csync_vio_file_stat_t *fs; + int rc; + + fs = create_fstat("file.txt", 0, 1217597845); + assert_non_null(fs); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/file.txt", + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, 0); + + /* the instruction should be set to new */ + st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root); + assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); + + + /* create a statedb */ + csync_set_status(csync, 0xFFFF); + + csync_vio_file_stat_destroy(fs); +} + +static void check_csync_detect_update_db_eval(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *st; + csync_vio_file_stat_t *fs; + int rc; + + fs = create_fstat("file.txt", 0, 42); + assert_non_null(fs); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/file.txt", + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, 0); + + /* the instruction should be set to new */ + st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root); + assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); + + /* create a statedb */ + csync_set_status(csync, 0xFFFF); + + csync_vio_file_stat_destroy(fs); +} + + +static void check_csync_detect_update_db_rename(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + // csync_file_stat_t *st; + + csync_vio_file_stat_t *fs; + int rc = 0; + + fs = create_fstat("wurst.txt", 0, 42); + assert_non_null(fs); + csync_set_statedb_exists(csync, 1); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/wurst.txt", + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, 0); + + /* the instruction should be set to rename */ + /* + * temporarily broken. + st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root); + assert_int_equal(st->instruction, CSYNC_INSTRUCTION_RENAME); + + st->instruction = CSYNC_INSTRUCTION_UPDATED; + */ + /* create a statedb */ + csync_set_status(csync, 0xFFFF); + + csync_vio_file_stat_destroy(fs); +} + +static void check_csync_detect_update_db_new(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_file_stat_t *st; + csync_vio_file_stat_t *fs; + int rc; + + fs = create_fstat("file.txt", 42000, 0); + assert_non_null(fs); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/file.txt", + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, 0); + + /* the instruction should be set to new */ + st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root); + assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW); + + + /* create a statedb */ + csync_set_status(csync, 0xFFFF); + + csync_vio_file_stat_destroy(fs); +} + +static void check_csync_detect_update_null(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_vio_file_stat_t *fs; + int rc; + + fs = create_fstat("file.txt", 0, 0); + assert_non_null(fs); + + rc = _csync_detect_update(csync, + NULL, + fs, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, -1); + + rc = _csync_detect_update(csync, + "/tmp/check_csync1/file.txt", + NULL, + CSYNC_FTW_TYPE_FILE); + assert_int_equal(rc, -1); + + csync_vio_file_stat_destroy(fs); +} + +static void check_csync_ftw(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_ftw(csync, "/tmp", csync_walker, MAX_DEPTH); + assert_int_equal(rc, 0); +} + +static void check_csync_ftw_empty_uri(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_ftw(csync, "", csync_walker, MAX_DEPTH); + assert_int_equal(rc, -1); +} + +static void check_csync_ftw_failing_fn(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_ftw(csync, "/tmp", failing_fn, MAX_DEPTH); + assert_int_equal(rc, -1); +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(check_csync_detect_update, setup, teardown_rm), + cmocka_unit_test_setup_teardown(check_csync_detect_update_db_none, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_detect_update_db_eval, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_detect_update_db_rename, setup, teardown), + cmocka_unit_test_setup_teardown(check_csync_detect_update_db_new, setup, teardown_rm), + cmocka_unit_test_setup_teardown(check_csync_detect_update_null, setup, teardown_rm), + + cmocka_unit_test_setup_teardown(check_csync_ftw, setup_ftw, teardown_rm), + cmocka_unit_test_setup_teardown(check_csync_ftw_empty_uri, setup_ftw, teardown_rm), + cmocka_unit_test_setup_teardown(check_csync_ftw_failing_fn, setup_ftw, teardown_rm), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/test/csync/csync_tests/check_csync_util.c b/test/csync/csync_tests/check_csync_util.c deleted file mode 100644 index e55c4ce77..000000000 --- a/test/csync/csync_tests/check_csync_util.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "torture.h" - -#include "csync_util.h" - -static void check_csync_instruction_str(void **state) -{ - const char *str; - - (void) state; /* unused */ - - str = csync_instruction_str(CSYNC_INSTRUCTION_ERROR); - assert_string_equal(str, "INSTRUCTION_ERROR"); - - str = csync_instruction_str(0xFFFF); - assert_string_equal(str, "ERROR!"); -} - -static void check_csync_memstat(void **state) -{ - (void) state; /* unused */ - - csync_memstat_check(); -} - -int torture_run_tests(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test(check_csync_instruction_str), - cmocka_unit_test(check_csync_memstat), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} - diff --git a/test/csync/csync_tests/check_csync_util.cpp b/test/csync/csync_tests/check_csync_util.cpp new file mode 100644 index 000000000..0df637741 --- /dev/null +++ b/test/csync/csync_tests/check_csync_util.cpp @@ -0,0 +1,53 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "torture.h" + +#include "csync_util.h" + +static void check_csync_instruction_str(void **state) +{ + const char *str; + + (void) state; /* unused */ + + str = csync_instruction_str(CSYNC_INSTRUCTION_ERROR); + assert_string_equal(str, "INSTRUCTION_ERROR"); + + str = csync_instruction_str((enum csync_instructions_e)0xFFFF); + assert_string_equal(str, "ERROR!"); +} + +static void check_csync_memstat(void **state) +{ + (void) state; /* unused */ + + csync_memstat_check(); +} + +int torture_run_tests(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(check_csync_instruction_str), + cmocka_unit_test(check_csync_memstat), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} + diff --git a/test/csync/encoding_tests/check_encoding.c b/test/csync/encoding_tests/check_encoding.c deleted file mode 100644 index eb761d318..000000000 --- a/test/csync/encoding_tests/check_encoding.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2013 by Klaas Freitag - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "torture.h" -#include -#include "c_string.h" -#include "c_path.h" - -#ifdef _WIN32 -#include -#endif - - -static void setup(void **state) -{ - (void) state; /* unused */ -} - -static void teardown(void **state) -{ - (void) state; /* unused */ -} - -static void check_iconv_to_native_normalization(void **state) -{ - mbchar_t *out = NULL; - const char *in= "\x48\xc3\xa4"; // UTF8 -#ifdef __APPLE__ - const char *exp_out = "\x48\x61\xcc\x88"; // UTF-8-MAC -#else - const char *exp_out = "\x48\xc3\xa4"; // UTF8 -#endif - - out = c_utf8_path_to_locale(in); - assert_string_equal(out, exp_out); - - c_free_locale_string(out); - assert_null(out); - - (void) state; /* unused */ -} - -static void check_iconv_from_native_normalization(void **state) -{ - char *out = NULL; -#ifdef _WIN32 - const mbchar_t *in = L"\x48\xc3\xa4"; // UTF-8 -#else -#ifdef __APPLE__ - const mbchar_t *in = "\x48\x61\xcc\x88"; // UTF-8-MAC -#else - const mbchar_t *in = "\x48\xc3\xa4"; // UTF-8 -#endif -#endif - const char *exp_out = "\x48\xc3\xa4"; // UTF-8 - - out = c_utf8_from_locale(in); - assert_string_equal(out, exp_out); - - c_free_locale_string(out); - assert_null(out); - - (void) state; /* unused */ -} - -static void check_iconv_ascii(void **state) -{ -#ifdef _WIN32 - const mbchar_t *in = L"abc/ABC\\123"; // UTF-8 -#else -#ifdef __APPLE__ - const mbchar_t *in = "abc/ABC\\123"; // UTF-8-MAC -#else - const mbchar_t *in = "abc/ABC\\123"; // UTF-8 -#endif -#endif - char *out = NULL; - const char *exp_out = "abc/ABC\\123"; - - out = c_utf8_from_locale(in); - assert_string_equal(out, exp_out); - - c_free_locale_string(out); - assert_null(out); - - (void) state; /* unused */ -} - -#define TESTSTRING "#cA\\#fß§4" -#define LTESTSTRING L"#cA\\#fß§4" - -static void check_to_multibyte(void **state) -{ - int rc = -1; - - mbchar_t *mb_string = c_utf8_path_to_locale( TESTSTRING ); - mbchar_t *mb_null = c_utf8_path_to_locale( NULL ); - - (void) state; - -#ifdef _WIN32 - assert_int_equal( wcscmp( LTESTSTRING, mb_string), 0 ); -#else - assert_string_equal(mb_string, TESTSTRING); -#endif - assert_true( mb_null == NULL ); - assert_int_equal(rc, -1); - - c_free_locale_string(mb_string); - c_free_locale_string(mb_null); -} - -static void check_long_win_path(void **state) -{ - (void) state; /* unused */ - - { - const char *path = "C://DATA/FILES/MUSIC/MY_MUSIC.mp3"; // check a short path - const char *exp_path = "\\\\?\\C:\\\\DATA\\FILES\\MUSIC\\MY_MUSIC.mp3"; - const char *new_short = c_path_to_UNC(path); - assert_string_equal(new_short, exp_path); - SAFE_FREE(new_short); - } - - { - const char *path = "\\\\foo\\bar/MY_MUSIC.mp3"; - const char *exp_path = "\\\\foo\\bar\\MY_MUSIC.mp3"; - const char *new_short = c_path_to_UNC(path); - assert_string_equal(new_short, exp_path); - SAFE_FREE(new_short); - } - - { - const char *path = "//foo\\bar/MY_MUSIC.mp3"; - const char *exp_path = "\\\\foo\\bar\\MY_MUSIC.mp3"; - const char *new_short = c_path_to_UNC(path); - assert_string_equal(new_short, exp_path); - SAFE_FREE(new_short); - } - - { - const char *path = "\\foo\\bar"; - const char *exp_path = "\\\\?\\foo\\bar"; - const char *new_short = c_path_to_UNC(path); - assert_string_equal(new_short, exp_path); - SAFE_FREE(new_short); - } - - { - const char *path = "/foo/bar"; - const char *exp_path = "\\\\?\\foo\\bar"; - const char *new_short = c_path_to_UNC(path); - assert_string_equal(new_short, exp_path); - SAFE_FREE(new_short); - } - - const char *longPath = "D://alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/" - "elonglonglonglong/flonglonglonglong/glonglonglonglong/hlonglonglonglong/ilonglonglonglong/" - "jlonglonglonglong/klonglonglonglong/llonglonglonglong/mlonglonglonglong/nlonglonglonglong/" - "olonglonglonglong/file.txt"; - const char *longPathConv = "\\\\?\\D:\\\\alonglonglonglong\\blonglonglonglong\\clonglonglonglong\\dlonglonglonglong\\" - "elonglonglonglong\\flonglonglonglong\\glonglonglonglong\\hlonglonglonglong\\ilonglonglonglong\\" - "jlonglonglonglong\\klonglonglonglong\\llonglonglonglong\\mlonglonglonglong\\nlonglonglonglong\\" - "olonglonglonglong\\file.txt"; - - const char *new_long = c_path_to_UNC(longPath); - // printf( "XXXXXXXXXXXX %s %d\n", new_long, mem_reserved); - - assert_string_equal(new_long, longPathConv); - - // printf( "YYYYYYYYYYYY %ld\n", strlen(new_long)); - assert_int_equal( strlen(new_long), 286); - SAFE_FREE(new_long); -} - -int torture_run_tests(void) -{ - const UnitTest tests[] = { - unit_test_setup_teardown(check_long_win_path, setup, teardown), - unit_test_setup_teardown(check_to_multibyte, setup, teardown), - unit_test_setup_teardown(check_iconv_ascii, setup, teardown), - unit_test_setup_teardown(check_iconv_to_native_normalization, setup, teardown), - unit_test_setup_teardown(check_iconv_from_native_normalization, setup, teardown), - - }; - - return run_tests(tests); -} - diff --git a/test/csync/encoding_tests/check_encoding.cpp b/test/csync/encoding_tests/check_encoding.cpp new file mode 100644 index 000000000..eb761d318 --- /dev/null +++ b/test/csync/encoding_tests/check_encoding.cpp @@ -0,0 +1,205 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2013 by Klaas Freitag + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "torture.h" +#include +#include "c_string.h" +#include "c_path.h" + +#ifdef _WIN32 +#include +#endif + + +static void setup(void **state) +{ + (void) state; /* unused */ +} + +static void teardown(void **state) +{ + (void) state; /* unused */ +} + +static void check_iconv_to_native_normalization(void **state) +{ + mbchar_t *out = NULL; + const char *in= "\x48\xc3\xa4"; // UTF8 +#ifdef __APPLE__ + const char *exp_out = "\x48\x61\xcc\x88"; // UTF-8-MAC +#else + const char *exp_out = "\x48\xc3\xa4"; // UTF8 +#endif + + out = c_utf8_path_to_locale(in); + assert_string_equal(out, exp_out); + + c_free_locale_string(out); + assert_null(out); + + (void) state; /* unused */ +} + +static void check_iconv_from_native_normalization(void **state) +{ + char *out = NULL; +#ifdef _WIN32 + const mbchar_t *in = L"\x48\xc3\xa4"; // UTF-8 +#else +#ifdef __APPLE__ + const mbchar_t *in = "\x48\x61\xcc\x88"; // UTF-8-MAC +#else + const mbchar_t *in = "\x48\xc3\xa4"; // UTF-8 +#endif +#endif + const char *exp_out = "\x48\xc3\xa4"; // UTF-8 + + out = c_utf8_from_locale(in); + assert_string_equal(out, exp_out); + + c_free_locale_string(out); + assert_null(out); + + (void) state; /* unused */ +} + +static void check_iconv_ascii(void **state) +{ +#ifdef _WIN32 + const mbchar_t *in = L"abc/ABC\\123"; // UTF-8 +#else +#ifdef __APPLE__ + const mbchar_t *in = "abc/ABC\\123"; // UTF-8-MAC +#else + const mbchar_t *in = "abc/ABC\\123"; // UTF-8 +#endif +#endif + char *out = NULL; + const char *exp_out = "abc/ABC\\123"; + + out = c_utf8_from_locale(in); + assert_string_equal(out, exp_out); + + c_free_locale_string(out); + assert_null(out); + + (void) state; /* unused */ +} + +#define TESTSTRING "#cA\\#fß§4" +#define LTESTSTRING L"#cA\\#fß§4" + +static void check_to_multibyte(void **state) +{ + int rc = -1; + + mbchar_t *mb_string = c_utf8_path_to_locale( TESTSTRING ); + mbchar_t *mb_null = c_utf8_path_to_locale( NULL ); + + (void) state; + +#ifdef _WIN32 + assert_int_equal( wcscmp( LTESTSTRING, mb_string), 0 ); +#else + assert_string_equal(mb_string, TESTSTRING); +#endif + assert_true( mb_null == NULL ); + assert_int_equal(rc, -1); + + c_free_locale_string(mb_string); + c_free_locale_string(mb_null); +} + +static void check_long_win_path(void **state) +{ + (void) state; /* unused */ + + { + const char *path = "C://DATA/FILES/MUSIC/MY_MUSIC.mp3"; // check a short path + const char *exp_path = "\\\\?\\C:\\\\DATA\\FILES\\MUSIC\\MY_MUSIC.mp3"; + const char *new_short = c_path_to_UNC(path); + assert_string_equal(new_short, exp_path); + SAFE_FREE(new_short); + } + + { + const char *path = "\\\\foo\\bar/MY_MUSIC.mp3"; + const char *exp_path = "\\\\foo\\bar\\MY_MUSIC.mp3"; + const char *new_short = c_path_to_UNC(path); + assert_string_equal(new_short, exp_path); + SAFE_FREE(new_short); + } + + { + const char *path = "//foo\\bar/MY_MUSIC.mp3"; + const char *exp_path = "\\\\foo\\bar\\MY_MUSIC.mp3"; + const char *new_short = c_path_to_UNC(path); + assert_string_equal(new_short, exp_path); + SAFE_FREE(new_short); + } + + { + const char *path = "\\foo\\bar"; + const char *exp_path = "\\\\?\\foo\\bar"; + const char *new_short = c_path_to_UNC(path); + assert_string_equal(new_short, exp_path); + SAFE_FREE(new_short); + } + + { + const char *path = "/foo/bar"; + const char *exp_path = "\\\\?\\foo\\bar"; + const char *new_short = c_path_to_UNC(path); + assert_string_equal(new_short, exp_path); + SAFE_FREE(new_short); + } + + const char *longPath = "D://alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/" + "elonglonglonglong/flonglonglonglong/glonglonglonglong/hlonglonglonglong/ilonglonglonglong/" + "jlonglonglonglong/klonglonglonglong/llonglonglonglong/mlonglonglonglong/nlonglonglonglong/" + "olonglonglonglong/file.txt"; + const char *longPathConv = "\\\\?\\D:\\\\alonglonglonglong\\blonglonglonglong\\clonglonglonglong\\dlonglonglonglong\\" + "elonglonglonglong\\flonglonglonglong\\glonglonglonglong\\hlonglonglonglong\\ilonglonglonglong\\" + "jlonglonglonglong\\klonglonglonglong\\llonglonglonglong\\mlonglonglonglong\\nlonglonglonglong\\" + "olonglonglonglong\\file.txt"; + + const char *new_long = c_path_to_UNC(longPath); + // printf( "XXXXXXXXXXXX %s %d\n", new_long, mem_reserved); + + assert_string_equal(new_long, longPathConv); + + // printf( "YYYYYYYYYYYY %ld\n", strlen(new_long)); + assert_int_equal( strlen(new_long), 286); + SAFE_FREE(new_long); +} + +int torture_run_tests(void) +{ + const UnitTest tests[] = { + unit_test_setup_teardown(check_long_win_path, setup, teardown), + unit_test_setup_teardown(check_to_multibyte, setup, teardown), + unit_test_setup_teardown(check_iconv_ascii, setup, teardown), + unit_test_setup_teardown(check_iconv_to_native_normalization, setup, teardown), + unit_test_setup_teardown(check_iconv_from_native_normalization, setup, teardown), + + }; + + return run_tests(tests); +} + diff --git a/test/csync/log_tests/check_log.c b/test/csync/log_tests/check_log.c deleted file mode 100644 index f5f961f72..000000000 --- a/test/csync/log_tests/check_log.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include - -#include "support.h" - -#include "config_csync.h" -#include "csync_log.h" - -static void setup(void) { - csync_log_init(); -} - -static void teardown(void) { - csync_log_fini(); -} - -START_TEST (log_create) -{ - fail_unless((csync_log_init() == 0), NULL); - fail_unless((csync_log_fini() == 0), NULL); -} -END_TEST - -START_TEST (log_load) -{ - char buf[256]; - snprintf(buf, (size_t) 256 - 1, "%s/%s", SOURCEDIR, "config/ocsync_log.conf"); - fail_unless(csync_log_load(buf) == 0); -} -END_TEST - -START_TEST (log_prio) -{ - CSYNC_LOG(CSYNC_LOG_PRIORITY_FATAL, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_FATAL, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_ALERT, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_CRIT, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTSET, "log %s", "test"); - CSYNC_LOG(CSYNC_LOG_PRIORITY_UNKNOWN, "log %s", "test"); -} -END_TEST - -START_TEST (log_null) -{ - char *z = NULL; - CSYNC_LOG(CSYNC_LOG_PRIORITY_UNKNOWN, "log %s", z); -} -END_TEST - -static Suite *log_suite(void) { - Suite *s = suite_create("Logger"); - - create_case(s, "log_create", log_create); - create_case_fixture(s, "log_load", log_load, setup, teardown); - create_case_fixture(s, "log_prio", log_prio, setup, teardown); - create_case_fixture(s, "log_null", log_null, setup, teardown); - - return s; -} - -int main(void) { - int nf; - - Suite *s = log_suite(); - - SRunner *sr; - sr = srunner_create(s); - srunner_run_all(sr, CK_VERBOSE); - nf = srunner_ntests_failed(sr); - srunner_free(sr); - - return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - diff --git a/test/csync/log_tests/check_log.cpp b/test/csync/log_tests/check_log.cpp new file mode 100644 index 000000000..f5f961f72 --- /dev/null +++ b/test/csync/log_tests/check_log.cpp @@ -0,0 +1,98 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "support.h" + +#include "config_csync.h" +#include "csync_log.h" + +static void setup(void) { + csync_log_init(); +} + +static void teardown(void) { + csync_log_fini(); +} + +START_TEST (log_create) +{ + fail_unless((csync_log_init() == 0), NULL); + fail_unless((csync_log_fini() == 0), NULL); +} +END_TEST + +START_TEST (log_load) +{ + char buf[256]; + snprintf(buf, (size_t) 256 - 1, "%s/%s", SOURCEDIR, "config/ocsync_log.conf"); + fail_unless(csync_log_load(buf) == 0); +} +END_TEST + +START_TEST (log_prio) +{ + CSYNC_LOG(CSYNC_LOG_PRIORITY_FATAL, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_FATAL, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_ALERT, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_CRIT, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTICE, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_NOTSET, "log %s", "test"); + CSYNC_LOG(CSYNC_LOG_PRIORITY_UNKNOWN, "log %s", "test"); +} +END_TEST + +START_TEST (log_null) +{ + char *z = NULL; + CSYNC_LOG(CSYNC_LOG_PRIORITY_UNKNOWN, "log %s", z); +} +END_TEST + +static Suite *log_suite(void) { + Suite *s = suite_create("Logger"); + + create_case(s, "log_create", log_create); + create_case_fixture(s, "log_load", log_load, setup, teardown); + create_case_fixture(s, "log_prio", log_prio, setup, teardown); + create_case_fixture(s, "log_null", log_null, setup, teardown); + + return s; +} + +int main(void) { + int nf; + + Suite *s = log_suite(); + + SRunner *sr; + sr = srunner_create(s); + srunner_run_all(sr, CK_VERBOSE); + nf = srunner_ntests_failed(sr); + srunner_free(sr); + + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + diff --git a/test/csync/torture.h b/test/csync/torture.h index b3272b8c1..045518ecc 100644 --- a/test/csync/torture.h +++ b/test/csync/torture.h @@ -20,6 +20,10 @@ #ifndef _TORTURE_H #define _TORTURE_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include @@ -41,4 +45,8 @@ int torture_csync_verbosity(void); */ int torture_run_tests(void); +#ifdef __cplusplus +} +#endif + #endif /* _TORTURE_H */ diff --git a/test/csync/vio_tests/check_vio.c b/test/csync/vio_tests/check_vio.c deleted file mode 100644 index 6d4afd7ef..000000000 --- a/test/csync/vio_tests/check_vio.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include -#include -#include -#include -#include - -#include "torture.h" - -#include "csync_private.h" -#include "vio/csync_vio.h" - -#define CSYNC_TEST_DIR "/tmp/csync_test/" -#define CSYNC_TEST_DIRS "/tmp/csync_test/this/is/a/mkdirs/test" -#define CSYNC_TEST_FILE "/tmp/csync_test/file.txt" - -#define MKDIR_MASK (S_IRWXU |S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) - -#define WD_BUFFER_SIZE 255 - -static char wd_buffer[WD_BUFFER_SIZE]; - -static void setup(void **state) -{ - CSYNC *csync; - int rc; - - assert_non_null(getcwd(wd_buffer, WD_BUFFER_SIZE)); - - rc = system("rm -rf /tmp/csync_test"); - assert_int_equal(rc, 0); - - csync_create(&csync, "/tmp/csync1"); - - csync->replica = LOCAL_REPLICA; - - *state = csync; -} - -static void setup_dir(void **state) { - int rc; - mbchar_t *dir = c_utf8_path_to_locale(CSYNC_TEST_DIR); - - setup(state); - - rc = _tmkdir(dir, MKDIR_MASK); - c_free_locale_string(dir); - assert_int_equal(rc, 0); - - assert_non_null(getcwd(wd_buffer, WD_BUFFER_SIZE)); - - rc = chdir(CSYNC_TEST_DIR); - assert_int_equal(rc, 0); -} - -static void teardown(void **state) { - CSYNC *csync = *state; - int rc; - - rc = csync_destroy(csync); - assert_int_equal(rc, 0); - - rc = chdir(wd_buffer); - assert_int_equal(rc, 0); - - rc = system("rm -rf /tmp/csync_test/"); - assert_int_equal(rc, 0); - - *state = NULL; -} - - -/* - * Test directory function - */ - -static void check_csync_vio_opendir(void **state) -{ - CSYNC *csync = *state; - csync_vio_handle_t *dh; - int rc; - - dh = csync_vio_opendir(csync, CSYNC_TEST_DIR); - assert_non_null(dh); - - rc = csync_vio_closedir(csync, dh); - assert_int_equal(rc, 0); -} - -static void check_csync_vio_opendir_perm(void **state) -{ - CSYNC *csync = *state; - csync_vio_handle_t *dh; - int rc; - mbchar_t *dir = c_utf8_path_to_locale(CSYNC_TEST_DIR); - - assert_non_null(dir); - - rc = _tmkdir(dir, (S_IWUSR|S_IXUSR)); - assert_int_equal(rc, 0); - - dh = csync_vio_opendir(csync, CSYNC_TEST_DIR); - assert_null(dh); - assert_int_equal(errno, EACCES); - - _tchmod(dir, MKDIR_MASK); - c_free_locale_string(dir); -} - -static void check_csync_vio_closedir_null(void **state) -{ - CSYNC *csync = *state; - int rc; - - rc = csync_vio_closedir(csync, NULL); - assert_int_equal(rc, -1); -} - -static void check_csync_vio_readdir(void **state) -{ - CSYNC *csync = *state; - csync_vio_handle_t *dh; - csync_vio_file_stat_t *dirent; - int rc; - - dh = csync_vio_opendir(csync, CSYNC_TEST_DIR); - assert_non_null(dh); - - dirent = csync_vio_readdir(csync, dh); - assert_non_null(dirent); - - csync_vio_file_stat_destroy(dirent); - rc = csync_vio_closedir(csync, dh); - assert_int_equal(rc, 0); -} - - -int torture_run_tests(void) -{ - const UnitTest tests[] = { - unit_test_setup_teardown(check_csync_vio_opendir, setup_dir, teardown), - unit_test_setup_teardown(check_csync_vio_opendir_perm, setup, teardown), - unit_test(check_csync_vio_closedir_null), - unit_test_setup_teardown(check_csync_vio_readdir, setup_dir, teardown), - }; - - return run_tests(tests); -} - diff --git a/test/csync/vio_tests/check_vio.cpp b/test/csync/vio_tests/check_vio.cpp new file mode 100644 index 000000000..a57637e85 --- /dev/null +++ b/test/csync/vio_tests/check_vio.cpp @@ -0,0 +1,166 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include +#include +#include + +#include "torture.h" + +#include "csync_private.h" +#include "vio/csync_vio.h" + +#define CSYNC_TEST_DIR "/tmp/csync_test/" +#define CSYNC_TEST_DIRS "/tmp/csync_test/this/is/a/mkdirs/test" +#define CSYNC_TEST_FILE "/tmp/csync_test/file.txt" + +#define MKDIR_MASK (S_IRWXU |S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) + +#define WD_BUFFER_SIZE 255 + +static char wd_buffer[WD_BUFFER_SIZE]; + +static void setup(void **state) +{ + CSYNC *csync; + int rc; + + assert_non_null(getcwd(wd_buffer, WD_BUFFER_SIZE)); + + rc = system("rm -rf /tmp/csync_test"); + assert_int_equal(rc, 0); + + csync_create(&csync, "/tmp/csync1"); + + csync->replica = LOCAL_REPLICA; + + *state = csync; +} + +static void setup_dir(void **state) { + int rc; + mbchar_t *dir = c_utf8_path_to_locale(CSYNC_TEST_DIR); + + setup(state); + + rc = _tmkdir(dir, MKDIR_MASK); + c_free_locale_string(dir); + assert_int_equal(rc, 0); + + assert_non_null(getcwd(wd_buffer, WD_BUFFER_SIZE)); + + rc = chdir(CSYNC_TEST_DIR); + assert_int_equal(rc, 0); +} + +static void teardown(void **state) { + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_destroy(csync); + assert_int_equal(rc, 0); + + rc = chdir(wd_buffer); + assert_int_equal(rc, 0); + + rc = system("rm -rf /tmp/csync_test/"); + assert_int_equal(rc, 0); + + *state = NULL; +} + + +/* + * Test directory function + */ + +static void check_csync_vio_opendir(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_vio_handle_t *dh; + int rc; + + dh = csync_vio_opendir(csync, CSYNC_TEST_DIR); + assert_non_null(dh); + + rc = csync_vio_closedir(csync, dh); + assert_int_equal(rc, 0); +} + +static void check_csync_vio_opendir_perm(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_vio_handle_t *dh; + int rc; + mbchar_t *dir = c_utf8_path_to_locale(CSYNC_TEST_DIR); + + assert_non_null(dir); + + rc = _tmkdir(dir, (S_IWUSR|S_IXUSR)); + assert_int_equal(rc, 0); + + dh = csync_vio_opendir(csync, CSYNC_TEST_DIR); + assert_null(dh); + assert_int_equal(errno, EACCES); + + _tchmod(dir, MKDIR_MASK); + c_free_locale_string(dir); +} + +static void check_csync_vio_closedir_null(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + int rc; + + rc = csync_vio_closedir(csync, NULL); + assert_int_equal(rc, -1); +} + +static void check_csync_vio_readdir(void **state) +{ + CSYNC *csync = (CSYNC*)*state; + csync_vio_handle_t *dh; + csync_vio_file_stat_t *dirent; + int rc; + + dh = csync_vio_opendir(csync, CSYNC_TEST_DIR); + assert_non_null(dh); + + dirent = csync_vio_readdir(csync, dh); + assert_non_null(dirent); + + csync_vio_file_stat_destroy(dirent); + rc = csync_vio_closedir(csync, dh); + assert_int_equal(rc, 0); +} + + +int torture_run_tests(void) +{ + const UnitTest tests[] = { + unit_test_setup_teardown(check_csync_vio_opendir, setup_dir, teardown), + unit_test_setup_teardown(check_csync_vio_opendir_perm, setup, teardown), + unit_test(check_csync_vio_closedir_null), + unit_test_setup_teardown(check_csync_vio_readdir, setup_dir, teardown), + }; + + return run_tests(tests); +} diff --git a/test/csync/vio_tests/check_vio_ext.c b/test/csync/vio_tests/check_vio_ext.c deleted file mode 100644 index c34e708c0..000000000 --- a/test/csync/vio_tests/check_vio_ext.c +++ /dev/null @@ -1,464 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2015-2013 by Klaas Freitag - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include -#include -#include -#include -#include -#include - -#include "torture.h" - -#include "csync_private.h" -#include "vio/csync_vio.h" - -#ifdef _WIN32 -#include - -#define CSYNC_TEST_DIR "C:/tmp/csync_test" -#else -#define CSYNC_TEST_DIR "/tmp/csync_test" -#endif -#define MKDIR_MASK (S_IRWXU |S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) - -#define WD_BUFFER_SIZE 255 - -static mbchar_t wd_buffer[WD_BUFFER_SIZE]; - -typedef struct { - CSYNC *csync; - char *result; - char *ignored_dir; -} statevar; - -/* remove the complete test dir */ -static int wipe_testdir() -{ - int rc = 0; - -#ifdef _WIN32 - /* The windows system call to rd bails out if the dir is not existing - * Check first. - */ - WIN32_FIND_DATA FindFileData; - - mbchar_t *dir = c_utf8_path_to_locale(CSYNC_TEST_DIR); - HANDLE handle = FindFirstFile(dir, &FindFileData); - c_free_locale_string(dir); - int found = handle != INVALID_HANDLE_VALUE; - - if(found) { - FindClose(handle); - rc = system("rd /s /q C:\\tmp\\csync_test"); - } -#else - rc = system("rm -rf /tmp/csync_test/"); -#endif - return rc; -} - -static void setup_testenv(void **state) { - int rc; - - rc = wipe_testdir(); - assert_int_equal(rc, 0); - - mbchar_t *dir = c_utf8_path_to_locale(CSYNC_TEST_DIR); - - rc = _tmkdir(dir, MKDIR_MASK); - assert_int_equal(rc, 0); - - assert_non_null(_tgetcwd(wd_buffer, WD_BUFFER_SIZE)); - - rc = _tchdir(dir); - assert_int_equal(rc, 0); - - c_free_locale_string(dir); - - /* --- initialize csync */ - statevar *mystate = malloc( sizeof(statevar) ); - mystate->result = NULL; - - csync_create(&(mystate->csync), "/tmp/csync1"); - - mystate->csync->replica = LOCAL_REPLICA; - - *state = mystate; -} - -static void output( const char *text ) -{ - mbchar_t *wtext = c_utf8_string_to_locale(text); - - #ifdef _WIN32 - wprintf(L"OOOO %ls (%ld)\n", wtext, strlen(text)); - #else - printf("%s\n", wtext); - #endif - c_free_locale_string(wtext); -} - -static void teardown(void **state) { - statevar *sv = (statevar*) *state; - CSYNC *csync = sv->csync; - int rc; - - output("================== Tearing down!\n"); - - rc = csync_destroy(csync); - assert_int_equal(rc, 0); - - rc = _tchdir(wd_buffer); - assert_int_equal(rc, 0); - - rc = wipe_testdir(); - assert_int_equal(rc, 0); - - *state = NULL; -} - -/* This function takes a relative path, prepends it with the CSYNC_TEST_DIR - * and creates each sub directory. - */ -static void create_dirs( const char *path ) -{ - int rc; - char *mypath = c_malloc( 2+strlen(CSYNC_TEST_DIR)+strlen(path)); - *mypath = '\0'; - strcat(mypath, CSYNC_TEST_DIR); - strcat(mypath, "/"); - strcat(mypath, path); - - char *p = mypath+strlen(CSYNC_TEST_DIR)+1; /* start behind the offset */ - int i = 0; - - assert_non_null(path); - - while( *(p+i) ) { - if( *(p+i) == '/' ) { - p[i] = '\0'; - - mbchar_t *mb_dir = c_utf8_path_to_locale(mypath); - /* wprintf(L"OOOO %ls (%ld)\n", mb_dir, strlen(mypath)); */ - rc = _tmkdir(mb_dir, MKDIR_MASK); - c_free_locale_string(mb_dir); - - assert_int_equal(rc, 0); - p[i] = '/'; - } - i++; - } - SAFE_FREE(mypath); -} - -/* - * This function uses the vio_opendir, vio_readdir and vio_closedir functions - * to traverse a file tree that was created before by the create_dir function. - * - * It appends a listing to the result member of the incoming struct in *state - * that can be compared later to what was expected in the calling functions. - * - * The int parameter cnt contains the number of seen files (not dirs) in the - * whole tree. - * - */ -static void traverse_dir(void **state, const char *dir, int *cnt) -{ - csync_vio_handle_t *dh; - csync_vio_file_stat_t *dirent; - statevar *sv = (statevar*) *state; - CSYNC *csync = sv->csync; - char *subdir; - char *subdir_out; - int rc; - int is_dir; - - /* Format: Smuggle in the C: for unix platforms as its urgently needed - * on Windows and the test can be nicely cross platform this way. */ -#ifdef _WIN32 - const char *format_str = "%s %s"; -#else - const char *format_str = "%s C:%s"; -#endif - - dh = csync_vio_opendir(csync, dir); - assert_non_null(dh); - - while( (dirent = csync_vio_readdir(csync, dh)) ) { - assert_non_null(dirent); - if (dirent->original_name) { - sv->ignored_dir = c_strdup(dirent->original_name); - continue; - } - - assert_non_null(dirent->name); - assert_int_equal( dirent->fields & CSYNC_VIO_FILE_STAT_FIELDS_TYPE, CSYNC_VIO_FILE_STAT_FIELDS_TYPE ); - - if( c_streq( dirent->name, "..") || c_streq( dirent->name, "." )) { - continue; - } - - is_dir = (dirent->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) ? 1:0; - - assert_int_not_equal( asprintf( &subdir, "%s/%s", dir, dirent->name ), -1 ); - - assert_int_not_equal( asprintf( &subdir_out, format_str, - is_dir ? "":" ", - subdir), -1 ); - - if( is_dir ) { - if( !sv->result ) { - sv->result = c_strdup( subdir_out); - } else { - int newlen = 1+strlen(sv->result)+strlen(subdir_out); - char *tmp = sv->result; - sv->result = c_malloc(newlen); - strcpy( sv->result, tmp); - SAFE_FREE(tmp); - - strcat( sv->result, subdir_out ); - } - } else { - *cnt = *cnt +1; - } - output(subdir_out); - if( is_dir ) { - traverse_dir( state, subdir, cnt); - } - - SAFE_FREE(subdir); - SAFE_FREE(subdir_out); - } - - csync_vio_file_stat_destroy(dirent); - rc = csync_vio_closedir(csync, dh); - assert_int_equal(rc, 0); - -} - -static void create_file( const char *path, const char *name, const char *content) -{ -#ifdef _WIN32 - - char *filepath = c_malloc( 2+strlen(CSYNC_TEST_DIR)+strlen(path) + strlen(name) ); - *filepath = '\0'; - strcpy(filepath, CSYNC_TEST_DIR); - strcat(filepath, "/"); - strcat(filepath, path); - strcat(filepath, name); - - DWORD dwWritten; // number of bytes written to file - HANDLE hFile; - - mbchar_t *w_fname = c_utf8_path_to_locale(filepath); - - hFile=CreateFile(w_fname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); - - assert_int_equal( 0, hFile==INVALID_HANDLE_VALUE ); - - int len = strlen(content); - mbchar_t *dst = NULL; - - dst = c_utf8_string_to_locale(content); - WriteFile(hFile, dst, len * sizeof(mbchar_t), &dwWritten, 0); - - CloseHandle(hFile); - SAFE_FREE(dst); - c_free_locale_string(w_fname); -#else - char *filepath = c_malloc( 1+strlen(path) + strlen(name) ); - *filepath = '\0'; - - strcpy(filepath, path); - strcat(filepath, name); - - FILE *sink; - sink = fopen(filepath,"w"); - - fprintf (sink, "we got: %s",content); - fclose(sink); - SAFE_FREE(filepath); -#endif - -} - -static void check_readdir_shorttree(void **state) -{ - statevar *sv = (statevar*) *state; - - const char *t1 = "alibaba/und/die/vierzig/räuber/"; - create_dirs( t1 ); - int files_cnt = 0; - - traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); - - assert_string_equal( sv->result, - " C:/tmp/csync_test/alibaba" - " C:/tmp/csync_test/alibaba/und" - " C:/tmp/csync_test/alibaba/und/die" - " C:/tmp/csync_test/alibaba/und/die/vierzig" - " C:/tmp/csync_test/alibaba/und/die/vierzig/räuber" ); - assert_int_equal(files_cnt, 0); -} - -static void check_readdir_with_content(void **state) -{ - statevar *sv = (statevar*) *state; - int files_cnt = 0; - - const char *t1 = "warum/nur/40/Räuber/"; - create_dirs( t1 ); - - create_file( t1, "Räuber Max.txt", "Der Max ist ein schlimmer finger"); - create_file( t1, "пя́тница.txt", "Am Freitag tanzt der Ürk"); - - - traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); - - assert_string_equal( sv->result, - " C:/tmp/csync_test/warum" - " C:/tmp/csync_test/warum/nur" - " C:/tmp/csync_test/warum/nur/40" - " C:/tmp/csync_test/warum/nur/40/Räuber"); - /* " C:/tmp/csync_test/warum/nur/40/Räuber/Räuber Max.txt" - " C:/tmp/csync_test/warum/nur/40/Räuber/пя́тница.txt"); */ - assert_int_equal(files_cnt, 2); /* Two files in the sub dir */ -} - -static void check_readdir_longtree(void **state) -{ - statevar *sv = (statevar*) *state; - - /* Strange things here: Compilers only support strings with length of 4k max. - * The expected result string is longer, so it needs to be split up in r1, r2 and r3 - */ - - /* create the test tree */ - const char *t1 = "vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und/BESSER/ZWEI/Butteln/VOLL RUM/"; - create_dirs( t1 ); - - const char *r1 = -" C:/tmp/csync_test/vierzig" -" C:/tmp/csync_test/vierzig/mann" -" C:/tmp/csync_test/vierzig/mann/auf" -" C:/tmp/csync_test/vierzig/mann/auf/des" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum"; - - - const char *r2 = -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH"; - - - const char *r3 = -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und/BESSER" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und/BESSER/ZWEI" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und/BESSER/ZWEI/Butteln" -" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und/BESSER/ZWEI/Butteln/VOLL RUM"; - - /* assemble the result string ... */ - int overall_len = 1+strlen(r1)+strlen(r2)+strlen(r3); - int files_cnt = 0; - char *result = c_malloc(overall_len); - *result = '\0'; - - strcat(result, r1); - strcat(result, r2); - strcat(result, r3); - - traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); - assert_int_equal(files_cnt, 0); - /* and compare. */ - assert_string_equal( sv->result, result); -} - -// https://github.com/owncloud/client/issues/3128 https://github.com/owncloud/client/issues/2777 -static void check_readdir_bigunicode(void **state) -{ - statevar *sv = (statevar*) *state; -// 1: ? ASCII: 239 - EF -// 2: ? ASCII: 187 - BB -// 3: ? ASCII: 191 - BF -// 4: ASCII: 32 - 20 - - char *p = 0; - asprintf( &p, "%s/%s", CSYNC_TEST_DIR, "goodone/" ); - int rc = _tmkdir(p, MKDIR_MASK); - assert_int_equal(rc, 0); - SAFE_FREE(p); - - const char *t1 = "goodone/ugly\xEF\xBB\xBF\x32" ".txt"; // file with encoding error - asprintf( &p, "%s/%s", CSYNC_TEST_DIR, t1 ); - rc = _tmkdir(p, MKDIR_MASK); - SAFE_FREE(p); - - assert_int_equal(rc, 0); - - int files_cnt = 0; - traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); - const char *expected_result = " C:/tmp/csync_test/goodone" - " C:/tmp/csync_test/goodone/ugly\xEF\xBB\xBF\x32" ".txt" - ; - assert_string_equal( sv->result, expected_result); - - assert_int_equal(files_cnt, 0); -} - -int torture_run_tests(void) -{ - const UnitTest tests[] = { - unit_test_setup_teardown(check_readdir_shorttree, setup_testenv, teardown), - unit_test_setup_teardown(check_readdir_with_content, setup_testenv, teardown), - unit_test_setup_teardown(check_readdir_longtree, setup_testenv, teardown), - unit_test_setup_teardown(check_readdir_bigunicode, setup_testenv, teardown), - }; - - return run_tests(tests); -} diff --git a/test/csync/vio_tests/check_vio_ext.cpp b/test/csync/vio_tests/check_vio_ext.cpp new file mode 100644 index 000000000..efa37a5d0 --- /dev/null +++ b/test/csync/vio_tests/check_vio_ext.cpp @@ -0,0 +1,464 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2015-2013 by Klaas Freitag + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include + +#include "torture.h" + +#include "csync_private.h" +#include "vio/csync_vio.h" + +#ifdef _WIN32 +#include + +#define CSYNC_TEST_DIR "C:/tmp/csync_test" +#else +#define CSYNC_TEST_DIR "/tmp/csync_test" +#endif +#define MKDIR_MASK (S_IRWXU |S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) + +#define WD_BUFFER_SIZE 255 + +static mbchar_t wd_buffer[WD_BUFFER_SIZE]; + +typedef struct { + CSYNC *csync; + char *result; + char *ignored_dir; +} statevar; + +/* remove the complete test dir */ +static int wipe_testdir() +{ + int rc = 0; + +#ifdef _WIN32 + /* The windows system call to rd bails out if the dir is not existing + * Check first. + */ + WIN32_FIND_DATA FindFileData; + + mbchar_t *dir = c_utf8_path_to_locale(CSYNC_TEST_DIR); + HANDLE handle = FindFirstFile(dir, &FindFileData); + c_free_locale_string(dir); + int found = handle != INVALID_HANDLE_VALUE; + + if(found) { + FindClose(handle); + rc = system("rd /s /q C:\\tmp\\csync_test"); + } +#else + rc = system("rm -rf /tmp/csync_test/"); +#endif + return rc; +} + +static void setup_testenv(void **state) { + int rc; + + rc = wipe_testdir(); + assert_int_equal(rc, 0); + + mbchar_t *dir = c_utf8_path_to_locale(CSYNC_TEST_DIR); + + rc = _tmkdir(dir, MKDIR_MASK); + assert_int_equal(rc, 0); + + assert_non_null(_tgetcwd(wd_buffer, WD_BUFFER_SIZE)); + + rc = _tchdir(dir); + assert_int_equal(rc, 0); + + c_free_locale_string(dir); + + /* --- initialize csync */ + statevar *mystate = (statevar*)malloc( sizeof(statevar) ); + mystate->result = NULL; + + csync_create(&(mystate->csync), "/tmp/csync1"); + + mystate->csync->replica = LOCAL_REPLICA; + + *state = mystate; +} + +static void output( const char *text ) +{ + mbchar_t *wtext = c_utf8_string_to_locale(text); + + #ifdef _WIN32 + wprintf(L"OOOO %ls (%ld)\n", wtext, strlen(text)); + #else + printf("%s\n", wtext); + #endif + c_free_locale_string(wtext); +} + +static void teardown(void **state) { + statevar *sv = (statevar*) *state; + CSYNC *csync = sv->csync; + int rc; + + output("================== Tearing down!\n"); + + rc = csync_destroy(csync); + assert_int_equal(rc, 0); + + rc = _tchdir(wd_buffer); + assert_int_equal(rc, 0); + + rc = wipe_testdir(); + assert_int_equal(rc, 0); + + *state = NULL; +} + +/* This function takes a relative path, prepends it with the CSYNC_TEST_DIR + * and creates each sub directory. + */ +static void create_dirs( const char *path ) +{ + int rc; + char *mypath = (char*)c_malloc( 2+strlen(CSYNC_TEST_DIR)+strlen(path)); + *mypath = '\0'; + strcat(mypath, CSYNC_TEST_DIR); + strcat(mypath, "/"); + strcat(mypath, path); + + char *p = mypath+strlen(CSYNC_TEST_DIR)+1; /* start behind the offset */ + int i = 0; + + assert_non_null(path); + + while( *(p+i) ) { + if( *(p+i) == '/' ) { + p[i] = '\0'; + + mbchar_t *mb_dir = c_utf8_path_to_locale(mypath); + /* wprintf(L"OOOO %ls (%ld)\n", mb_dir, strlen(mypath)); */ + rc = _tmkdir(mb_dir, MKDIR_MASK); + c_free_locale_string(mb_dir); + + assert_int_equal(rc, 0); + p[i] = '/'; + } + i++; + } + SAFE_FREE(mypath); +} + +/* + * This function uses the vio_opendir, vio_readdir and vio_closedir functions + * to traverse a file tree that was created before by the create_dir function. + * + * It appends a listing to the result member of the incoming struct in *state + * that can be compared later to what was expected in the calling functions. + * + * The int parameter cnt contains the number of seen files (not dirs) in the + * whole tree. + * + */ +static void traverse_dir(void **state, const char *dir, int *cnt) +{ + csync_vio_handle_t *dh; + csync_vio_file_stat_t *dirent; + statevar *sv = (statevar*) *state; + CSYNC *csync = sv->csync; + char *subdir; + char *subdir_out; + int rc; + int is_dir; + + /* Format: Smuggle in the C: for unix platforms as its urgently needed + * on Windows and the test can be nicely cross platform this way. */ +#ifdef _WIN32 + const char *format_str = "%s %s"; +#else + const char *format_str = "%s C:%s"; +#endif + + dh = csync_vio_opendir(csync, dir); + assert_non_null(dh); + + while( (dirent = csync_vio_readdir(csync, dh)) ) { + assert_non_null(dirent); + if (dirent->original_name) { + sv->ignored_dir = c_strdup(dirent->original_name); + continue; + } + + assert_non_null(dirent->name); + assert_int_equal( dirent->fields & CSYNC_VIO_FILE_STAT_FIELDS_TYPE, CSYNC_VIO_FILE_STAT_FIELDS_TYPE ); + + if( c_streq( dirent->name, "..") || c_streq( dirent->name, "." )) { + continue; + } + + is_dir = (dirent->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) ? 1:0; + + assert_int_not_equal( asprintf( &subdir, "%s/%s", dir, dirent->name ), -1 ); + + assert_int_not_equal( asprintf( &subdir_out, format_str, + is_dir ? "":" ", + subdir), -1 ); + + if( is_dir ) { + if( !sv->result ) { + sv->result = c_strdup( subdir_out); + } else { + int newlen = 1+strlen(sv->result)+strlen(subdir_out); + char *tmp = sv->result; + sv->result = (char*)c_malloc(newlen); + strcpy( sv->result, tmp); + SAFE_FREE(tmp); + + strcat( sv->result, subdir_out ); + } + } else { + *cnt = *cnt +1; + } + output(subdir_out); + if( is_dir ) { + traverse_dir( state, subdir, cnt); + } + + SAFE_FREE(subdir); + SAFE_FREE(subdir_out); + } + + csync_vio_file_stat_destroy(dirent); + rc = csync_vio_closedir(csync, dh); + assert_int_equal(rc, 0); + +} + +static void create_file( const char *path, const char *name, const char *content) +{ +#ifdef _WIN32 + + char *filepath = c_malloc( 2+strlen(CSYNC_TEST_DIR)+strlen(path) + strlen(name) ); + *filepath = '\0'; + strcpy(filepath, CSYNC_TEST_DIR); + strcat(filepath, "/"); + strcat(filepath, path); + strcat(filepath, name); + + DWORD dwWritten; // number of bytes written to file + HANDLE hFile; + + mbchar_t *w_fname = c_utf8_path_to_locale(filepath); + + hFile=CreateFile(w_fname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + + assert_int_equal( 0, hFile==INVALID_HANDLE_VALUE ); + + int len = strlen(content); + mbchar_t *dst = NULL; + + dst = c_utf8_string_to_locale(content); + WriteFile(hFile, dst, len * sizeof(mbchar_t), &dwWritten, 0); + + CloseHandle(hFile); + SAFE_FREE(dst); + c_free_locale_string(w_fname); +#else + char *filepath = (char*)c_malloc( 1+strlen(path) + strlen(name) ); + *filepath = '\0'; + + strcpy(filepath, path); + strcat(filepath, name); + + FILE *sink; + sink = fopen(filepath,"w"); + + fprintf (sink, "we got: %s",content); + fclose(sink); + SAFE_FREE(filepath); +#endif + +} + +static void check_readdir_shorttree(void **state) +{ + statevar *sv = (statevar*) *state; + + const char *t1 = "alibaba/und/die/vierzig/räuber/"; + create_dirs( t1 ); + int files_cnt = 0; + + traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); + + assert_string_equal( sv->result, + " C:/tmp/csync_test/alibaba" + " C:/tmp/csync_test/alibaba/und" + " C:/tmp/csync_test/alibaba/und/die" + " C:/tmp/csync_test/alibaba/und/die/vierzig" + " C:/tmp/csync_test/alibaba/und/die/vierzig/räuber" ); + assert_int_equal(files_cnt, 0); +} + +static void check_readdir_with_content(void **state) +{ + statevar *sv = (statevar*) *state; + int files_cnt = 0; + + const char *t1 = "warum/nur/40/Räuber/"; + create_dirs( t1 ); + + create_file( t1, "Räuber Max.txt", "Der Max ist ein schlimmer finger"); + create_file( t1, "пя́тница.txt", "Am Freitag tanzt der Ürk"); + + + traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); + + assert_string_equal( sv->result, + " C:/tmp/csync_test/warum" + " C:/tmp/csync_test/warum/nur" + " C:/tmp/csync_test/warum/nur/40" + " C:/tmp/csync_test/warum/nur/40/Räuber"); + /* " C:/tmp/csync_test/warum/nur/40/Räuber/Räuber Max.txt" + " C:/tmp/csync_test/warum/nur/40/Räuber/пя́тница.txt"); */ + assert_int_equal(files_cnt, 2); /* Two files in the sub dir */ +} + +static void check_readdir_longtree(void **state) +{ + statevar *sv = (statevar*) *state; + + /* Strange things here: Compilers only support strings with length of 4k max. + * The expected result string is longer, so it needs to be split up in r1, r2 and r3 + */ + + /* create the test tree */ + const char *t1 = "vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und/BESSER/ZWEI/Butteln/VOLL RUM/"; + create_dirs( t1 ); + + const char *r1 = +" C:/tmp/csync_test/vierzig" +" C:/tmp/csync_test/vierzig/mann" +" C:/tmp/csync_test/vierzig/mann/auf" +" C:/tmp/csync_test/vierzig/mann/auf/des" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum"; + + + const char *r2 = +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH"; + + + const char *r3 = +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und/BESSER" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und/BESSER/ZWEI" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und/BESSER/ZWEI/Butteln" +" C:/tmp/csync_test/vierzig/mann/auf/des/toten/Mann/kiste/ooooooooooooooooooooooh/and/ne/bottle/voll/rum/und/so/singen/wir/VIERZIG/MANN/AUF/DES/TOTEN/MANNS/KISTE/OOOOOOOOH/AND/NE/BOTTLE/VOLL/RUM/undnochmalallezusammen/VierZig/MannaufDesTotenManns/KISTE/ooooooooooooooooooooooooooohhhhhh/und/BESSER/ZWEI/Butteln/VOLL RUM"; + + /* assemble the result string ... */ + int overall_len = 1+strlen(r1)+strlen(r2)+strlen(r3); + int files_cnt = 0; + char *result = (char*)c_malloc(overall_len); + *result = '\0'; + + strcat(result, r1); + strcat(result, r2); + strcat(result, r3); + + traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); + assert_int_equal(files_cnt, 0); + /* and compare. */ + assert_string_equal( sv->result, result); +} + +// https://github.com/owncloud/client/issues/3128 https://github.com/owncloud/client/issues/2777 +static void check_readdir_bigunicode(void **state) +{ + statevar *sv = (statevar*) *state; +// 1: ? ASCII: 239 - EF +// 2: ? ASCII: 187 - BB +// 3: ? ASCII: 191 - BF +// 4: ASCII: 32 - 20 + + char *p = 0; + asprintf( &p, "%s/%s", CSYNC_TEST_DIR, "goodone/" ); + int rc = _tmkdir(p, MKDIR_MASK); + assert_int_equal(rc, 0); + SAFE_FREE(p); + + const char *t1 = "goodone/ugly\xEF\xBB\xBF\x32" ".txt"; // file with encoding error + asprintf( &p, "%s/%s", CSYNC_TEST_DIR, t1 ); + rc = _tmkdir(p, MKDIR_MASK); + SAFE_FREE(p); + + assert_int_equal(rc, 0); + + int files_cnt = 0; + traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); + const char *expected_result = " C:/tmp/csync_test/goodone" + " C:/tmp/csync_test/goodone/ugly\xEF\xBB\xBF\x32" ".txt" + ; + assert_string_equal( sv->result, expected_result); + + assert_int_equal(files_cnt, 0); +} + +int torture_run_tests(void) +{ + const UnitTest tests[] = { + unit_test_setup_teardown(check_readdir_shorttree, setup_testenv, teardown), + unit_test_setup_teardown(check_readdir_with_content, setup_testenv, teardown), + unit_test_setup_teardown(check_readdir_longtree, setup_testenv, teardown), + unit_test_setup_teardown(check_readdir_bigunicode, setup_testenv, teardown), + }; + + return run_tests(tests); +} diff --git a/test/csync/vio_tests/check_vio_file_stat.c b/test/csync/vio_tests/check_vio_file_stat.c deleted file mode 100644 index a67255e81..000000000 --- a/test/csync/vio_tests/check_vio_file_stat.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * libcsync -- a library to sync a directory with another - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "torture.h" - -#include "csync.h" - -static void check_csync_vio_file_stat_new(void **state) -{ - csync_vio_file_stat_t *tstat; - - (void) state; /* unused */ - - tstat = csync_vio_file_stat_new(); - assert_non_null(tstat); - - csync_vio_file_stat_destroy(tstat); -} - - -int torture_run_tests(void) -{ - const UnitTest tests[] = { - unit_test(check_csync_vio_file_stat_new), - }; - - return run_tests(tests); -} - diff --git a/test/csync/vio_tests/check_vio_file_stat.cpp b/test/csync/vio_tests/check_vio_file_stat.cpp new file mode 100644 index 000000000..a67255e81 --- /dev/null +++ b/test/csync/vio_tests/check_vio_file_stat.cpp @@ -0,0 +1,45 @@ +/* + * libcsync -- a library to sync a directory with another + * + * Copyright (c) 2008-2013 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "torture.h" + +#include "csync.h" + +static void check_csync_vio_file_stat_new(void **state) +{ + csync_vio_file_stat_t *tstat; + + (void) state; /* unused */ + + tstat = csync_vio_file_stat_new(); + assert_non_null(tstat); + + csync_vio_file_stat_destroy(tstat); +} + + +int torture_run_tests(void) +{ + const UnitTest tests[] = { + unit_test(check_csync_vio_file_stat_new), + }; + + return run_tests(tests); +} +