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

Reply via email to