Evgeny Kotkov wrote: > Apparently, this behavior is caused by a problem in the specific optimization > within the FSFS open_path() routine. > > I constructed an FS regression test and committed it and the fix in: > https://svn.apache.org/r1847572 > > (I'll try to nominate it for backport once I get some time.)
Excellent! Thanks! I had opened issue SVN-4791 for this. I have updated the log message and the issue to cross-reference each other. https://issues.apache.org/jira/browse/SVN-4791 I expect we could improve the issue summary line which is currently "FSFS Can't get entries of non-directory". I'm glad you included a FS-level test for it. In case anyone is interested, I attach a patch that backports the initial RA-level test to 1.8. 1.9 and 1.10. I haven't reviewed or tested your r1847572 fix or test. -- - Julian
Backport the initial RA-level test for SVN-4791 to 1.8, 1.9, 1.10. * 1.8.x/subversion/tests/libsvn_ra/ra-test.c * 1.9.x/subversion/tests/libsvn_ra/ra-test.c * 1.10.x/subversion/tests/libsvn_ra/ra-test.c (cant_get_entries_of_non_directory): New test. (test_funcs): Run it. --This line, and those below, will be ignored-- Index: 1.10.x/subversion/tests/libsvn_ra/ra-test.c =================================================================== --- 1.10.x/subversion/tests/libsvn_ra/ra-test.c (revision 1847591) +++ 1.10.x/subversion/tests/libsvn_ra/ra-test.c (working copy) @@ -19,13 +19,13 @@ * specific language governing permissions and limitations * under the License. * ==================================================================== */ - + #include <apr_general.h> #include <apr_pools.h> #include <apr_file_io.h> #include <assert.h> #include "svn_error.h" @@ -1781,12 +1781,134 @@ commit_locked_file(const svn_test_opts_t SVN_TEST_ASSERT(propval); SVN_TEST_STRING_ASSERT(propval->data, "propval"); return SVN_NO_ERROR; } +static svn_error_t * +cant_get_entries_of_non_directory(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_ra_session_t *session; + + SVN_ERR(make_and_open_repos(&session, + "cant_get_entries_of_non_directory", opts, + pool)); + + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + void *dir_baton; + void *file_baton; + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton)); + SVN_ERR(editor->add_directory("A", root_baton, NULL, SVN_INVALID_REVNUM, + pool, &dir_baton)); + SVN_ERR(editor->add_file("A/mu", dir_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + void *dir_baton; + const char* repos_root_url; + const char* A_url; + + SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, pool)); + A_url = svn_path_url_add_component2(repos_root_url, "A", pool); + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 1, pool, &root_baton)); + SVN_ERR(editor->add_directory("B", root_baton, A_url, 1, + pool, &dir_baton)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 2, pool, &root_baton)); + SVN_ERR(editor->delete_entry("B/mu", 2, root_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + void *dir_baton; + void *subdir_baton; + void *file_baton; + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 3, pool, &root_baton)); + SVN_ERR(editor->open_directory("B", root_baton, 3, pool, &dir_baton)); + SVN_ERR(editor->add_directory("B/mu", root_baton, NULL, SVN_INVALID_REVNUM, + pool, &subdir_baton)); + SVN_ERR(editor->add_file("B/mu/iota", subdir_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(subdir_baton, pool)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + /* The following updates fail when executed in this order + one after another within the same session. + + When commenting out one of the blocks the test passes + */ + { + const svn_ra_reporter3_t *reporter; + void *report_baton; + + SVN_ERR(svn_ra_do_update3(session, &reporter, &report_baton, + 3, "", svn_depth_infinity, TRUE, FALSE, + svn_delta_default_editor(pool), NULL, + pool, pool)); + SVN_ERR(reporter->set_path(report_baton, "", 3, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->set_path(report_baton, "B", 2, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->finish_report(report_baton, pool)); + } + { + const svn_ra_reporter3_t *reporter; + void *report_baton; + + SVN_ERR(svn_ra_do_update3(session, &reporter, &report_baton, + 4, "", svn_depth_infinity, TRUE, FALSE, + svn_delta_default_editor(pool), NULL, + pool, pool)); + SVN_ERR(reporter->set_path(report_baton, "", 4, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->set_path(report_baton, "B", 3, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->finish_report(report_baton, pool)); + } + return SVN_NO_ERROR; +} + /* The test table. */ static int max_threads = 4; static struct svn_test_descriptor_t test_funcs[] = @@ -1817,10 +1939,12 @@ static struct svn_test_descriptor_t test SVN_TEST_OPTS_PASS(tunnel_run_checkout, "verify checkout over a tunnel"), SVN_TEST_OPTS_PASS(commit_empty_last_change, "check how last change applies to empty commit"), SVN_TEST_OPTS_PASS(commit_locked_file, "check commit editor for a locked file"), + SVN_TEST_OPTS_XFAIL(cant_get_entries_of_non_directory, + "check that there's no \"Can't get entries\" error"), SVN_TEST_NULL }; SVN_TEST_MAIN Index: 1.8.x/subversion/tests/libsvn_ra/ra-test.c =================================================================== --- 1.8.x/subversion/tests/libsvn_ra/ra-test.c (revision 1847591) +++ 1.8.x/subversion/tests/libsvn_ra/ra-test.c (working copy) @@ -197,17 +197,142 @@ commit_callback_failure(const svn_test_o /* This is what users should do if close_edit fails... Except that in this case the commit actually succeeded*/ SVN_ERR(editor->abort_edit(edit_baton, pool)); return SVN_NO_ERROR; } + +static svn_error_t * +cant_get_entries_of_non_directory(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_ra_session_t *session; + + SVN_ERR(make_and_open_local_repos(&session, + "cant_get_entries_of_non_directory", opts, + pool)); + + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + void *dir_baton; + void *file_baton; + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton)); + SVN_ERR(editor->add_directory("A", root_baton, NULL, SVN_INVALID_REVNUM, + pool, &dir_baton)); + SVN_ERR(editor->add_file("A/mu", dir_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + void *dir_baton; + const char* repos_root_url; + const char* A_url; + + SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, pool)); + A_url = svn_path_url_add_component2(repos_root_url, "A", pool); + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 1, pool, &root_baton)); + SVN_ERR(editor->add_directory("B", root_baton, A_url, 1, + pool, &dir_baton)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 2, pool, &root_baton)); + SVN_ERR(editor->delete_entry("B/mu", 2, root_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + void *dir_baton; + void *subdir_baton; + void *file_baton; + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 3, pool, &root_baton)); + SVN_ERR(editor->open_directory("B", root_baton, 3, pool, &dir_baton)); + SVN_ERR(editor->add_directory("B/mu", root_baton, NULL, SVN_INVALID_REVNUM, + pool, &subdir_baton)); + SVN_ERR(editor->add_file("B/mu/iota", subdir_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(subdir_baton, pool)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + /* The following updates fail when executed in this order + one after another within the same session. + + When commenting out one of the blocks the test passes + */ + { + const svn_ra_reporter3_t *reporter; + void *report_baton; + + SVN_ERR(svn_ra_do_update3(session, &reporter, &report_baton, + 3, "", svn_depth_infinity, TRUE, FALSE, + svn_delta_default_editor(pool), NULL, + pool, pool)); + SVN_ERR(reporter->set_path(report_baton, "", 3, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->set_path(report_baton, "B", 2, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->finish_report(report_baton, pool)); + } + { + const svn_ra_reporter3_t *reporter; + void *report_baton; + + SVN_ERR(svn_ra_do_update3(session, &reporter, &report_baton, + 4, "", svn_depth_infinity, TRUE, FALSE, + svn_delta_default_editor(pool), NULL, + pool, pool)); + SVN_ERR(reporter->set_path(report_baton, "", 4, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->set_path(report_baton, "B", 3, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->finish_report(report_baton, pool)); + } + return SVN_NO_ERROR; +} + /* The test table. */ struct svn_test_descriptor_t test_funcs[] = { SVN_TEST_NULL, SVN_TEST_OPTS_PASS(location_segments_test, "test svn_ra_get_location_segments"), SVN_TEST_OPTS_PASS(commit_callback_failure, "commit callback failure"), + SVN_TEST_OPTS_XFAIL(cant_get_entries_of_non_directory, + "check that there's no \"Can't get entries\" error"), SVN_TEST_NULL }; Index: 1.9.x/subversion/tests/libsvn_ra/ra-test.c =================================================================== --- 1.9.x/subversion/tests/libsvn_ra/ra-test.c (revision 1847591) +++ 1.9.x/subversion/tests/libsvn_ra/ra-test.c (working copy) @@ -838,12 +838,135 @@ tunnel_run_checkout(const svn_test_opts_ SVN_ERR(reporter->finish_report(report_baton, pool)); return SVN_NO_ERROR; } + +static svn_error_t * +cant_get_entries_of_non_directory(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_ra_session_t *session; + + SVN_ERR(make_and_open_repos(&session, + "cant_get_entries_of_non_directory", opts, + pool)); + + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + void *dir_baton; + void *file_baton; + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton)); + SVN_ERR(editor->add_directory("A", root_baton, NULL, SVN_INVALID_REVNUM, + pool, &dir_baton)); + SVN_ERR(editor->add_file("A/mu", dir_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + void *dir_baton; + const char* repos_root_url; + const char* A_url; + + SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, pool)); + A_url = svn_path_url_add_component2(repos_root_url, "A", pool); + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 1, pool, &root_baton)); + SVN_ERR(editor->add_directory("B", root_baton, A_url, 1, + pool, &dir_baton)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 2, pool, &root_baton)); + SVN_ERR(editor->delete_entry("B/mu", 2, root_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + { + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton; + void *dir_baton; + void *subdir_baton; + void *file_baton; + + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + apr_hash_make(pool), NULL, + NULL, NULL, FALSE, pool)); + SVN_ERR(editor->open_root(edit_baton, 3, pool, &root_baton)); + SVN_ERR(editor->open_directory("B", root_baton, 3, pool, &dir_baton)); + SVN_ERR(editor->add_directory("B/mu", root_baton, NULL, SVN_INVALID_REVNUM, + pool, &subdir_baton)); + SVN_ERR(editor->add_file("B/mu/iota", subdir_baton, NULL, SVN_INVALID_REVNUM, + pool, &file_baton)); + SVN_ERR(editor->close_file(file_baton, NULL, pool)); + SVN_ERR(editor->close_directory(subdir_baton, pool)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + } + /* The following updates fail when executed in this order + one after another within the same session. + + When commenting out one of the blocks the test passes + */ + { + const svn_ra_reporter3_t *reporter; + void *report_baton; + + SVN_ERR(svn_ra_do_update3(session, &reporter, &report_baton, + 3, "", svn_depth_infinity, TRUE, FALSE, + svn_delta_default_editor(pool), NULL, + pool, pool)); + SVN_ERR(reporter->set_path(report_baton, "", 3, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->set_path(report_baton, "B", 2, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->finish_report(report_baton, pool)); + } + { + const svn_ra_reporter3_t *reporter; + void *report_baton; + + SVN_ERR(svn_ra_do_update3(session, &reporter, &report_baton, + 4, "", svn_depth_infinity, TRUE, FALSE, + svn_delta_default_editor(pool), NULL, + pool, pool)); + SVN_ERR(reporter->set_path(report_baton, "", 4, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->set_path(report_baton, "B", 3, svn_depth_infinity, FALSE, + NULL, pool)); + SVN_ERR(reporter->finish_report(report_baton, pool)); + } + return SVN_NO_ERROR; +} + /* The test table. */ static int max_threads = 2; static struct svn_test_descriptor_t test_funcs[] = @@ -864,10 +987,12 @@ static struct svn_test_descriptor_t test SVN_TEST_OPTS_PASS(base_revision_above_youngest, "base revision newer than youngest"), SVN_TEST_OPTS_PASS(ra_list_has_props, "check list has_props performance"), SVN_TEST_OPTS_PASS(tunnel_run_checkout, "verify checkout over a tunnel"), + SVN_TEST_OPTS_XFAIL(cant_get_entries_of_non_directory, + "check that there's no \"Can't get entries\" error"), SVN_TEST_NULL }; SVN_TEST_MAIN