diff --git a/csync/src/csync_reconcile.c b/csync/src/csync_reconcile.c index a3f094f9a..038b5c0c0 100644 --- a/csync/src/csync_reconcile.c +++ b/csync/src/csync_reconcile.c @@ -35,6 +35,39 @@ #define ACCEPTED_TIME_DIFF 5 #define ONE_HOUR 3600 + +/* 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); + } +} + /* * 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 @@ -86,6 +119,11 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) { } 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) { diff --git a/csync/tests/ownCloud/t4.pl b/csync/tests/ownCloud/t4.pl index 9f7efa889..5c8a115db 100755 --- a/csync/tests/ownCloud/t4.pl +++ b/csync/tests/ownCloud/t4.pl @@ -108,6 +108,12 @@ remoteCleanup('test_stat'); system( "echo echo hello >> " . localDir() . 'echo.sh' ); chmod 0751, localDir() . 'echo.sh'; +#and add a file in anotherdir for the next test +mkdir( localDir() . 'anotherdir' ); +mkdir( localDir() . 'anotherdir/sub' ); +system( "echo foobar > " . localDir() . 'anotherdir/file1.txt' ); +system( "echo foobar > " . localDir() . 'anotherdir/sub/file2.txt' ); + csync(); assertLocalAndRemoteDir( '', 0 ); @@ -126,6 +132,24 @@ open(my $fh, "<", localDir() . 'echo.sh'); my $perm = (stat $fh)[2] & 07777; assert( $perm eq 0751, "permissions not kept" ); +printInfo("Remove a directory and make it a symlink instead\n"); +system( "rm -rf " . localDir() . 'anotherdir' ); +system( "ln -s /bin " . localDir() . 'anotherdir' ); +# remember the fileid of the file on the server +my $oldfileid1 = remoteFileId( 'anotherdir/', 'file1.txt' ); +my $oldfileid2 = remoteFileId( 'anotherdir/sub', 'file2.txt' ); +csync(); + +#check that the files in ignored directory has NOT been removed +my $newfileid1 = remoteFileId( 'anotherdir/', 'file1.txt' ); +my $newfileid2 = remoteFileId( 'anotherdir/sub', 'file2.txt' ); +assert( $oldfileid1 eq $newfileid1, "File removed (file1.txt)" ); +assert( $oldfileid2 eq $newfileid2, "File removed (file2.txt)" ); + +printInfo("Now remove the symlink\n"); +system( "rm -f " . localDir() . 'anotherdir' ); +csync(); +assertLocalAndRemoteDir( '', 0 ); cleanup();