Comparar commits

...

35 Commits

Autor SHA1 Mensagem Data
Christian Kamm a139d1a279 FolderWizard: pass AccountPtr by const&
For review of #5045
2016-11-29 10:39:20 +01:00
Christian Kamm 13e624c38f Fix compilation with Qt < 5.1
For review of #5045
2016-11-29 10:39:08 +01:00
Christian Kamm 276985f6c3 Fix perl tests for sync journal db name 2016-11-25 13:21:34 +01:00
Christian Kamm 49f8143f00 Bugfixes for sync journal name generation and usage
* Use 'user' value for journal name generation
* Save journal name in settings
* Make owncloudcmd choose the right db
2016-11-23 16:48:15 +01:00
Christian Kamm bea7241910 Don't wipe each journal on each start 2016-11-23 16:48:15 +01:00
Christian Kamm 61b4da944c Split folders configuration locations for backwards compatibility 2016-11-23 16:48:15 +01:00
Christian Kamm 6fe1868693 Always migrate .csync_journal to ._sync_xxx 2016-11-23 16:48:15 +01:00
Christian Kamm 9641c7a1e7 Rename sync journal to ._sync_xxx.db.
The added underscore means that older clients will also ignore the
file.
2016-11-23 16:48:15 +01:00
Christian Kamm 3e59a9b316 Merge branch 'master' into dbjournal_per_account 2016-11-23 16:47:56 +01:00
Klaas Freitag 16e28567a6 Folderman: Some comments for the checkPathValidityForNewFolder method. 2016-10-12 18:16:53 +02:00
Klaas Freitag 3bef42db6b folderman: checkPathValidityForNewFolder - correct file path comparison
It now checks based on the correct case sensitivity and also using the new
method for filename comparison.
2016-10-12 14:50:10 +02:00
Klaas Freitag 27d23edacc Utility: Add a function to check if two filenames are equal plus test.
It calls canonical path always and works with the correct case preserving
depending on the platform.
2016-10-12 14:48:00 +02:00
Klaas Freitag e1a48e3c33 Move the journal file name generation to the syncjournaldb class.
As requested by Olivier.
2016-10-10 16:59:17 +02:00
Klaas Freitag 5d13f9290f Fix the folderman test, adopting the checkPathValidity method tests. 2016-10-07 16:24:09 +02:00
Klaas Freitag c84140d293 checkPathValidityForNewFolder: Catch sync folders underneath sym links.
plus some whitespace changes.
2016-10-07 16:23:13 +02:00
Klaas Freitag 838c072ccc Folder Setup: Allow to set up more sync connects to the same folder.
One local folder can now be configured as sync target for multiple
accounts as long as their url and user differ.

Also this patch accepts that the sync folder is behind a symlink.
Also this patch fixes a bug that before the user input was taken
canonically which was not working for the symlink handling.
2016-09-30 14:08:00 +02:00
Klaas Freitag ed6a708460 SyncJournalDb: Migrate the old csync journal to the new name.
Migration strategy to the new filename.
2016-09-29 16:41:11 +02:00
Klaas Freitag 0c9dcdafc2 csync: Only free the statedb filename on destroy.
The filename must not be wiped in csync_commit as that is
happening after every sync. It is only set once in the
constructor of the SyncEngine in csync_init().
2016-09-29 16:36:14 +02:00
Klaas Freitag f0dc3b2deb FolderWatcher: Also ignore the new sync journal file name format. 2016-09-02 16:19:10 +02:00
Klaas Freitag 84ede3f01f Make sync journal name generating a method of SyncJournal.
Before it was in Folder, however, the command line client does not
have the Folder class. To not duplicate code, the function to generate
the sync journal name went to SyncEngine class.
2016-09-02 16:19:10 +02:00
Klaas Freitag 2daf895e43 Documentation: Mention the new format for the journal file. 2016-09-02 16:19:10 +02:00
Klaas Freitag 3b651b2da9 folderman: Clean some comments 2016-09-02 16:19:04 +02:00
Klaas Freitag 6fd930908c csync_tests: Adopted to new cmocka API. 2016-09-02 15:49:54 +02:00
Klaas Freitag 62125a442d csync_exclude: Also exclude the new syncjournal filename. 2016-09-02 15:49:21 +02:00
Klaas Freitag 2d54fb2ff9 csync_update: Do not check to exclude .csync_journal.db
It is indeed already handled by csync_exclude.
2016-09-02 15:48:45 +02:00
Klaas Freitag e46fad52bb Make the sync journal file name a method of the Folder class.
The sync journal name has a dependency on the remote url now.
2016-09-02 12:29:21 +02:00
Klaas Freitag f3cfd2b70b csync: Free statedb file member before realloc 2016-07-17 21:13:17 +02:00
Klaas Freitag 78caa1a712 Fix tests for syncjournaldb 2016-07-17 21:10:07 +02:00
Klaas Freitag 0884ad6517 owncloudcmd: Adopt to new syncJournalDb MD5 based name schema. 2016-07-10 13:05:09 +02:00
Klaas Freitag 37fc4e4332 SyncJournalDb: Cleanup of Constructor interface.
The parameter path is not longer needed.
2016-07-10 13:04:29 +02:00
Klaas Freitag 9cc90159f1 FolderMan: ensureJournalGone needs to know the exact journal name, WIP 2016-07-10 12:57:35 +02:00
Klaas Freitag 4ceee86c66 SyncEngine: Calculate a uniq journal name using the remote account.
That should allow to sync the same local folder to multiple remote destinations.

see #3764
2016-07-10 12:56:43 +02:00
Klaas Freitag b9ea7c3414 csync: Do not compute the journal name in csync_update any more.
Rely on the name that was passed to csync_init()
2016-07-10 12:54:33 +02:00
Klaas Freitag 3033e693be Folder: For wipe reuse the journal name from SyncEngine.
Do not compute on its own any more.
2016-07-10 12:52:47 +02:00
Klaas Freitag a1bc01d3b1 SyncJournalDb: Add a method to set the name of the db file. 2016-07-10 12:51:42 +02:00
41 arquivos alterados com 600 adições e 239 exclusões
+8 -13
Ver Arquivo
@@ -114,7 +114,7 @@ void csync_create(CSYNC **csync, const char *local) {
*csync = ctx;
}
void csync_init(CSYNC *ctx) {
void csync_init(CSYNC *ctx, const char *db_file) {
assert(ctx);
/* Do not initialize twice */
@@ -125,6 +125,9 @@ void csync_init(CSYNC *ctx) {
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);
@@ -146,19 +149,11 @@ int csync_update(CSYNC *ctx) {
}
ctx->status_code = CSYNC_STATUS_OK;
/* create/load statedb */
rc = asprintf(&ctx->statedb.file, "%s/.csync_journal.db",
ctx->local.uri);
if (rc < 0) {
ctx->status_code = CSYNC_STATUS_MEMORY_ERROR;
return rc;
}
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Journal: %s", ctx->statedb.file);
if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) {
/* 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;
@@ -515,7 +510,6 @@ static void _csync_clean_ctx(CSYNC *ctx)
c_rbtree_free(ctx->local.tree);
c_rbtree_free(ctx->remote.tree);
SAFE_FREE(ctx->statedb.file);
SAFE_FREE(ctx->remote.root_perms);
}
@@ -572,6 +566,7 @@ int csync_destroy(CSYNC *ctx) {
_csync_clean_ctx(ctx);
SAFE_FREE(ctx->statedb.file);
SAFE_FREE(ctx->local.uri);
SAFE_FREE(ctx->error_string);
+1 -1
Ver Arquivo
@@ -326,7 +326,7 @@ void OCSYNC_EXPORT csync_create(CSYNC **csync, const char *local);
*
* @param ctx The context to initialize.
*/
void OCSYNC_EXPORT csync_init(CSYNC *ctx);
void OCSYNC_EXPORT csync_init(CSYNC *ctx, const char *db_file);
/**
* @brief Update detection
+5
Ver Arquivo
@@ -230,6 +230,11 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
}
blen = strlen(bname);
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;
-31
Ver Arquivo
@@ -704,8 +704,6 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
}
while ((dirent = csync_vio_readdir(ctx, dh))) {
const char *path = NULL;
size_t ulen = 0;
int flen;
int flag;
@@ -744,35 +742,6 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
goto error;
}
/* Create relative path: For local replica, we need to remove the base path. */
path = filename;
if (ctx->current == LOCAL_REPLICA) {
ulen = strlen(ctx->local.uri) + 1;
if (((size_t)flen) < ulen) {
csync_vio_file_stat_destroy(dirent);
dirent = NULL;
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
goto error;
}
path += ulen;
}
/* skip ".csync_journal.db" and ".csync_journal.db.ctmp" */
/* Isn't this done via csync_exclude already? */
if (c_streq(path, ".csync_journal.db")
|| c_streq(path, ".csync_journal.db.ctmp")
|| c_streq(path, ".csync_journal.db.ctmp-journal")
|| c_streq(path, ".csync-progressdatabase")
|| c_streq(path, ".csync_journal.db-shm")
|| c_streq(path, ".csync_journal.db-wal")
|| c_streq(path, ".csync_journal.db-journal")) {
csync_vio_file_stat_destroy(dirent);
dirent = NULL;
SAFE_FREE(filename);
continue;
}
/* 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);
+14 -8
Ver Arquivo
@@ -23,7 +23,7 @@
#include "csync_private.h"
static void setup(void **state) {
static int setup(void **state) {
CSYNC *csync;
int rc;
@@ -33,9 +33,11 @@ static void setup(void **state) {
csync_create(&csync, "/tmp/check_csync1");
*state = csync;
return 0;
}
static void setup_module(void **state) {
static int setup_module(void **state) {
CSYNC *csync;
int rc;
@@ -44,11 +46,13 @@ static void setup_module(void **state) {
csync_create(&csync, "/tmp/check_csync1");
csync_init(csync);
csync_init(csync, "foo");
*state = csync;
return 0;
}
static void teardown(void **state) {
static int teardown(void **state) {
CSYNC *csync = *state;
int rc;
@@ -61,6 +65,8 @@ static void teardown(void **state) {
assert_int_equal(rc, 0);
*state = NULL;
return 0;
}
static void check_csync_commit(void **state)
@@ -88,10 +94,10 @@ static void check_csync_commit_dummy(void **state)
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test_setup_teardown(check_csync_commit, setup, teardown),
unit_test_setup_teardown(check_csync_commit_dummy, setup_module, teardown),
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 run_tests(tests);
return cmocka_run_group_tests(tests, NULL, NULL);
}
+4 -4
Ver Arquivo
@@ -50,11 +50,11 @@ static void check_csync_create(void **state)
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test(check_csync_destroy_null),
unit_test(check_csync_create),
const struct CMUnitTest tests[] = {
cmocka_unit_test(check_csync_destroy_null),
cmocka_unit_test(check_csync_create),
};
return run_tests(tests);
return cmocka_run_group_tests(tests, NULL, NULL);
}
+28 -13
Ver Arquivo
@@ -29,15 +29,16 @@
#define EXCLUDE_LIST_FILE SOURCEDIR"/../sync-exclude.lst"
static void setup(void **state) {
static int setup(void **state) {
CSYNC *csync;
csync_create(&csync, "/tmp/check_csync1");
*state = csync;
return 0;
}
static void setup_init(void **state) {
static int setup_init(void **state) {
CSYNC *csync;
int rc;
@@ -59,9 +60,10 @@ static void setup_init(void **state) {
assert_int_equal(rc, 0);
*state = csync;
return 0;
}
static void teardown(void **state) {
static int teardown(void **state) {
CSYNC *csync = *state;
int rc;
@@ -74,6 +76,8 @@ static void teardown(void **state) {
assert_int_equal(rc, 0);
*state = NULL;
return 0;
}
static void check_csync_exclude_add(void **state)
@@ -143,6 +147,17 @@ static void check_csync_excluded(void **state)
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);
/* pattern ]*.directory - ignore and remove */
rc = csync_excluded_no_ctx(csync->excludes, "my.~directory", CSYNC_FTW_TYPE_FILE);
@@ -380,16 +395,16 @@ static void check_csync_exclude_expand_escapes(void **state)
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test_setup_teardown(check_csync_exclude_add, setup, teardown),
unit_test_setup_teardown(check_csync_exclude_load, setup, teardown),
unit_test_setup_teardown(check_csync_excluded, setup_init, teardown),
unit_test_setup_teardown(check_csync_excluded_traversal, setup_init, teardown),
unit_test_setup_teardown(check_csync_pathes, setup_init, teardown),
unit_test_setup_teardown(check_csync_is_windows_reserved_word, setup_init, teardown),
unit_test_setup_teardown(check_csync_excluded_performance, setup_init, teardown),
unit_test(check_csync_exclude_expand_escapes),
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 run_tests(tests);
return cmocka_run_group_tests(tests, NULL, NULL);
}
+13 -8
Ver Arquivo
@@ -23,7 +23,7 @@
#include "csync_private.h"
static void setup(void **state) {
static int setup(void **state) {
CSYNC *csync;
int rc;
@@ -33,9 +33,10 @@ static void setup(void **state) {
csync_create(&csync, "/tmp/check_csync1");
*state = csync;
return 0;
}
static void setup_module(void **state) {
static int setup_module(void **state) {
CSYNC *csync;
int rc;
@@ -45,9 +46,10 @@ static void setup_module(void **state) {
csync_create(&csync, "/tmp/check_csync1");
*state = csync;
return 0;
}
static void teardown(void **state) {
static int teardown(void **state) {
CSYNC *csync = *state;
int rc;
@@ -60,24 +62,27 @@ static void teardown(void **state) {
assert_int_equal(rc, 0);
*state = NULL;
return 0;
}
static void check_csync_init(void **state)
{
CSYNC *csync = *state;
csync_init(csync);
csync_init(csync, "");
assert_int_equal(csync->status & CSYNC_STATUS_INIT, 1);
}
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test_setup_teardown(check_csync_init, setup, teardown),
unit_test_setup_teardown(check_csync_init, setup_module, teardown),
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 run_tests(tests);
return cmocka_run_group_tests(tests, NULL, NULL);
}
+11 -7
Ver Arquivo
@@ -26,7 +26,7 @@
#include "csync_log.c"
#include "c_private.h"
static void setup(void **state) {
static int setup(void **state) {
CSYNC *csync;
int rc;
@@ -36,9 +36,11 @@ static void setup(void **state) {
csync_create(&csync, "/tmp/check_csync1");
*state = csync;
return 0;
}
static void teardown(void **state) {
static int teardown(void **state) {
CSYNC *csync = *state;
int rc;
@@ -51,6 +53,8 @@ static void teardown(void **state) {
assert_int_equal(rc, 0);
*state = NULL;
return 0;
}
static void check_log_callback(int verbosity,
@@ -134,11 +138,11 @@ static void check_logging(void **state)
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test(check_set_log_level),
unit_test(check_set_auth_callback),
unit_test_setup_teardown(check_logging, setup, teardown),
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 run_tests(tests);
return cmocka_run_group_tests(tests, NULL, NULL);
}
+3 -3
Ver Arquivo
@@ -48,10 +48,10 @@ static void check_csync_normalize_etag(void **state)
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test(check_csync_normalize_etag),
const struct CMUnitTest tests[] = {
cmocka_unit_test(check_csync_normalize_etag),
};
return run_tests(tests);
return cmocka_run_group_tests(tests, NULL, NULL);
}
@@ -26,7 +26,7 @@
#define TESTDB "/tmp/check_csync1/test.db"
static void setup(void **state) {
static int setup(void **state) {
CSYNC *csync;
int rc;
@@ -47,9 +47,11 @@ static void setup(void **state) {
rc = sqlite3_close(db);
assert_int_equal(rc, SQLITE_OK);
return 0;
}
static void teardown(void **state) {
static int teardown(void **state) {
CSYNC *csync = *state;
int rc;
@@ -60,6 +62,8 @@ static void teardown(void **state) {
assert_int_equal(rc, 0);
*state = NULL;
return 0;
}
static void check_csync_statedb_load(void **state)
@@ -116,11 +120,11 @@ static void check_csync_statedb_close(void **state)
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test_setup_teardown(check_csync_statedb_load, setup, teardown),
unit_test_setup_teardown(check_csync_statedb_close, setup, teardown),
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 run_tests(tests);
return cmocka_run_group_tests(tests, NULL, NULL);
}
@@ -27,7 +27,7 @@
static void setup(void **state)
static int setup(void **state)
{
CSYNC *csync;
int rc = 0;
@@ -39,7 +39,7 @@ static void setup(void **state)
rc = system("mkdir -p /tmp/check_csync");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1");
csync_init(csync);
csync_init(csync, TESTDB);
sqlite3 *db = NULL;
rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
@@ -51,9 +51,11 @@ static void setup(void **state)
assert_int_equal(rc, 0);
*state = csync;
return 0;
}
static void setup_db(void **state)
static int setup_db(void **state)
{
char *errmsg;
int rc = 0;
@@ -89,10 +91,12 @@ static void setup_db(void **state)
assert_int_equal(rc, SQLITE_OK);
sqlite3_close(db);
return 0;
}
static void teardown(void **state) {
static int teardown(void **state) {
CSYNC *csync = *state;
int rc = 0;
@@ -104,6 +108,8 @@ static void teardown(void **state) {
assert_int_equal(rc, 0);
*state = NULL;
return 0;
}
@@ -204,15 +210,15 @@ static void check_csync_statedb_get_stat_by_inode_not_found(void **state)
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test_setup_teardown(check_csync_statedb_query_statement, setup, teardown),
unit_test_setup_teardown(check_csync_statedb_drop_tables, setup, teardown),
unit_test_setup_teardown(check_csync_statedb_insert_metadata, setup, teardown),
unit_test_setup_teardown(check_csync_statedb_write, setup, teardown),
unit_test_setup_teardown(check_csync_statedb_get_stat_by_hash_not_found, setup_db, teardown),
unit_test_setup_teardown(check_csync_statedb_get_stat_by_inode_not_found, setup_db, teardown),
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 run_tests(tests);
return cmocka_run_group_tests(tests, NULL, NULL);
}
+25 -17
Ver Arquivo
@@ -81,7 +81,7 @@ static void statedb_insert_metadata(sqlite3 *db)
}
}
static void setup(void **state)
static int setup(void **state)
{
CSYNC *csync;
int rc;
@@ -92,7 +92,7 @@ static void setup(void **state)
rc = system("mkdir -p /tmp/check_csync1");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1");
csync_init(csync);
csync_init(csync, TESTDB);
/* Create a new db with metadata */
sqlite3 *db;
@@ -109,9 +109,11 @@ static void setup(void **state)
assert_int_equal(rc, 0);
*state = csync;
return 0;
}
static void setup_ftw(void **state)
static int setup_ftw(void **state)
{
CSYNC *csync;
int rc;
@@ -121,7 +123,7 @@ static void setup_ftw(void **state)
rc = system("mkdir -p /tmp/check_csync1");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp");
csync_init(csync);
csync_init(csync, TESTDB);
sqlite3 *db = NULL;
rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
@@ -135,9 +137,11 @@ static void setup_ftw(void **state)
csync->statedb.file = c_strdup( TESTDB );
*state = csync;
return 0;
}
static void teardown(void **state)
static int teardown(void **state)
{
CSYNC *csync = *state;
int rc;
@@ -147,9 +151,11 @@ static void teardown(void **state)
assert_int_equal(rc, 0);
*state = NULL;
return 0;
}
static void teardown_rm(void **state) {
static int teardown_rm(void **state) {
int rc;
teardown(state);
@@ -158,6 +164,8 @@ static void teardown_rm(void **state) {
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 */
@@ -424,19 +432,19 @@ static void check_csync_ftw_failing_fn(void **state)
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test_setup_teardown(check_csync_detect_update, setup, teardown_rm),
unit_test_setup_teardown(check_csync_detect_update_db_none, setup, teardown),
unit_test_setup_teardown(check_csync_detect_update_db_eval, setup, teardown),
unit_test_setup_teardown(check_csync_detect_update_db_rename, setup, teardown),
unit_test_setup_teardown(check_csync_detect_update_db_new, setup, teardown_rm),
unit_test_setup_teardown(check_csync_detect_update_null, setup, teardown_rm),
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),
unit_test_setup_teardown(check_csync_ftw, setup_ftw, teardown_rm),
unit_test_setup_teardown(check_csync_ftw_empty_uri, setup_ftw, teardown_rm),
unit_test_setup_teardown(check_csync_ftw_failing_fn, setup_ftw, 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 run_tests(tests);
return cmocka_run_group_tests(tests, NULL, NULL);
}
+4 -4
Ver Arquivo
@@ -43,11 +43,11 @@ static void check_csync_memstat(void **state)
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test(check_csync_instruction_str),
unit_test(check_csync_memstat),
const struct CMUnitTest tests[] = {
cmocka_unit_test(check_csync_instruction_str),
cmocka_unit_test(check_csync_memstat),
};
return run_tests(tests);
return cmocka_run_group_tests(tests, NULL, NULL);
}
+1
Ver Arquivo
@@ -457,6 +457,7 @@ sub traverse( $$;$ )
$isHere = 1 if( $acceptConflicts && !$isHere && $f =~ /_conflict/ );
$isHere = 1 if( $f =~ /\.csync/ );
$isHere = 1 if( $f =~ /\._sync_/ );
assert( $isHere, "Filename local, but not remote: $f" );
}
+1 -1
Ver Arquivo
@@ -176,7 +176,7 @@ assertLocalAndRemoteDir( 'remoteToLocal1', 1);
printInfo("simulate a owncloud 5 update by removing all the fileid");
## simulate a owncloud 5 update by removing all the fileid
system( "sqlite3 " . localDir() . ".csync_journal.db \"UPDATE metadata SET fileid='';\"");
system( "sqlite3 " . localDir() . "._sync_*.db \"UPDATE metadata SET fileid='';\"");
#refresh the ids
csync();
assertLocalAndRemoteDir( 'remoteToLocal1', 1);
+1 -1
Ver Arquivo
@@ -61,7 +61,7 @@ sub getETagFromJournal($$)
{
my ($name,$num) = @_;
my $sql = "sqlite3 " . localDir() . ".csync_journal.db \"SELECT md5 FROM metadata WHERE path='$name';\"";
my $sql = "sqlite3 " . localDir() . "._sync_*.db \"SELECT md5 FROM metadata WHERE path='$name';\"";
open(my $fh, '-|', $sql) or die $!;
my $etag = <$fh>;
close $fh;
+5 -5
Ver Arquivo
@@ -37,8 +37,8 @@ sub assertCsyncJournalOk {
my $path = $_[0];
# FIXME: should test also remoteperm but it's not working with owncloud6
# my $cmd = 'sqlite3 ' . $path . '.csync_journal.db "SELECT count(*) from metadata where length(remotePerm) == 0 or length(fileId) == 0"';
my $cmd = 'sqlite3 ' . $path . '.csync_journal.db "SELECT count(*) from metadata where length(fileId) == 0"';
# my $cmd = 'sqlite3 ' . $path . '._sync_*.db "SELECT count(*) from metadata where length(remotePerm) == 0 or length(fileId) == 0"';
my $cmd = 'sqlite3 ' . $path . '._sync_*.db "SELECT count(*) from metadata where length(fileId) == 0"';
my $result = `$cmd`;
assert($result == "0");
}
@@ -170,14 +170,14 @@ assertLocalAndRemoteDir( '', 0);
#######################################################################
printInfo( "move a directory in a outside read only folder" );
system("sqlite3 " . localDir().'.csync_journal.db .dump');
system("sqlite3 " . localDir().'._sync_*.db .dump');
#Missing directory should be restored
#new directory should be uploaded
system("mv " . localDir().'readonlyDirectory_PERM_M_/subdir_PERM_CK_ ' . localDir().'normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_' );
csync();
system("sqlite3 " . localDir().'.csync_journal.db .dump');
system("sqlite3 " . localDir().'._sync_*.db .dump');
assertCsyncJournalOk(localDir());
# old name restored
@@ -229,7 +229,7 @@ system("rm -r " . localDir(). "readonlyDirectory_PERM_M_/moved_PERM_CK_");
assertLocalAndRemoteDir( '', 0);
system("sqlite3 " . localDir().'.csync_journal.db .dump');
system("sqlite3 " . localDir().'._sync_*.db .dump');
#######################################################################
+2 -1
Ver Arquivo
@@ -153,7 +153,8 @@ By default, the ownCloud Client ignores the following files:
* Files matched by one of the patterns defined in the Ignored Files Editor
* Files containing characters that do not work on certain file systems ``(`\, /, :, ?, *, ", >, <, |`)``.
* Files starting with ``.csync_journal.db``, as these files are reserved for journalling.
* Files starting with ``._sync_xxxxxxx.db`` and the old format ``.csync_journal.db``,
as these files are reserved for journalling.
If a pattern selected using a checkbox in the `ignoredFilesEditor-label` (or if
a line in the exclude file starts with the character ``]`` directly followed by
+14 -4
Ver Arquivo
@@ -278,7 +278,6 @@ void selectiveSyncFixup(OCC::SyncJournalDb *journal, const QStringList &newList)
}
}
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
@@ -383,11 +382,20 @@ int main(int argc, char **argv) {
QByteArray remUrl = options.target_url.toUtf8();
// Find the folder and the original owncloud url
QStringList splitted = url.path().split(account->davPath());
QStringList splitted = url.path().split("/" + account->davPath());
url.setPath(splitted.value(0));
url.setScheme(url.scheme().replace("owncloud", "http"));
QString folder = splitted.value(1);
QUrl credentialFreeUrl = url;
credentialFreeUrl.setUserName(QString());
credentialFreeUrl.setPassword(QString());
// Remote folders typically start with a / and don't end with one
QString folder = "/" + splitted.value(1);
if (folder.endsWith("/") && folder != "/") {
folder.chop(1);
}
SimpleSslErrorHandler *sslErrorHandler = new SimpleSslErrorHandler;
@@ -470,7 +478,9 @@ restart_sync:
}
Cmd cmd;
SyncJournalDb db(options.source_dir);
QString dbPath = options.source_dir + SyncJournalDb::makeDbName(credentialFreeUrl, folder, user);
SyncJournalDb db(dbPath);
if (!selectiveSyncList.empty()) {
selectiveSyncFixup(&db, selectiveSyncList);
}
+2 -1
Ver Arquivo
@@ -289,7 +289,8 @@ void AccountSettings::slotFolderWizardAccepted()
FolderDefinition definition;
definition.localPath = FolderDefinition::prepareLocalPath(
folderWizard->field(QLatin1String("sourceFolder")).toString());
definition.targetPath = folderWizard->property("targetPath").toString();
definition.targetPath = FolderDefinition::prepareTargetPath(
folderWizard->property("targetPath").toString());
{
QDir dir(definition.localPath);
+71 -6
Ver Arquivo
@@ -46,6 +46,7 @@
namespace OCC {
const char oldJournalPath[] = ".csync_journal.db";
Folder::Folder(const FolderDefinition& definition,
AccountState* accountState,
@@ -60,8 +61,9 @@ Folder::Folder(const FolderDefinition& definition,
, _lastSyncDuration(0)
, _consecutiveFailingSyncs(0)
, _consecutiveFollowUpSyncs(0)
, _journal(definition.localPath)
, _journal(_definition.absoluteJournalPath())
, _fileLog(new SyncRunFileLog)
, _saveBackwardsCompatible(false)
{
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
@@ -124,6 +126,7 @@ Folder::~Folder()
_engine.reset();
}
void Folder::checkLocalPath()
{
const QFileInfo fi(_definition.localPath);
@@ -203,7 +206,7 @@ void Folder::setIgnoreHiddenFiles(bool ignore)
_definition.ignoreHiddenFiles = ignore;
}
QString Folder::cleanPath()
QString Folder::cleanPath() const
{
QString cleanedPath = QDir::cleanPath(_canonicalLocalPath);
@@ -584,8 +587,33 @@ void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
void Folder::saveToSettings() const
{
// Remove first to make sure we don't get duplicates
removeFromSettings();
auto settings = _accountState->settings();
settings->beginGroup(QLatin1String("Folders"));
// The folder is saved to backwards-compatible "Folders"
// section only if it has the migrate flag set (i.e. was in
// there before) or if the folder is the only one for the
// given target path.
// This ensures that older clients will not read a configuration
// where two folders for different accounts point at the same
// local folders.
bool oneAccountOnly = true;
foreach (Folder* other, FolderMan::instance()->map()) {
if (other != this && other->cleanPath() == this->cleanPath()) {
oneAccountOnly = false;
break;
}
}
bool compatible = _saveBackwardsCompatible || oneAccountOnly;
if (compatible) {
settings->beginGroup(QLatin1String("Folders"));
} else {
settings->beginGroup(QLatin1String("Multifolders"));
}
FolderDefinition::save(*settings, _definition);
settings->sync();
@@ -594,9 +622,12 @@ void Folder::saveToSettings() const
void Folder::removeFromSettings() const
{
auto settings = _accountState->settings();
auto settings = _accountState->settings();
settings->beginGroup(QLatin1String("Folders"));
settings->remove(FolderMan::escapeAlias(_definition.alias));
settings->endGroup();
settings->beginGroup(QLatin1String("Multifolders"));
settings->remove(FolderMan::escapeAlias(_definition.alias));
}
bool Folder::isFileExcludedAbsolute(const QString& fullPath) const
@@ -628,12 +659,12 @@ void Folder::slotTerminateSync()
// local folder is synced to the same ownCloud.
void Folder::wipe()
{
QString stateDbFile = path()+QLatin1String(".csync_journal.db");
QString stateDbFile = _engine->journal()->databaseFilePath();
// Delete files that have been partially downloaded.
slotDiscardDownloadProgress();
//Unregister the socket API so it does not keep the .sync_journal file open
//Unregister the socket API so it does not keep the ._sync_journal file open
FolderMan::instance()->socketApi()->slotUnregisterPath(alias());
_journal.close(); // close the sync journal
@@ -950,6 +981,11 @@ void Folder::scheduleThisFolderSoon()
}
}
void Folder::setSaveBackwardsCompatible(bool save)
{
_saveBackwardsCompatible = save;
}
void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool *cancel)
{
ConfigFile cfgFile;
@@ -1002,6 +1038,7 @@ void FolderDefinition::save(QSettings& settings, const FolderDefinition& folder)
{
settings.beginGroup(FolderMan::escapeAlias(folder.alias));
settings.setValue(QLatin1String("localPath"), folder.localPath);
settings.setValue(QLatin1String("journalPath"), folder.journalPath);
settings.setValue(QLatin1String("targetPath"), folder.targetPath);
settings.setValue(QLatin1String("paused"), folder.paused);
settings.setValue(QLatin1String("ignoreHiddenFiles"), folder.ignoreHiddenFiles);
@@ -1014,6 +1051,7 @@ bool FolderDefinition::load(QSettings& settings, const QString& alias,
settings.beginGroup(alias);
folder->alias = FolderMan::unescapeAlias(alias);
folder->localPath = settings.value(QLatin1String("localPath")).toString();
folder->journalPath = settings.value(QLatin1String("journalPath")).toString();
folder->targetPath = settings.value(QLatin1String("targetPath")).toString();
folder->paused = settings.value(QLatin1String("paused")).toBool();
folder->ignoreHiddenFiles = settings.value(QLatin1String("ignoreHiddenFiles"), QVariant(true)).toBool();
@@ -1023,6 +1061,9 @@ bool FolderDefinition::load(QSettings& settings, const QString& alias,
// code we assum /, so clean it up now.
folder->localPath = prepareLocalPath(folder->localPath);
// Target paths also have a convention
folder->targetPath = prepareTargetPath(folder->targetPath);
return true;
}
@@ -1035,5 +1076,29 @@ QString FolderDefinition::prepareLocalPath(const QString& path)
return p;
}
QString FolderDefinition::prepareTargetPath(const QString &path)
{
QString p = path;
if (p.endsWith(QLatin1Char('/'))) {
p.chop(1);
}
// Doing this second ensures the empty string or "/" come
// out as "/".
if (!p.startsWith(QLatin1Char('/'))) {
p.prepend(QLatin1Char('/'));
}
return p;
}
QString FolderDefinition::absoluteJournalPath() const
{
return QDir(localPath).filePath(journalPath);
}
QString FolderDefinition::defaultJournalPath(AccountPtr account)
{
return SyncJournalDb::makeDbName(account->url(), targetPath, account->credentials()->user());
}
} // namespace OCC
+28 -1
Ver Arquivo
@@ -53,6 +53,8 @@ public:
QString alias;
/// path on local machine
QString localPath;
/// path to the journal, usually relative to localPath
QString journalPath;
/// path on remote
QString targetPath;
/// whether the folder is paused
@@ -69,6 +71,15 @@ public:
/// Ensure / as separator and trailing /.
static QString prepareLocalPath(const QString& path);
/// Ensure starting / and no ending /.
static QString prepareTargetPath(const QString& path);
/// journalPath relative to localPath.
QString absoluteJournalPath() const;
/// Returns the relative journal path that's appropriate for this folder and account.
QString defaultJournalPath(AccountPtr account);
};
/**
@@ -111,7 +122,7 @@ public:
/**
* wrapper for QDir::cleanPath("Z:\\"), which returns "Z:\\", but we need "Z:" instead
*/
QString cleanPath();
QString cleanPath() const;
/**
* remote folder path
@@ -206,6 +217,12 @@ public:
*/
void scheduleThisFolderSoon();
/**
* Migration: When this flag is true, this folder will save to
* the backwards-compatible 'Folders' section in the config file.
*/
void setSaveBackwardsCompatible(bool save);
signals:
void syncStateChange();
void syncStarted();
@@ -334,6 +351,16 @@ private:
QScopedPointer<SyncRunFileLog> _fileLog;
QTimer _scheduleSelfTimer;
/**
* When the same local path is synced to multiple accounts, only one
* of them can be stored in the settings in a way that's compatible
* with old clients that don't support it. This flag marks folders
* that shall be written in a backwards-compatible way, by being set
* on the *first* Folder instance that was configured for each local
* path.
*/
bool _saveBackwardsCompatible;
};
}
+109 -44
Ver Arquivo
@@ -209,18 +209,16 @@ int FolderMan::setupFolders()
continue;
}
settings->beginGroup(id);
settings->beginGroup(QLatin1String("Folders"));
foreach (const auto& folderAlias, settings->childGroups()) {
FolderDefinition folderDefinition;
if (FolderDefinition::load(*settings, folderAlias, &folderDefinition)) {
Folder* f = addFolderInternal(std::move(folderDefinition), account.data());
if (f) {
scheduleFolder(f);
emit folderSyncStateChange(f);
}
}
}
settings->endGroup(); // Folders
setupFoldersHelper(*settings, account, true);
settings->endGroup();
// See Folder::saveToSettings for details about why this exists.
settings->beginGroup(QLatin1String("Multifolders"));
setupFoldersHelper(*settings, account, false);
settings->endGroup();
settings->endGroup(); // <account>
}
@@ -229,6 +227,34 @@ int FolderMan::setupFolders()
return _folderMap.size();
}
void FolderMan::setupFoldersHelper(QSettings &settings, AccountStatePtr account, bool backwardsCompatible)
{
foreach (const auto& folderAlias, settings.childGroups()) {
FolderDefinition folderDefinition;
if (FolderDefinition::load(settings, folderAlias, &folderDefinition)) {
// Migration: Old settings don't have journalPath
if (folderDefinition.journalPath.isEmpty()) {
folderDefinition.journalPath = folderDefinition.defaultJournalPath(account->account());
}
folderDefinition.defaultJournalPath(account->account());
// Migration: If an old db is found, move it to the new name.
if (backwardsCompatible) {
SyncJournalDb::maybeMigrateDb(folderDefinition.localPath, folderDefinition.absoluteJournalPath());
}
Folder* f = addFolderInternal(std::move(folderDefinition), account.data());
if (f) {
// Migration: Mark folders that shall be saved in a backwards-compatible way
if (backwardsCompatible) {
f->setSaveBackwardsCompatible(true);
}
scheduleFolder(f);
emit folderSyncStateChange(f);
}
}
}
}
int FolderMan::setupFoldersMigration()
{
ConfigFile cfg;
@@ -259,18 +285,16 @@ int FolderMan::setupFoldersMigration()
return _folderMap.size();
}
bool FolderMan::ensureJournalGone(const QString &localPath)
bool FolderMan::ensureJournalGone( const QString& journalDbFile )
{
// FIXME move this to UI, not libowncloudsync
// remove old .csync_journal file
QString stateDbFile = localPath+QLatin1String("/.csync_journal.db");
while (QFile::exists(stateDbFile) && !QFile::remove(stateDbFile)) {
qDebug() << "Could not remove old db file at" << stateDbFile;
// remove the old journal file
while (QFile::exists(journalDbFile) && !QFile::remove(journalDbFile)) {
qDebug() << "Could not remove old db file at" << journalDbFile;
int ret = QMessageBox::warning(0, tr("Could not reset folder state"),
tr("An old sync journal '%1' was found, "
"but could not be removed. Please make sure "
"that no application is currently using it.")
.arg(QDir::fromNativeSeparators(QDir::cleanPath(stateDbFile))),
.arg(QDir::fromNativeSeparators(QDir::cleanPath(journalDbFile))),
QMessageBox::Retry|QMessageBox::Abort);
if (ret == QMessageBox::Abort) {
return false;
@@ -833,11 +857,27 @@ void FolderMan::slotFolderSyncFinished( const SyncResult& )
Folder* FolderMan::addFolder(AccountState* accountState, const FolderDefinition& folderDefinition)
{
if (!ensureJournalGone(folderDefinition.localPath)) {
// Choose a db filename
auto definition = folderDefinition;
definition.journalPath = definition.defaultJournalPath(accountState->account());
if (!ensureJournalGone(definition.absoluteJournalPath())) {
return 0;
}
auto folder = addFolderInternal(folderDefinition, accountState);
auto folder = addFolderInternal(definition, accountState);
// Migration: The first account that's configured for a local folder shall
// be saved in a backwards-compatible way.
bool oneAccountOnly = true;
foreach (Folder* other, FolderMan::instance()->map()) {
if (other != folder && other->cleanPath() == folder->cleanPath()) {
oneAccountOnly = false;
break;
}
}
folder->setSaveBackwardsCompatible(oneAccountOnly);
if(folder) {
folder->saveToSettings();
emit folderSyncStateChange(folder);
@@ -846,7 +886,8 @@ Folder* FolderMan::addFolder(AccountState* accountState, const FolderDefinition&
return folder;
}
Folder* FolderMan::addFolderInternal(FolderDefinition folderDefinition, AccountState* accountState)
Folder* FolderMan::addFolderInternal(FolderDefinition folderDefinition,
AccountState* accountState)
{
auto alias = folderDefinition.alias;
int count = 0;
@@ -1209,17 +1250,16 @@ QString FolderMan::statusToString( SyncResult syncStatus, bool paused ) const
return folderMessage;
}
QString FolderMan::checkPathValidityForNewFolder(const QString& path, bool forNewDirectory)
QString FolderMan::checkPathValidityForNewFolder(const QString& path, const QUrl &serverUrl, bool forNewDirectory)
{
if (path.isEmpty()) {
return tr("No valid folder selected!");
}
QFileInfo selFile( path );
QString userInput = selFile.canonicalFilePath();
if (!selFile.exists()) {
return checkPathValidityForNewFolder(selFile.dir().path(), true);
return checkPathValidityForNewFolder(selFile.dir().path(), serverUrl, true);
}
if( !selFile.isDir() ) {
@@ -1231,6 +1271,10 @@ QString FolderMan::checkPathValidityForNewFolder(const QString& path, bool forNe
}
// check if the local directory isn't used yet in another ownCloud sync
Qt::CaseSensitivity cs = Qt::CaseSensitive;
if( Utility::fsCasePreserving() ) {
cs = Qt::CaseInsensitive;
}
for (auto i = _folderMap.constBegin(); i != _folderMap.constEnd(); ++i ) {
Folder *f = static_cast<Folder*>(i.value());
@@ -1238,39 +1282,60 @@ QString FolderMan::checkPathValidityForNewFolder(const QString& path, bool forNe
if( folderDir.isEmpty() ) {
continue;
}
if( ! folderDir.endsWith(QLatin1Char('/')) ) folderDir.append(QLatin1Char('/'));
if( ! folderDir.endsWith(QLatin1Char('/'), cs) ) folderDir.append(QLatin1Char('/'));
if (QDir::cleanPath(f->path()) == QDir::cleanPath(userInput)
&& QDir::cleanPath(QDir(f->path()).canonicalPath()) == QDir(userInput).canonicalPath()) {
return tr("The local folder %1 is already used in a folder sync connection. "
"Please pick another one!")
.arg(QDir::toNativeSeparators(userInput));
}
if (!forNewDirectory && QDir::cleanPath(folderDir).startsWith(QDir::cleanPath(userInput)+'/')) {
const QString folderDirClean = QDir::cleanPath(folderDir)+'/';
const QString userDirClean = QDir::cleanPath(path)+'/';
// folderDir follows sym links, path not.
bool differentPathes = !Utility::fileNamesEqual(QDir::cleanPath(folderDir), QDir::cleanPath(path));
if (!forNewDirectory && differentPathes && folderDirClean.startsWith(userDirClean,cs)) {
return tr("The local folder %1 already contains a folder used in a folder sync connection. "
"Please pick another one!")
.arg(QDir::toNativeSeparators(userInput));
.arg(QDir::toNativeSeparators(path));
}
QString absCleanUserFolder = QDir::cleanPath(QDir(userInput).canonicalPath())+'/';
if (!forNewDirectory && QDir::cleanPath(folderDir).startsWith(absCleanUserFolder) ) {
return tr("The local folder %1 is a symbolic link. "
"The link target already contains a folder used in a folder sync connection. "
"Please pick another one!")
.arg(QDir::toNativeSeparators(userInput));
}
// QDir::cleanPath keeps links
// canonicalPath() remove symlinks and uses the symlink targets.
QString absCleanUserFolder = QDir::cleanPath(QDir(path).canonicalPath())+'/';
if (QDir::cleanPath(QString(userInput)).startsWith( QDir::cleanPath(folderDir)+'/')) {
if ( (forNewDirectory || differentPathes) && userDirClean.startsWith( folderDirClean, cs )) {
return tr("The local folder %1 is already contained in a folder used in a folder sync connection. "
"Please pick another one!")
.arg(QDir::toNativeSeparators(userInput));
.arg(QDir::toNativeSeparators(path));
}
if (absCleanUserFolder.startsWith( QDir::cleanPath(folderDir)+'/')) {
// both follow symlinks.
bool cleanUserEqualsCleanFolder = Utility::fileNamesEqual(absCleanUserFolder, folderDirClean );
if (differentPathes && absCleanUserFolder.startsWith( folderDirClean, cs ) &&
! cleanUserEqualsCleanFolder ) {
return tr("The local folder %1 is a symbolic link. "
"The link target is already contained in a folder used in a folder sync connection. "
"Please pick another one!")
.arg(QDir::toNativeSeparators(userInput));
.arg(QDir::toNativeSeparators(path));
}
if (differentPathes && folderDirClean.startsWith(absCleanUserFolder, cs) &&
!cleanUserEqualsCleanFolder && !forNewDirectory ) {
return tr("The local folder %1 contains a symbolic link. "
"The link target contains an already synced folder "
"Please pick another one!")
.arg(QDir::toNativeSeparators(path));
}
// if both pathes are equal, the server url needs to be different
// otherwise it would mean that a new connection from the same local folder
// to the same account is added which is not wanted. The account must differ.
if( serverUrl.isValid() && Utility::fileNamesEqual(absCleanUserFolder,folderDir ) ) {
QUrl folderUrl = f->accountState()->account()->url();
QString user = f->accountState()->account()->credentials()->user();
folderUrl.setUserName(user);
if( serverUrl == folderUrl ) {
return tr("There is already a sync from the server to this local folder. "
"Please pick another local folder!");
}
}
}
+7 -4
Ver Arquivo
@@ -97,11 +97,11 @@ public:
Folder* setupFolderFromOldConfigFile(const QString &, AccountState *account );
/**
* Ensures that a given directory does not contain a .csync_journal.
* Ensures that a given directory does not contain a sync journal file.
*
* @returns false if the journal could not be removed, true otherwise.
*/
static bool ensureJournalGone(const QString &path);
static bool ensureJournalGone(const QString& journalDbFile);
/** Creates a new and empty local directory. */
bool startFromScratch( const QString& );
@@ -128,7 +128,7 @@ public:
*
* @returns an empty string if it is allowed, or an error if it is not allowed
*/
QString checkPathValidityForNewFolder(const QString &path, bool forNewDirectory = false);
QString checkPathValidityForNewFolder(const QString &path, const QUrl& serverUrl = QUrl(), bool forNewDirectory = false);
/**
* While ignoring hidden files can theoretically be switched per folder,
@@ -257,7 +257,8 @@ private:
/** Adds a new folder, does not add it to the account settings and
* does not set an account on the new folder.
*/
Folder* addFolderInternal(FolderDefinition folderDefinition, AccountState* accountState);
Folder* addFolderInternal(FolderDefinition folderDefinition,
AccountState* accountState);
/* unloads a folder object, does not delete it */
void unloadFolder( Folder * );
@@ -273,6 +274,8 @@ private:
// restarts the application (Linux only)
void restartApplication();
void setupFoldersHelper(QSettings& settings, AccountStatePtr account, bool backwardsCompatible);
QSet<Folder*> _disabledFolders;
Folder::Map _folderMap;
QString _folderConfigPath;
+2 -1
Ver Arquivo
@@ -169,7 +169,8 @@ void FolderWatcherPrivate::slotReceivedNotification(int fd)
if (event->len > 0 && event->wd > -1) {
QByteArray fileName(event->name);
// qDebug() << Q_FUNC_INFO << event->name;
if (fileName.startsWith(".csync_journal.db") ||
if (fileName.startsWith("._sync_") ||
fileName.startsWith(".csync_journal.db") ||
fileName.startsWith(".owncloudsync.log")) {
// qDebug() << "ignore journal";
} else {
+12 -6
Ver Arquivo
@@ -56,8 +56,9 @@ QString FormatWarningsWizardPage::formatWarnings(const QStringList &warnings) co
return ret;
}
FolderWizardLocalPath::FolderWizardLocalPath()
: FormatWarningsWizardPage()
FolderWizardLocalPath::FolderWizardLocalPath(const AccountPtr& account)
: FormatWarningsWizardPage(),
_account(account)
{
_ui.setupUi(this);
registerField(QLatin1String("sourceFolder*"), _ui.localFolderLineEdit);
@@ -89,8 +90,13 @@ void FolderWizardLocalPath::cleanupPage()
bool FolderWizardLocalPath::isComplete() const
{
QUrl serverUrl = _account->url();
serverUrl.setUserName( _account->credentials()->user() );
QString errorStr = FolderMan::instance()->checkPathValidityForNewFolder(
QDir::fromNativeSeparators(_ui.localFolderLineEdit->text()));
QDir::fromNativeSeparators(_ui.localFolderLineEdit->text()), serverUrl);
bool isOk = errorStr.isEmpty();
QStringList warnStrings;
@@ -133,7 +139,7 @@ void FolderWizardLocalPath::slotChooseLocalFolder()
}
// =================================================================================
FolderWizardRemotePath::FolderWizardRemotePath(AccountPtr account)
FolderWizardRemotePath::FolderWizardRemotePath(const AccountPtr& account)
: FormatWarningsWizardPage()
,_warnWasVisible(false)
,_account(account)
@@ -473,7 +479,7 @@ void FolderWizardRemotePath::showWarn( const QString& msg ) const
// ====================================================================================
FolderWizardSelectiveSync::FolderWizardSelectiveSync(AccountPtr account)
FolderWizardSelectiveSync::FolderWizardSelectiveSync(const AccountPtr& account)
{
QVBoxLayout *layout = new QVBoxLayout(this);
_treeView = new SelectiveSyncTreeView(account, this);
@@ -527,7 +533,7 @@ void FolderWizardSelectiveSync::cleanupPage()
FolderWizard::FolderWizard(AccountPtr account, QWidget *parent)
: QWizard(parent),
_folderWizardSourcePage(new FolderWizardLocalPath),
_folderWizardSourcePage(new FolderWizardLocalPath(account)),
_folderWizardTargetPage(0),
_folderWizardSelectiveSyncPage(new FolderWizardSelectiveSync(account))
{
+4 -3
Ver Arquivo
@@ -49,7 +49,7 @@ class FolderWizardLocalPath : public FormatWarningsWizardPage
{
Q_OBJECT
public:
FolderWizardLocalPath();
explicit FolderWizardLocalPath(const AccountPtr& account);
~FolderWizardLocalPath();
virtual bool isComplete() const Q_DECL_OVERRIDE;
@@ -63,6 +63,7 @@ protected slots:
private:
Ui_FolderWizardSourcePage _ui;
Folder::Map _folderMap;
AccountPtr _account;
};
@@ -75,7 +76,7 @@ class FolderWizardRemotePath : public FormatWarningsWizardPage
{
Q_OBJECT
public:
explicit FolderWizardRemotePath(AccountPtr account);
explicit FolderWizardRemotePath(const AccountPtr& account);
~FolderWizardRemotePath();
virtual bool isComplete() const Q_DECL_OVERRIDE;
@@ -117,7 +118,7 @@ class FolderWizardSelectiveSync : public QWizardPage
{
Q_OBJECT
public:
explicit FolderWizardSelectiveSync(AccountPtr account);
explicit FolderWizardSelectiveSync(const AccountPtr& account);
~FolderWizardSelectiveSync();
virtual bool validatePage() Q_DECL_OVERRIDE;
+1 -1
Ver Arquivo
@@ -496,7 +496,7 @@ void OwncloudSetupWizard::slotAssistantFinished( int result )
qDebug() << "Adding folder definition for" << localFolder << _remoteFolder;
FolderDefinition folderDefinition;
folderDefinition.localPath = localFolder;
folderDefinition.targetPath = _remoteFolder;
folderDefinition.targetPath = FolderDefinition::prepareTargetPath(_remoteFolder);
folderDefinition.ignoreHiddenFiles = folderMan->ignoreHiddenFiles();
auto f = folderMan->addFolder(account, folderDefinition);
+6 -1
Ver Arquivo
@@ -125,8 +125,13 @@ void OwncloudAdvancedSetupPage::initializePage()
void OwncloudAdvancedSetupPage::updateStatus()
{
const QString locFolder = localFolder();
const QString url = static_cast<OwncloudWizard *>(wizard())->ocUrl();
const QString user = static_cast<OwncloudWizard *>(wizard())->getCredentials()->user();
QUrl serverUrl(url);
serverUrl.setUserName(user);
// check if the local folder exists. If so, and if its not empty, show a warning.
QString errorStr = FolderMan::instance()->checkPathValidityForNewFolder(locFolder);
QString errorStr = FolderMan::instance()->checkPathValidityForNewFolder(locFolder, serverUrl);
_localFolderValid = errorStr.isEmpty();
QString t;
+1 -1
Ver Arquivo
@@ -177,7 +177,7 @@ void PropagateRemoteMove::finalize()
record._contentChecksum = oldRecord._contentChecksum;
record._contentChecksumType = oldRecord._contentChecksumType;
if (record._fileSize != oldRecord._fileSize) {
qDebug() << "Warning: file sizes differ on server vs csync_journal: " << record._fileSize << oldRecord._fileSize;
qDebug() << "Warning: file sizes differ on server vs sync journal: " << record._fileSize << oldRecord._fileSize;
record._fileSize = oldRecord._fileSize; // server might have claimed different size, we take the old one from the DB
}
}
+4 -1
Ver Arquivo
@@ -83,7 +83,10 @@ SyncEngine::SyncEngine(AccountPtr account, const QString& localPath,
Q_ASSERT(localPath.endsWith(QLatin1Char('/')));
csync_create(&_csync_ctx, localPath.toUtf8().data());
csync_init(_csync_ctx);
const QString dbFile = _journal->databaseFilePath();
csync_init(_csync_ctx, dbFile.toUtf8().data());
_excludedFiles.reset(new ExcludedFiles(&_csync_ctx->excludes));
_syncFileStatusTracker.reset(new SyncFileStatusTracker(this));
+2
Ver Arquivo
@@ -164,6 +164,8 @@ private slots:
private:
void handleSyncError(CSYNC *ctx, const char *state);
QString journalDbFilePath() const;
static int treewalkLocal( TREE_WALK_FILE*, void *);
static int treewalkRemote( TREE_WALK_FILE*, void *);
int treewalkFile( TREE_WALK_FILE*, bool );
+58 -13
Ver Arquivo
@@ -16,6 +16,8 @@
#include <QStringList>
#include <QDebug>
#include <QElapsedTimer>
#include <QUrl>
#include "ownsql.h"
#include <inttypes.h>
@@ -30,17 +32,63 @@
namespace OCC {
SyncJournalDb::SyncJournalDb(const QString& path, QObject *parent) :
QObject(parent), _transaction(0)
SyncJournalDb::SyncJournalDb(const QString& dbFilePath, QObject *parent) :
QObject(parent),
_dbFile(dbFilePath),
_transaction(0)
{
_dbFile = path;
if( !_dbFile.endsWith('/') ) {
_dbFile.append('/');
}
QString SyncJournalDb::makeDbName(const QUrl& remoteUrl,
const QString& remotePath,
const QString& user)
{
QString journalPath = QLatin1String("._sync_");
QString key = QString::fromUtf8("%1@%2:%3").arg(
user,
remoteUrl.toString(),
remotePath);
QByteArray ba = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Md5);
journalPath.append( ba.left(6).toHex() );
journalPath.append(".db");
return journalPath;
}
bool SyncJournalDb::maybeMigrateDb(const QString& localPath, const QString& absoluteJournalPath)
{
const QString oldDbName = localPath + QLatin1String(".csync_journal.db");
if( !FileSystem::fileExists(oldDbName) ) {
return true;
}
_dbFile.append(".csync_journal.db");
const QString newDbName = absoluteJournalPath;
// Whenever there is an old db file, migrate it to the new db path.
// This is done to make switching from older versions to newer versions
// work correctly even if the user had previously used a new version
// and therefore already has an (outdated) new-style db file.
QString error;
if( FileSystem::fileExists( newDbName ) ) {
if( !FileSystem::remove(newDbName, &error) ) {
qDebug() << "Database migration: Could not remove db file" << newDbName
<< "due to" << error;
return false;
}
}
if( !FileSystem::rename(oldDbName, newDbName, &error) ) {
qDebug() << "Database migration: could not rename " << oldDbName
<< "to" << newDbName << ":" << error;
return false;
}
qDebug() << "Journal successfully migrated from" << oldDbName << "to" << newDbName;
return true;
}
bool SyncJournalDb::exists()
@@ -49,7 +97,7 @@ bool SyncJournalDb::exists()
return (!_dbFile.isEmpty() && QFile::exists(_dbFile));
}
QString SyncJournalDb::databaseFilePath()
QString SyncJournalDb::databaseFilePath() const
{
return _dbFile;
}
@@ -135,8 +183,6 @@ bool SyncJournalDb::checkConnect()
return false;
}
bool isNewDb = !QFile::exists(_dbFile);
// The database file is created by this call (SQLITE_OPEN_CREATE)
if( !_db.openOrCreateReadWrite(_dbFile) ) {
QString error = _db.error();
@@ -310,10 +356,9 @@ bool SyncJournalDb::checkConnect()
SqlQuery versionQuery("SELECT major, minor, patch FROM version;", _db);
if (!versionQuery.next()) {
// If there was no entry in the table, it means we are likely upgrading from 1.5
if (!isNewDb) {
qDebug() << Q_FUNC_INFO << "possibleUpgradeFromMirall_1_5 detected!";
forceRemoteDiscovery = true;
}
qDebug() << Q_FUNC_INFO << "possibleUpgradeFromMirall_1_5 detected!";
forceRemoteDiscovery = true;
createQuery.prepare("INSERT INTO version VALUES (?1, ?2, ?3, ?4);");
createQuery.bindValue(1, MIRALL_VERSION_MAJOR);
createQuery.bindValue(2, MIRALL_VERSION_MINOR);
+11 -2
Ver Arquivo
@@ -37,9 +37,17 @@ class OWNCLOUDSYNC_EXPORT SyncJournalDb : public QObject
{
Q_OBJECT
public:
explicit SyncJournalDb(const QString& path, QObject *parent = 0);
explicit SyncJournalDb(const QString& dbFilePath, QObject *parent = 0);
virtual ~SyncJournalDb();
/// Create a journal path for a specific configuration
static QString makeDbName(const QUrl& remoteUrl,
const QString& remotePath,
const QString& user);
/// Migrate a csync_journal to the new path, if necessary. Returns false on error
static bool maybeMigrateDb(const QString& localPath, const QString& absoluteJournalPath);
// to verify that the record could be queried successfully check
// with SyncJournalFileRecord::isValid()
SyncJournalFileRecord getFileRecord(const QString& filename);
@@ -58,7 +66,8 @@ public:
bool exists();
void walCheckpoint();
QString databaseFilePath();
QString databaseFilePath() const;
static qint64 getPHash(const QString& );
void updateErrorBlacklistEntry( const SyncJournalErrorBlacklistRecord& item );
+15 -1
Ver Arquivo
@@ -277,12 +277,26 @@ bool Utility::fsCasePreserving()
if( isWindows() || isMac() ) {
re = true;
} else {
static bool isTest = qgetenv("OWNCLOUD_TEST_CASE_PRESERVING").toInt();
bool isTest = qgetenv("OWNCLOUD_TEST_CASE_PRESERVING").toInt();
re = isTest;
}
return re;
}
bool Utility::fileNamesEqual( const QString& fn1, const QString& fn2)
{
const QDir fd1(fn1);
const QDir fd2(fn2);
// Attention: If the path does not exist, canonicalPath returns ""
// ONLY use this function with existing pathes.
const QString a = fd1.canonicalPath();
const QString b = fd2.canonicalPath();
bool re = !a.isEmpty() && QString::compare( a, b,
fsCasePreserving() ? Qt::CaseInsensitive : Qt::CaseSensitive) == 0;
return re;
}
QDateTime Utility::qDateTimeFromTime_t(qint64 t)
{
return QDateTime::fromMSecsSinceEpoch(t * 1000);
+5
Ver Arquivo
@@ -105,6 +105,11 @@ namespace Utility
// if false, the two cases are two different files.
OWNCLOUDSYNC_EXPORT bool fsCasePreserving();
// Check if two pathes that MUST exist are equal. This function
// uses QDir::canonicalPath() to judge and cares for the systems
// case sensitivity.
OWNCLOUDSYNC_EXPORT bool fileNamesEqual( const QString& fn1, const QString& fn2);
// Call the given command with the switch --version and rerun the first line
// of the output.
// If command is empty, the function calls the running application which, on
+1 -1
Ver Arquivo
@@ -737,7 +737,7 @@ public:
_account->setUrl(QUrl(QStringLiteral("http://admin:admin@localhost/owncloud")));
_account->setCredentials(new FakeCredentials{_fakeQnam});
_journalDb.reset(new OCC::SyncJournalDb(localPath()));
_journalDb.reset(new OCC::SyncJournalDb(localPath() + "._sync_test.db"));
_syncEngine.reset(new OCC::SyncEngine(_account, localPath(), "", _journalDb.get()));
// A new folder will update the local file state database on first sync.
+41 -7
Ver Arquivo
@@ -16,9 +16,20 @@
#include "account.h"
#include "accountstate.h"
#include "configfile.h"
#include "creds/httpcredentials.h"
using namespace OCC;
class HttpCredentialsTest : public HttpCredentials {
public:
HttpCredentialsTest(const QString& user, const QString& password)
: HttpCredentials(user, password, "", "")
{}
void askFromUser() Q_DECL_OVERRIDE {
}
};
static FolderDefinition folderDefinition(const QString &path) {
FolderDefinition d;
@@ -53,7 +64,13 @@ private slots:
f.write("hello");
}
AccountStatePtr newAccountState(new AccountState(Account::create()));
AccountPtr account = Account::create();
QUrl url("http://example.de");
HttpCredentialsTest *cred = new HttpCredentialsTest("testuser", "secret");
account->setCredentials(cred);
account->setUrl( url );
AccountStatePtr newAccountState(new AccountState(account));
FolderMan *folderman = FolderMan::instance();
QCOMPARE(folderman, &_fm);
QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dir.path() + "/sub/ownCloud1")));
@@ -61,6 +78,8 @@ private slots:
// those should be allowed
// QString FolderMan::checkPathValidityForNewFolder(const QString& path, const QUrl &serverUrl, bool forNewDirectory)
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/free"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/free2/"), QString());
// Not an existing directory -> Ok
@@ -71,11 +90,21 @@ private slots:
// A file -> Error
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/file.txt").isNull());
// There are folders configured in those folders: -> ERROR
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/ownCloud2/").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/").isNull());
// There are folders configured in those folders, url needs to be taken into account: -> ERROR
QUrl url2(url);
const QString user = account->credentials()->user();
url2.setUserName(user);
// The following both fail because they refer to the same account (user and url)
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1", url2).isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/ownCloud2/", url2).isNull());
// Now it will work because the account is different
QUrl url3("http://anotherexample.org");
url3.setUserName("dummy");
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1", url3), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/ownCloud2/", url3), QString());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path()).isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1/folder").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1/folder/f").isNull());
@@ -93,7 +122,12 @@ private slots:
// Not Ok
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link2").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link3").isNull());
// link 3 points to an existing sync folder. To make it fail, the account must be the same
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link3", url2).isNull());
// while with a different account, this is fine
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/link3", url3), QString());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link4").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link3/folder").isNull());
+3 -6
Ver Arquivo
@@ -13,18 +13,13 @@
using namespace OCC;
namespace {
const char testdbC[] = "/tmp";
}
class TestSyncJournalDB : public QObject
{
Q_OBJECT
public:
TestSyncJournalDB()
: _db(testdbC)
: _db("/tmp/csync-test.db")
{
}
@@ -41,6 +36,8 @@ private slots:
void cleanupTestCase()
{
const QString file = _db.databaseFilePath();
QFile::remove(file);
}
void testFileRecord()
+49
Ver Arquivo
@@ -5,6 +5,9 @@
*/
#include <QtTest>
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
#include <QTemporaryDir>
#endif
#include "utility.h"
@@ -150,6 +153,52 @@ private slots:
s = timeAgoInWords(earlyTS, laterTS );
QCOMPARE(s, QLatin1String("Less than a minute ago"));
}
void testFsCasePreserving()
{
qputenv("OWNCLOUD_TEST_CASE_PRESERVING", "1");
QVERIFY(fsCasePreserving());
qputenv("OWNCLOUD_TEST_CASE_PRESERVING", "0");
QVERIFY(! fsCasePreserving());
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
qunsetenv("OWNCLOUD_TEST_CASE_PRESERVING");
QVERIFY(isMac() || isWindows() ? fsCasePreserving() : ! fsCasePreserving());
#endif
}
void testFileNamesEqual()
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
qDebug() << "*** checking fileNamesEqual function";
QTemporaryDir dir;
QVERIFY(dir.isValid());
QDir dir2(dir.path());
QVERIFY(dir2.mkpath("test"));
if( !fsCasePreserving() ) {
QVERIFY(dir2.mkpath("TEST"));
}
QVERIFY(dir2.mkpath("test/TESTI"));
QVERIFY(dir2.mkpath("TESTI"));
QString a = dir.path();
QString b = dir.path();
QVERIFY(fileNamesEqual(a, b));
QVERIFY(fileNamesEqual(a+"/test", b+"/test")); // both exist
QVERIFY(fileNamesEqual(a+"/test/TESTI", b+"/test/../test/TESTI")); // both exist
qputenv("OWNCLOUD_TEST_CASE_PRESERVING", "1");
QVERIFY(fileNamesEqual(a+"/test", b+"/TEST")); // both exist
QVERIFY(!fileNamesEqual(a+"/test", b+"/test/TESTI")); // both are different
dir.remove();
qunsetenv("OWNCLOUD_TEST_CASE_PRESERVING");
#endif
}
};
QTEST_APPLESS_MAIN(TestUtility)