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