Thu, 03 Oct 2019 12:00:25 +0200
implement hash based conflict resolution in dav-sync pull
dav/scfg.c | file | annotate | diff | comparison | revisions | |
dav/scfg.h | file | annotate | diff | comparison | revisions | |
dav/sync.c | file | annotate | diff | comparison | revisions | |
test/bin-test/test-dav-sync-hashing.sh | file | annotate | diff | comparison | revisions |
--- a/dav/scfg.c Sun Sep 29 12:57:13 2019 +0200 +++ b/dav/scfg.c Thu Oct 03 12:00:25 2019 +0200 @@ -438,6 +438,7 @@ bool lockpush = false; bool hashing = false; bool store_hash = false; + bool pull_skip_hashing = false; //bool detect_copy = false; time_t lock_timeout = 0; uint32_t metadata = 0; @@ -627,6 +628,7 @@ dir->lockpush = lockpush; dir->hashing = hashing; dir->store_hash = store_hash; + dir->pull_skip_hashing = pull_skip_hashing; dir->lock_timeout = lock_timeout; dir->metadata = metadata; dir->splitconfig = splitconfig;
--- a/dav/scfg.h Sun Sep 29 12:57:13 2019 +0200 +++ b/dav/scfg.h Thu Oct 03 12:00:25 2019 +0200 @@ -100,6 +100,7 @@ bool lockpush; bool hashing; bool store_hash; + bool pull_skip_hashing; uint32_t db_settings; } SyncDirectory;
--- a/dav/sync.c Sun Sep 29 12:57:13 2019 +0200 +++ b/dav/sync.c Thu Oct 03 12:00:25 2019 +0200 @@ -965,6 +965,7 @@ fprintf(stderr, "Error: resource %s has no etag\n", res->path); return REMOTE_NO_CHANGE; } + char *hash = sync_get_content_hash(res); DavBool issplit = dav_get_property(res, "idav:split") ? TRUE : FALSE; if(issplit) { @@ -1004,15 +1005,9 @@ } } else if(local) { DavBool nochange = FALSE; - char *content_hash = sync_get_content_hash(res); - if(SYNC_SYMLINK(dir) && nullstrcmp(link, local->link_target)) { ret = REMOTE_CHANGE_LINK; nochange = TRUE; - } else if(content_hash && local->hash) { - if(!strcmp(content_hash, local->hash)) { - nochange = TRUE; - } } else if(local->etag) { sstr_t e = sstr(etag); if(sstrprefix(e, S("W/"))) { @@ -1041,7 +1036,28 @@ } else { ret = REMOTE_CHANGE_NEW; } - + + // if hashing is enabled we can compare the hash of the remote file + // with the local file to test if a file is really modified + if (!iscollection && + !link && + (ret == REMOTE_CHANGE_MODIFIED || ret == REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED) && + exists && + hash && + !dir->pull_skip_hashing) + { + // because rehashing a file is slow, there is a config element for + // disabling this (pull-skip-hashing) + char *local_hash = util_file_hash(local_path); + if(local_hash) { + if(!strcmp(hash, local_hash)) { + ret = REMOTE_NO_CHANGE; + } + } + free(local_hash); + } + + // if a file is not modified, check if the metadata has changed while(ret == REMOTE_NO_CHANGE && local) { // check if tags have changed if(dir->tagconfig) { @@ -2909,7 +2925,7 @@ char *hash = sync_get_content_hash(remote); if(hash && res->hash && equal) { - // if requested, check if the local and remote are equal + // if requested, check if the local and remote res are equal if(!strcmp(hash, res->hash)) { *equal = TRUE; return 0;
--- a/test/bin-test/test-dav-sync-hashing.sh Sun Sep 29 12:57:13 2019 +0200 +++ b/test/bin-test/test-dav-sync-hashing.sh Thu Oct 03 12:00:25 2019 +0200 @@ -412,6 +412,7 @@ rm -f tmp-sync/test2a/t13file1 mv tmp-sync/test2a/t13file2 tmp-sync/test2a/t13file1 +sleep 2 dav_sync_push test2a "test 13: push failed" # we can't check the exact output, because there are multiple valid ways