You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/11/27 08:53:35 UTC

svn commit: r1545955 [12/15] - in /subversion/branches/fsfs-improvements: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/server-side/ contrib/server-side/svncutter/ notes/ subversion/bindings/javahl/native/ ...

Modified: subversion/branches/fsfs-improvements/subversion/svnserve/svnserve.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/svnserve/svnserve.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/svnserve/svnserve.c (original)
+++ subversion/branches/fsfs-improvements/subversion/svnserve/svnserve.c Wed Nov 27 07:53:29 2013
@@ -48,6 +48,7 @@
 #include "svn_cache_config.h"
 #include "svn_version.h"
 #include "svn_io.h"
+#include "svn_hash.h"
 
 #include "svn_private_config.h"
 
@@ -360,7 +361,6 @@ static void usage(const char *progname, 
   svn_error_clear(svn_cmdline_fprintf(stderr, pool,
                                       _("Type '%s --help' for usage.\n"),
                                       progname));
-  exit(1);
 }
 
 static void help(apr_pool_t *pool)
@@ -393,7 +393,6 @@ static void help(apr_pool_t *pool)
       svn_error_clear(svn_cmdline_fprintf(stdout, pool, "  %s\n", optstr));
     }
   svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
-  exit(0);
 }
 
 static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool)
@@ -445,113 +444,172 @@ static apr_status_t redirect_stdout(void
   return apr_file_dup2(out_file, err_file, pool);
 }
 
-#if APR_HAS_THREADS
-/* The pool passed to apr_thread_create can only be released when both
-
-      A: the call to apr_thread_create has returned to the calling thread
-      B: the new thread has started running and reached apr_thread_start_t
-
-   So we set the atomic counter to 2 then both the calling thread and
-   the new thread decrease it and when it reaches 0 the pool can be
-   released.  */
-typedef struct shared_pool_t {
-  svn_atomic_t count;
-  apr_pool_t *pool;              /* root pool used to allocate the socket */
-  svn_root_pools__t *root_pools; /* put it back into this after use */
-} shared_pool_t;
-
-static shared_pool_t *
-attach_shared_pool(apr_pool_t *pool,
-                   svn_root_pools__t *root_pools)
+/* Wait for the next client connection to come in from SOCK.  Allocate
+ * the connection in a root pool from CONNECTION_POOLS and assign PARAMS.
+ * Return the connection object in *CONNECTION.
+ * 
+ * Use HANDLING_MODE for proper internal cleanup.
+ */
+static svn_error_t *
+accept_connection(connection_t **connection,
+                  apr_socket_t *sock,
+                  svn_root_pools__t *connection_pools,
+                  serve_params_t *params,
+                  enum connection_handling_mode handling_mode)
 {
-  shared_pool_t *shared = apr_palloc(pool, sizeof(*shared));
-
-  shared->pool = pool;
-  shared->root_pools = root_pools;
-  svn_atomic_set(&shared->count, 2);
+  apr_status_t status;
+  
+  /* Non-standard pool handling.  The main thread never blocks to join
+   *         the connection threads so it cannot clean up after each one.  So
+   *         separate pools that can be cleared at thread exit are used. */
+  
+  apr_pool_t *pool = svn_root_pools__acquire_pool(connection_pools);
+  *connection = apr_pcalloc(pool, sizeof(**connection));
+  (*connection)->pool = pool;
+  (*connection)->params = params;
+  (*connection)->root_pools = connection_pools;
+  (*connection)->ref_count = 1;
+  
+  do
+    {
+      #ifdef WIN32
+      if (winservice_is_stopping())
+        exit(0);
+      #endif
+      
+      status = apr_socket_accept(&(*connection)->usock, sock, pool);
+      if (handling_mode == connection_mode_fork)
+        {
+          apr_proc_t proc;
+          
+          /* Collect any zombie child processes. */
+          while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
+            pool) == APR_CHILD_DONE)
+            ;
+        }
+    }
+  while (APR_STATUS_IS_EINTR(status)
+    || APR_STATUS_IS_ECONNABORTED(status)
+    || APR_STATUS_IS_ECONNRESET(status));
+  
+  return status
+       ? svn_error_wrap_apr(status, _("Can't accept client connection"))
+       : SVN_NO_ERROR;
+}
 
-  return shared;
+/* Add a reference to CONNECTION, i.e. keep it and it's pool valid unless
+ * that reference gets released using release_shared_pool().
+ */
+static void
+attach_connection(connection_t *connection)
+{
+  svn_atomic_inc(&connection->ref_count);
 }
 
+/* Release a reference to CONNECTION.  If there are no more references,
+ * the connection will be
+ */
 static void
-release_shared_pool(struct shared_pool_t *shared)
+close_connection(connection_t *connection)
 {
-  if (svn_atomic_dec(&shared->count) == 0)
-    svn_root_pools__release_pool(shared->pool, shared->root_pools);
+  /* this will automatically close USOCK */
+  if (svn_atomic_dec(&connection->ref_count) == 0)
+    svn_root_pools__release_pool(connection->pool, connection->root_pools);
 }
-#endif
 
 /* Wrapper around serve() that takes a socket instead of a connection.
  * This is to off-load work from the main thread in threaded and fork modes.
+ *
+ * If an error occurs, log it and also return it.
  */
 static svn_error_t *
-serve_socket(apr_socket_t *usock,
-             serve_params_t *params,
+serve_socket(connection_t *connection,
              apr_pool_t *pool)
 {
-  apr_status_t status;
-  svn_ra_svn_conn_t *conn;
-  svn_error_t *err;
-  
-  /* Enable TCP keep-alives on the socket so we time out when
-   * the connection breaks due to network-layer problems.
-   * If the peer has dropped the connection due to a network partition
-   * or a crash, or if the peer no longer considers the connection
-   * valid because we are behind a NAT and our public IP has changed,
-   * it will respond to the keep-alive probe with a RST instead of an
-   * acknowledgment segment, which will cause svn to abort the session
-   * even while it is currently blocked waiting for data from the peer. */
-  status = apr_socket_opt_set(usock, APR_SO_KEEPALIVE, 1);
-  if (status)
-    {
-      /* It's not a fatal error if we cannot enable keep-alives. */
-    }
-
-  /* create the connection, configure ports etc. */
-  conn = svn_ra_svn_create_conn3(usock, NULL, NULL,
-                                 params->compression_level,
-                                 params->zero_copy_limit,
-                                 params->error_check_interval,
-                                 pool);
-
   /* process the actual request and log errors */
-  err = serve(conn, params, pool);
+  svn_error_t *err = serve_interruptable(NULL, connection, NULL, pool);
   if (err)
-    logger__log_error(params->logger, err, NULL,
-                      get_client_info(conn, params, pool));
+    logger__log_error(connection->params->logger, err, NULL,
+                      get_client_info(connection->conn, connection->params,
+                                      pool));
 
   return svn_error_trace(err);
 }
 
-/* "Arguments" passed from the main thread to the connection thread */
-struct serve_thread_t {
-  apr_socket_t *usock;
-  serve_params_t *params;
-#if APR_HAS_THREADS
-  shared_pool_t *shared_pool;
-#endif
-};
-
 #if APR_HAS_THREADS
 
 /* allocate and recycle root pools for connection objects.
    There should be at most THREADPOOL_MAX_SIZE such pools. */
 svn_root_pools__t *connection_pools;
 
+#if HAVE_THREADPOOLS
+
+/* The global thread pool serving all connections. */
+apr_thread_pool_t *threads;
+
+/* Very simple load determination callback for serve_interruptable:
+   With less than have the threads in THREADS in use, we can afford to
+   wait in the socket read() function.  Otherwise, poll them round-robin. */
+static svn_boolean_t
+is_busy(connection_t *connection)
+{
+  return apr_thread_pool_threads_count(threads) * 2
+       > apr_thread_pool_thread_max_get(threads);
+}
+
+/* Serve the connection given by DATA.  Under high load, serve only
+   the current command (if any) and then put the connection back into
+   THREAD's task pool. */
 static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
 {
-  struct serve_thread_t *d = data;
+  svn_boolean_t done;
+  connection_t *connection = data;
+  svn_error_t *err;
+
+  apr_pool_t *pool = svn_root_pools__acquire_pool(connection_pools);
+
+  /* process the actual request and log errors */
+  err = serve_interruptable(&done, connection, is_busy, pool);
+  if (err)
+    {
+      logger__log_error(connection->params->logger, err, NULL,
+                        get_client_info(connection->conn, connection->params,
+                                        pool));
+      svn_error_clear(err);
+    }
+  svn_root_pools__release_pool(pool, connection_pools);
+
+  /* Close or re-schedule connection. */
+  if (done)
+    close_connection(connection);
+  else
+    apr_thread_pool_push(threads, serve_thread, connection, 0, NULL);
+    
+  return NULL;
+}
+
+#else
 
+/* Fully serve the connection given by DATA. */
+static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
+{
+  struct connection_t *connection = data;
   apr_pool_t *pool = svn_root_pools__acquire_pool(connection_pools);
-  svn_error_clear(serve_socket(d->usock, d->params, pool));
+
+  /* serve_socket() logs any error it returns, so ignore it. */
+  svn_error_clear(serve_socket(connection, pool));
+
   svn_root_pools__release_pool(pool, connection_pools);
 
-  release_shared_pool(d->shared_pool);
+  /* destroy the connection object */
+  close_connection(connection);
 
   return NULL;
 }
 #endif
 
+#endif
+
 /* Write the PID of the current process as a decimal number, followed by a
    newline to the file FILENAME, using POOL for temporary allocations. */
 static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool)
@@ -591,14 +649,19 @@ check_lib_versions(void)
 }
 
 
-int main(int argc, const char *argv[])
+/*
+ * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
+ * either return an error to be displayed, or set *EXIT_CODE to non-zero and
+ * return SVN_NO_ERROR.
+ */
+static svn_error_t *
+sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
 {
   enum run_mode run_mode = run_mode_unspecified;
   svn_boolean_t foreground = FALSE;
-  apr_socket_t *sock, *usock;
+  apr_socket_t *sock;
   apr_file_t *in_file, *out_file;
   apr_sockaddr_t *sa;
-  apr_pool_t *pool;
   svn_error_t *err;
   apr_getopt_t *os;
   int opt;
@@ -606,17 +669,16 @@ int main(int argc, const char *argv[])
   const char *arg;
   apr_status_t status;
   apr_proc_t proc;
-#if APR_HAS_THREADS
-  shared_pool_t *shared_pool;
-  struct serve_thread_t *thread_data;
-#if HAVE_THREADPOOLS
-  apr_thread_pool_t *threads;
-#else
+#if APR_HAS_THREADS && !HAVE_THREADPOOLS
   apr_threadattr_t *tattr;
   apr_thread_t *tid;
 #endif
-#endif
+  svn_boolean_t is_multi_threaded;
   enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
+  apr_hash_t *fs_config = NULL;
+  svn_boolean_t cache_fulltexts = TRUE;
+  svn_boolean_t cache_txdeltas = TRUE;
+  svn_boolean_t cache_revprops = FALSE;
   apr_uint16_t port = SVN_RA_SVN_PORT;
   const char *host = NULL;
   int family = APR_INET;
@@ -634,30 +696,17 @@ int main(int argc, const char *argv[])
   svn_node_kind_t kind;
   svn_root_pools__t *socket_pools;
 
-  /* Initialize the app. */
-  if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
-    return EXIT_FAILURE;
-
-  /* Create our top-level pool. */
-  pool = svn_pool_create(NULL);
-
 #ifdef SVN_HAVE_SASL
-  SVN_INT_ERR(cyrus_init(pool));
+  SVN_ERR(cyrus_init(pool));
 #endif
 
   /* Check library versions */
-  err = check_lib_versions();
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+  SVN_ERR(check_lib_versions());
 
   /* Initialize the FS library. */
-  err = svn_fs_initialize(pool);
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+  SVN_ERR(svn_fs_initialize(pool));
 
-  err = svn_cmdline__getopt_init(&os, argc, argv, pool);
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+  SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
 
   params.root = "/";
   params.tunnel = FALSE;
@@ -667,12 +716,12 @@ int main(int argc, const char *argv[])
   params.cfg = NULL;
   params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
   params.logger = NULL;
+  params.config_pool = NULL;
+  params.authz_pool = NULL;
+  params.repos_pool = NULL;
   params.vhost = FALSE;
   params.username_case = CASE_ASIS;
   params.memory_cache_size = (apr_uint64_t)-1;
-  params.cache_fulltexts = TRUE;
-  params.cache_txdeltas = TRUE;
-  params.cache_revprops = FALSE;
   params.zero_copy_limit = 0;
   params.error_check_interval = 4096;
 
@@ -682,7 +731,11 @@ int main(int argc, const char *argv[])
       if (APR_STATUS_IS_EOF(status))
         break;
       if (status != APR_SUCCESS)
-        usage(argv[0], pool);
+        {
+          usage(argv[0], pool);
+          *exit_code = EXIT_FAILURE;
+          return SVN_NO_ERROR;
+        }
       switch (opt)
         {
         case '6':
@@ -694,7 +747,7 @@ int main(int argc, const char *argv[])
 
         case 'h':
           help(pool);
-          break;
+          return SVN_NO_ERROR;
 
         case 'q':
           quiet = TRUE;
@@ -735,10 +788,8 @@ int main(int argc, const char *argv[])
 
             err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10);
             if (err)
-              return svn_cmdline_handle_exit_error(
-                       svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
-                                         _("Invalid port '%s'"), arg),
-                       pool, "svnserve: ");
+              return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
+                                       _("Invalid port '%s'"), arg);
             port = (apr_uint16_t)val;
           }
           break;
@@ -768,23 +819,18 @@ int main(int argc, const char *argv[])
           break;
 
         case 'r':
-          SVN_INT_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));
+          SVN_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));
 
-          err = svn_io_check_resolved_path(params.root, &kind, pool);
-          if (err)
-            return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+          SVN_ERR(svn_io_check_resolved_path(params.root, &kind, pool));
           if (kind != svn_node_dir)
             {
-              svn_error_clear
-                (svn_cmdline_fprintf
-                   (stderr, pool,
-                    _("svnserve: Root path '%s' does not exist "
-                      "or is not a directory.\n"), params.root));
-              return EXIT_FAILURE;
+              return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                       _("Root path '%s' does not exist "
+                         "or is not a directory"), params.root);
             }
 
           params.root = svn_dirent_internal_style(params.root, pool);
-          SVN_INT_ERR(svn_dirent_get_absolute(&params.root, params.root, pool));
+          SVN_ERR(svn_dirent_get_absolute(&params.root, params.root, pool));
           break;
 
         case 'R':
@@ -809,18 +855,15 @@ int main(int argc, const char *argv[])
           break;
 
         case SVNSERVE_OPT_CACHE_TXDELTAS:
-          params.cache_txdeltas
-             = svn_tristate__from_word(arg) == svn_tristate_true;
+          cache_txdeltas = svn_tristate__from_word(arg) == svn_tristate_true;
           break;
 
         case SVNSERVE_OPT_CACHE_FULLTEXTS:
-          params.cache_fulltexts
-             = svn_tristate__from_word(arg) == svn_tristate_true;
+          cache_fulltexts = svn_tristate__from_word(arg) == svn_tristate_true;
           break;
 
         case SVNSERVE_OPT_CACHE_REVPROPS:
-          params.cache_revprops
-             = svn_tristate__from_word(arg) == svn_tristate_true;
+          cache_revprops = svn_tristate__from_word(arg) == svn_tristate_true;
           break;
 
         case SVNSERVE_OPT_CLIENT_SPEED:
@@ -851,17 +894,16 @@ int main(int argc, const char *argv[])
 #endif
 
         case SVNSERVE_OPT_CONFIG_FILE:
-          SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
+          SVN_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
           config_filename = svn_dirent_internal_style(config_filename, pool);
-          SVN_INT_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
-                                              pool));
+          SVN_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
+                                          pool));
           break;
 
         case SVNSERVE_OPT_PID_FILE:
-          SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
+          SVN_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
           pid_filename = svn_dirent_internal_style(pid_filename, pool);
-          SVN_INT_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename,
-                                              pool));
+          SVN_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, pool));
           break;
 
          case SVNSERVE_OPT_VIRTUAL_HOST:
@@ -869,10 +911,9 @@ int main(int argc, const char *argv[])
            break;
 
          case SVNSERVE_OPT_LOG_FILE:
-          SVN_INT_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
+          SVN_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
           log_filename = svn_dirent_internal_style(log_filename, pool);
-          SVN_INT_ERR(svn_dirent_get_absolute(&log_filename, log_filename,
-                                              pool));
+          SVN_ERR(svn_dirent_get_absolute(&log_filename, log_filename, pool));
           break;
 
         }
@@ -880,12 +921,16 @@ int main(int argc, const char *argv[])
 
   if (is_version)
     {
-      SVN_INT_ERR(version(quiet, pool));
-      exit(0);
+      SVN_ERR(version(quiet, pool));
+      return SVN_NO_ERROR;
     }
 
   if (os->ind != argc)
-    usage(argv[0], pool);
+    {
+      usage(argv[0], pool);
+      *exit_code = EXIT_FAILURE;
+      return SVN_NO_ERROR;
+    }
 
   if (mode_opt_count != 1)
     {
@@ -898,6 +943,8 @@ int main(int argc, const char *argv[])
 #endif
                        stderr, pool));
       usage(argv[0], pool);
+      *exit_code = EXIT_FAILURE;
+      return SVN_NO_ERROR;
     }
 
   if (handling_opt_count > 1)
@@ -906,31 +953,56 @@ int main(int argc, const char *argv[])
                       _("You may only specify one of -T or --single-thread\n"),
                       stderr, pool));
       usage(argv[0], pool);
+      *exit_code = EXIT_FAILURE;
+      return SVN_NO_ERROR;
     }
 
+  /* construct object pools */
+  is_multi_threaded = handling_mode == connection_mode_thread;
+  fs_config = apr_hash_make(pool);
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
+                cache_txdeltas ? "1" :"0");
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
+                cache_fulltexts ? "1" :"0");
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
+                cache_revprops ? "2" :"0");
+
+  SVN_ERR(svn_repos__config_pool_create(&params.config_pool,
+                                        is_multi_threaded,
+                                        pool));
+  SVN_ERR(svn_repos__authz_pool_create(&params.authz_pool,
+                                       params.config_pool,
+                                       is_multi_threaded,
+                                       pool));
+  SVN_ERR(svn_repos__repos_pool_create(&params.repos_pool,
+                                       fs_config,
+                                       is_multi_threaded,
+                                       pool));
+
   /* If a configuration file is specified, load it and any referenced
    * password and authorization files. */
   if (config_filename)
     {
       params.base = svn_dirent_dirname(config_filename, pool);
 
-      SVN_INT_ERR(svn_config_read3(&params.cfg, config_filename,
-                                   TRUE, /* must_exist */
-                                   FALSE, /* section_names_case_sensitive */
-                                   FALSE, /* option_names_case_sensitive */
-                                   pool));
+      SVN_ERR(svn_repos__config_pool_get(&params.cfg, NULL,
+                                         params.config_pool,
+                                         config_filename, 
+                                         TRUE, /* must_exist */
+                                         FALSE, /* names_case_sensitive */
+                                         NULL,
+                                         pool));
     }
 
   if (log_filename)
-    SVN_INT_ERR(logger__create(&params.logger, log_filename, pool));
+    SVN_ERR(logger__create(&params.logger, log_filename, pool));
+  else if (run_mode == run_mode_listen_once)
+    SVN_ERR(logger__create_for_stderr(&params.logger, pool));
 
   if (params.tunnel_user && run_mode != run_mode_tunnel)
     {
-      svn_error_clear
-        (svn_cmdline_fprintf
-           (stderr, pool,
-            _("Option --tunnel-user is only valid in tunnel mode.\n")));
-      exit(1);
+      return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+               _("Option --tunnel-user is only valid in tunnel mode"));
     }
 
   if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
@@ -944,15 +1016,13 @@ int main(int argc, const char *argv[])
       status = apr_file_open_stdin(&in_file, pool);
       if (status)
         {
-          err = svn_error_wrap_apr(status, _("Can't open stdin"));
-          return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+          return svn_error_wrap_apr(status, _("Can't open stdin"));
         }
 
       status = apr_file_open_stdout(&out_file, pool);
       if (status)
         {
-          err = svn_error_wrap_apr(status, _("Can't open stdout"));
-          return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+          return svn_error_wrap_apr(status, _("Can't open stdout"));
         }
 
       /* Use a subpool for the connection to ensure that if SASL is used
@@ -964,8 +1034,10 @@ int main(int argc, const char *argv[])
                                      params.zero_copy_limit,
                                      params.error_check_interval,
                                      connection_pool);
-      svn_error_clear(serve(conn, &params, connection_pool));
-      exit(0);
+      err = serve(conn, &params, connection_pool);
+      svn_pool_destroy(connection_pool);
+
+      return err;
     }
 
 #ifdef WIN32
@@ -1010,7 +1082,8 @@ int main(int argc, const char *argv[])
             }
 
           svn_error_clear(err);
-          exit(1);
+          *exit_code = EXIT_FAILURE;
+          return SVN_NO_ERROR;
         }
 
       /* The service is now in the "starting" state.  Before the SCM will
@@ -1055,8 +1128,7 @@ int main(int argc, const char *argv[])
                                  sockaddr_info_flags, pool);
   if (status)
     {
-      err = svn_error_wrap_apr(status, _("Can't get address info"));
-      return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+      return svn_error_wrap_apr(status, _("Can't get address info"));
     }
 
 
@@ -1069,25 +1141,32 @@ int main(int argc, const char *argv[])
 #endif
   if (status)
     {
-      err = svn_error_wrap_apr(status, _("Can't create server socket"));
-      return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+      return svn_error_wrap_apr(status, _("Can't create server socket"));
     }
 
   /* Prevents "socket in use" errors when server is killed and quickly
    * restarted. */
-  apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
+  status = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
+  if (status)
+    {
+      return svn_error_wrap_apr(status, _("Can't set options on server socket"));
+    }
 
   status = apr_socket_bind(sock, sa);
   if (status)
     {
-      err = svn_error_wrap_apr(status, _("Can't bind server socket"));
-      return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+      return svn_error_wrap_apr(status, _("Can't bind server socket"));
     }
 
-  apr_socket_listen(sock, ACCEPT_BACKLOG);
+  status = apr_socket_listen(sock, ACCEPT_BACKLOG);
+  if (status)
+    {
+      return svn_error_wrap_apr(status, _("Can't listen on server socket"));
+    }
 
 #if APR_HAS_FORK
   if (run_mode != run_mode_listen_once && !foreground)
+    /* ### ignoring errors... */
     apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
 
   apr_signal(SIGCHLD, sigchld_handler);
@@ -1106,7 +1185,7 @@ int main(int argc, const char *argv[])
 #endif
 
   if (pid_filename)
-    SVN_INT_ERR(write_pid_file(pid_filename, pool));
+    SVN_ERR(write_pid_file(pid_filename, pool));
 
 #ifdef WIN32
   status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
@@ -1148,14 +1227,10 @@ int main(int argc, const char *argv[])
 
   /* we use (and recycle) separate pools for sockets (many small ones)
      and connections (fewer but larger ones) */
-  err = svn_root_pools__create(&socket_pools);
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+  SVN_ERR(svn_root_pools__create(&socket_pools));
 
 #if APR_HAS_THREADS
-  err = svn_root_pools__create(&connection_pools);
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+  SVN_ERR(svn_root_pools__create(&connection_pools));
 #endif
 
 #if HAVE_THREADPOOLS
@@ -1168,8 +1243,7 @@ int main(int argc, const char *argv[])
                                       pool);
       if (status)
         {
-          err = svn_error_wrap_apr (status, _("Can't create thread pool"));
-          return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+          return svn_error_wrap_apr(status, _("Can't create thread pool"));
         }
 
       /* let idle threads linger for a while in case more requests are
@@ -1179,82 +1253,45 @@ int main(int argc, const char *argv[])
       /* don't queue requests unless we reached the worker thread limit */
       apr_thread_pool_threshold_set(threads, 0);
     }
+  else
+    {
+      threads = NULL;
+    }
 #endif
 
   while (1)
     {
-      apr_pool_t *socket_pool;
-
-#ifdef WIN32
-      if (winservice_is_stopping())
-        return ERROR_SUCCESS;
-#endif
-
-      /* Non-standard pool handling.  The main thread never blocks to join
-         the connection threads so it cannot clean up after each one.  So
-         separate pools that can be cleared at thread exit are used. */
-
-      socket_pool = svn_root_pools__acquire_pool(socket_pools);
-
-      status = apr_socket_accept(&usock, sock, socket_pool);
-      if (handling_mode == connection_mode_fork)
-        {
-          /* Collect any zombie child processes. */
-          while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
-                                         socket_pool) == APR_CHILD_DONE)
-            ;
-        }
-      if (APR_STATUS_IS_EINTR(status)
-          || APR_STATUS_IS_ECONNABORTED(status)
-          || APR_STATUS_IS_ECONNRESET(status))
-        {
-          svn_root_pools__release_pool(socket_pool, socket_pools);
-          continue;
-        }
-      if (status)
-        {
-          err = svn_error_wrap_apr
-            (status, _("Can't accept client connection"));
-          return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
-        }
-
+      connection_t *connection = NULL;
+      SVN_ERR(accept_connection(&connection, sock, socket_pools, &params,
+                                handling_mode));
       if (run_mode == run_mode_listen_once)
         {
-          err = serve_socket(usock, &params, socket_pool);
-
-          if (err)
-            svn_handle_error2(err, stdout, FALSE, "svnserve: ");
-          svn_error_clear(err);
-
-          apr_socket_close(usock);
-          apr_socket_close(sock);
-          exit(0);
+          err = serve_socket(connection, connection->pool);
+          close_connection(connection);
+          return err;
         }
 
       switch (handling_mode)
         {
         case connection_mode_fork:
 #if APR_HAS_FORK
-          status = apr_proc_fork(&proc, socket_pool);
+          status = apr_proc_fork(&proc, connection->pool);
           if (status == APR_INCHILD)
             {
+              /* the child would't listen to the main server's socket */
               apr_socket_close(sock);
-              svn_error_clear(serve_socket(usock, &params, socket_pool));
-              apr_socket_close(usock);
-              exit(0);
-            }
-          else if (status == APR_INPARENT)
-            {
-              apr_socket_close(usock);
+
+              /* serve_socket() logs any error it returns, so ignore it. */
+              svn_error_clear(serve_socket(connection, connection->pool));
+              close_connection(connection);
+              return SVN_NO_ERROR;
             }
-          else
+          else if (status != APR_INPARENT)
             {
               err = svn_error_wrap_apr(status, "apr_proc_fork");
               logger__log_error(params.logger, err, NULL, NULL);
               svn_error_clear(err);
-              apr_socket_close(usock);
             }
-          svn_root_pools__release_pool(socket_pool, socket_pools);
 #endif
           break;
 
@@ -1263,52 +1300,79 @@ int main(int argc, const char *argv[])
              particularly sophisticated strategy for a threaded server, it's
              little different from forking one process per connection. */
 #if APR_HAS_THREADS
-          shared_pool = attach_shared_pool(socket_pool, socket_pools);
+          attach_connection(connection);
 
-          thread_data = apr_palloc(socket_pool, sizeof(*thread_data));
-          thread_data->usock = usock;
-          thread_data->params = &params;
-          thread_data->shared_pool = shared_pool;
 #if HAVE_THREADPOOLS
-          status = apr_thread_pool_push(threads, serve_thread, thread_data,
+          status = apr_thread_pool_push(threads, serve_thread, connection,
                                         0, NULL);
 #else
-          status = apr_threadattr_create(&tattr, socket_pool);
+          status = apr_threadattr_create(&tattr, connection->pool);
           if (status)
             {
-              err = svn_error_wrap_apr(status, _("Can't create threadattr"));
-              svn_handle_error2(err, stderr, FALSE, "svnserve: ");
-              svn_error_clear(err);
-              exit(1);
+              return svn_error_wrap_apr(status, _("Can't create threadattr"));
             }
           status = apr_threadattr_detach_set(tattr, 1);
           if (status)
             {
-              err = svn_error_wrap_apr(status, _("Can't set detached state"));
-              svn_handle_error2(err, stderr, FALSE, "svnserve: ");
-              svn_error_clear(err);
-              exit(1);
+              return svn_error_wrap_apr(status, _("Can't set detached state"));
             }
-          status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
-                                     shared_pool->pool);
+          status = apr_thread_create(&tid, tattr, serve_thread, connection,
+                                     connection->pool);
 #endif
           if (status)
             {
-              err = svn_error_wrap_apr(status, THREAD_ERROR_MSG);
-              svn_handle_error2(err, stderr, FALSE, "svnserve: ");
-              svn_error_clear(err);
-              exit(1);
+              return svn_error_wrap_apr(status, THREAD_ERROR_MSG);
             }
-          release_shared_pool(shared_pool);
 #endif
           break;
 
         case connection_mode_single:
           /* Serve one connection at a time. */
-          svn_error_clear(serve_socket(usock, &params, socket_pool));
-          svn_root_pools__release_pool(socket_pool, socket_pools);
+          /* serve_socket() logs any error it returns, so ignore it. */
+          svn_error_clear(serve_socket(connection, connection->pool));
         }
+
+      close_connection(connection);
     }
 
   /* NOTREACHED */
 }
+
+int
+main(int argc, const char *argv[])
+{
+  apr_pool_t *pool;
+  int exit_code = EXIT_SUCCESS;
+  svn_error_t *err;
+
+  /* Initialize the app. */
+  if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
+    return EXIT_FAILURE;
+
+  /* Create our top-level pool. */
+  pool = svn_pool_create(NULL);
+
+  err = sub_main(&exit_code, argc, argv, pool);
+
+  /* Flush stdout and report if it fails. It would be flushed on exit anyway
+     but this makes sure that output is not silently lost if it fails. */
+  err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
+
+  if (err)
+    {
+      exit_code = EXIT_FAILURE;
+      svn_cmdline_handle_exit_error(err, NULL, "svnserve: ");
+    }
+
+#if HAVE_THREADPOOLS
+  /* Explicitly wait for all threads to exit.  As we found out with similar
+     code in our C test framework, the memory pool cleanup below cannot be
+     trusted to do the right thing. */
+  if (threads)
+    apr_thread_pool_destroy(threads);
+#endif
+
+  /* this will also close the server's socket */
+  svn_pool_destroy(pool);
+  return exit_code;
+}

Modified: subversion/branches/fsfs-improvements/subversion/svnsync/svnsync.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/svnsync/svnsync.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/svnsync/svnsync.c (original)
+++ subversion/branches/fsfs-improvements/subversion/svnsync/svnsync.c Wed Nov 27 07:53:29 2013
@@ -1875,8 +1875,13 @@ help_cmd(apr_getopt_t *os, void *baton, 
 
 /*** Main ***/
 
-int
-main(int argc, const char *argv[])
+/*
+ * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
+ * either return an error to be displayed, or set *EXIT_CODE to non-zero and
+ * return SVN_NO_ERROR.
+ */
+static svn_error_t *
+sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
 {
   const svn_opt_subcommand_desc2_t *subcommand = NULL;
   apr_array_header_t *received_opts;
@@ -1884,7 +1889,6 @@ main(int argc, const char *argv[])
   svn_config_t *config;
   apr_status_t apr_err;
   apr_getopt_t *os;
-  apr_pool_t *pool;
   svn_error_t *err;
   int opt_id, i;
   const char *username = NULL, *source_username = NULL, *sync_username = NULL;
@@ -1893,23 +1897,10 @@ main(int argc, const char *argv[])
   const char *source_prop_encoding = NULL;
   svn_boolean_t force_interactive = FALSE;
 
-  if (svn_cmdline_init("svnsync", stderr) != EXIT_SUCCESS)
-    {
-      return EXIT_FAILURE;
-    }
-
-  err = check_lib_versions();
-  if (err)
-    return svn_cmdline_handle_exit_error(err, NULL, "svnsync: ");
-
-  /* Create our top-level pool.  Use a separate mutexless allocator,
-   * given this application is single threaded.
-   */
-  pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+  /* Check library versions */
+  SVN_ERR(check_lib_versions());
 
-  err = svn_ra_initialize(pool);
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
+  SVN_ERR(svn_ra_initialize(pool));
 
   /* Initialize the option baton. */
   memset(&opt_baton, 0, sizeof(opt_baton));
@@ -1920,14 +1911,12 @@ main(int argc, const char *argv[])
 
   if (argc <= 1)
     {
-      SVN_INT_ERR(help_cmd(NULL, NULL, pool));
-      svn_pool_destroy(pool);
-      return EXIT_FAILURE;
+      SVN_ERR(help_cmd(NULL, NULL, pool));
+      *exit_code = EXIT_FAILURE;
+      return SVN_NO_ERROR;
     }
 
-  err = svn_cmdline__getopt_init(&os, argc, argv, pool);
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
+  SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
 
   os->interleave = 1;
 
@@ -1941,9 +1930,9 @@ main(int argc, const char *argv[])
         break;
       else if (apr_err)
         {
-          SVN_INT_ERR(help_cmd(NULL, NULL, pool));
-          svn_pool_destroy(pool);
-          return EXIT_FAILURE;
+          SVN_ERR(help_cmd(NULL, NULL, pool));
+          *exit_code = EXIT_FAILURE;
+          return SVN_NO_ERROR;
         }
 
       APR_ARRAY_PUSH(received_opts, int) = opt_id;
@@ -2005,12 +1994,9 @@ main(int argc, const char *argv[])
                     apr_array_make(pool, 1,
                                    sizeof(svn_cmdline__config_argument_t*));
 
-            err = svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool);
-            if (!err)
-              err = svn_cmdline__parse_config_option(config_options,
-                                                     opt_arg, pool);
-            if (err)
-              return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
+            SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool));
+            SVN_ERR(svn_cmdline__parse_config_option(config_options,
+                                                     opt_arg, pool));
             break;
 
           case svnsync_opt_source_prop_encoding:
@@ -2044,13 +2030,11 @@ main(int argc, const char *argv[])
                                        opt_arg, pool) != 0)
               {
                 const char *utf8_opt_arg;
-                err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
-                if (! err)
-                  err = svn_error_createf(
+                SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
+                return svn_error_createf(
                             SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
                             _("Syntax error in revision argument '%s'"),
                             utf8_opt_arg);
-                return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
               }
 
             /* We only allow numbers and 'HEAD'. */
@@ -2060,10 +2044,9 @@ main(int argc, const char *argv[])
                     (opt_baton.end_rev.kind != svn_opt_revision_head) &&
                     (opt_baton.end_rev.kind != svn_opt_revision_unspecified)))
               {
-                err = svn_error_createf(
+                return svn_error_createf(
                           SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
                           _("Invalid revision range '%s' provided"), opt_arg);
-                return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
               }
             break;
 
@@ -2074,14 +2057,14 @@ main(int argc, const char *argv[])
 
           default:
             {
-              SVN_INT_ERR(help_cmd(NULL, NULL, pool));
-              svn_pool_destroy(pool);
-              return EXIT_FAILURE;
+              SVN_ERR(help_cmd(NULL, NULL, pool));
+              *exit_code = EXIT_FAILURE;
+              return SVN_NO_ERROR;
             }
         }
 
-      if(opt_err)
-        return svn_cmdline_handle_exit_error(opt_err, pool, "svnsync: ");
+      if (opt_err)
+        return opt_err;
     }
 
   if (opt_baton.help)
@@ -2091,10 +2074,9 @@ main(int argc, const char *argv[])
    * exclusive. */
   if (opt_baton.non_interactive && force_interactive)
     {
-      err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                             _("--non-interactive and --force-interactive "
-                               "are mutually exclusive"));
-      return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
+      return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                              _("--non-interactive and --force-interactive "
+                                "are mutually exclusive"));
     }
   else
     opt_baton.non_interactive = !svn_cmdline__be_interactive(
@@ -2108,12 +2090,11 @@ main(int argc, const char *argv[])
       && (source_username || sync_username
           || source_password || sync_password))
     {
-      err = svn_error_create
+      return svn_error_create
         (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
          _("Cannot use --username or --password with any of "
            "--source-username, --source-password, --sync-username, "
            "or --sync-password.\n"));
-      return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
     }
   if (username)
     {
@@ -2133,24 +2114,20 @@ main(int argc, const char *argv[])
   /* Disallow mixing of --steal-lock and --disable-locking. */
   if (opt_baton.steal_lock && opt_baton.disable_locking)
     {
-      err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                             _("--disable-locking and --steal-lock are "
-                               "mutually exclusive"));
-      return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
+      return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                              _("--disable-locking and --steal-lock are "
+                                "mutually exclusive"));
     }
 
   /* --trust-server-cert can only be used with --non-interactive */
   if (opt_baton.trust_server_cert && !opt_baton.non_interactive)
     {
-      err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                             _("--trust-server-cert requires "
-                               "--non-interactive"));
-      return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
+      return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                              _("--trust-server-cert requires "
+                                "--non-interactive"));
     }
 
-  err = svn_config_ensure(opt_baton.config_dir, pool);
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "synsync: ");
+  SVN_ERR(svn_config_ensure(opt_baton.config_dir, pool));
 
   if (subcommand == NULL)
     {
@@ -2169,9 +2146,9 @@ main(int argc, const char *argv[])
             }
           else
             {
-              SVN_INT_ERR(help_cmd(NULL, NULL, pool));
-              svn_pool_destroy(pool);
-              return EXIT_FAILURE;
+              SVN_ERR(help_cmd(NULL, NULL, pool));
+              *exit_code = EXIT_FAILURE;
+              return SVN_NO_ERROR;
             }
         }
       else
@@ -2181,9 +2158,9 @@ main(int argc, const char *argv[])
                                                          first_arg);
           if (subcommand == NULL)
             {
-              SVN_INT_ERR(help_cmd(NULL, NULL, pool));
-              svn_pool_destroy(pool);
-              return EXIT_FAILURE;
+              SVN_ERR(help_cmd(NULL, NULL, pool));
+              *exit_code = EXIT_FAILURE;
+              return SVN_NO_ERROR;
             }
         }
     }
@@ -2204,23 +2181,20 @@ main(int argc, const char *argv[])
           svn_opt_format_option(&optstr, badopt, FALSE, pool);
           if (subcommand->name[0] == '-')
             {
-              SVN_INT_ERR(help_cmd(NULL, NULL, pool));
+              SVN_ERR(help_cmd(NULL, NULL, pool));
             }
           else
             {
-              err = svn_error_createf
+              return svn_error_createf
                 (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
                  _("Subcommand '%s' doesn't accept option '%s'\n"
                    "Type 'svnsync help %s' for usage.\n"),
                  subcommand->name, optstr, subcommand->name);
-              return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
             }
         }
     }
 
-  err = svn_config_get_config(&opt_baton.config, opt_baton.config_dir, pool);
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
+  SVN_ERR(svn_config_get_config(&opt_baton.config, opt_baton.config_dir, pool));
 
   /* Update the options in the config */
   if (config_options)
@@ -2296,10 +2270,40 @@ main(int argc, const char *argv[])
                                      _("Try 'svnsync help' for more info"));
         }
 
-      return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
+      return err;
     }
 
-  svn_pool_destroy(pool);
+  return SVN_NO_ERROR;
+}
 
-  return EXIT_SUCCESS;
+int
+main(int argc, const char *argv[])
+{
+  apr_pool_t *pool;
+  int exit_code = EXIT_SUCCESS;
+  svn_error_t *err;
+
+  /* Initialize the app. */
+  if (svn_cmdline_init("svnsync", stderr) != EXIT_SUCCESS)
+    return EXIT_FAILURE;
+
+  /* Create our top-level pool.  Use a separate mutexless allocator,
+   * given this application is single threaded.
+   */
+  pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+
+  err = sub_main(&exit_code, argc, argv, pool);
+
+  /* Flush stdout and report if it fails. It would be flushed on exit anyway
+     but this makes sure that output is not silently lost if it fails. */
+  err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
+
+  if (err)
+    {
+      exit_code = EXIT_FAILURE;
+      svn_cmdline_handle_exit_error(err, NULL, "svnsync: ");
+    }
+
+  svn_pool_destroy(pool);
+  return exit_code;
 }

Modified: subversion/branches/fsfs-improvements/subversion/svnversion/svnversion.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/svnversion/svnversion.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/svnversion/svnversion.c (original)
+++ subversion/branches/fsfs-improvements/subversion/svnversion/svnversion.c Wed Nov 27 07:53:29 2013
@@ -47,7 +47,6 @@ usage(apr_pool_t *pool)
 {
   svn_error_clear(svn_cmdline_fprintf
                   (stderr, pool, _("Type 'svnversion --help' for usage.\n")));
-  exit(1);
 }
 
 
@@ -97,7 +96,6 @@ help(const apr_getopt_option_t *options,
       ++options;
     }
   svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
-  exit(0);
 }
 
 
@@ -117,16 +115,19 @@ check_lib_versions(void)
 }
 
 /*
+ * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
+ * either return an error to be displayed, or set *EXIT_CODE to non-zero and
+ * return SVN_NO_ERROR.
+ *
  * Why is this not an svn subcommand?  I have this vague idea that it could
  * be run as part of the build process, with the output embedded in the svn
  * program.  Obviously we don't want to have to run svn when building svn.
  */
-int
-main(int argc, const char *argv[])
+static svn_error_t *
+sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
 {
   const char *wc_path, *trail_url;
   const char *local_abspath;
-  apr_pool_t *pool;
   svn_wc_revision_status_t *res;
   svn_boolean_t no_newline = FALSE, committed = FALSE;
   svn_error_t *err;
@@ -146,33 +147,18 @@ main(int argc, const char *argv[])
       {0,             0,  0,  0}
     };
 
-  /* Initialize the app. */
-  if (svn_cmdline_init("svnversion", stderr) != EXIT_SUCCESS)
-    return EXIT_FAILURE;
-
-  /* Create our top-level pool.  Use a separate mutexless allocator,
-   * given this application is single threaded.
-   */
-  pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
-
   /* Check library versions */
-  err = check_lib_versions();
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "svnversion: ");
+  SVN_ERR(check_lib_versions());
 
 #if defined(WIN32) || defined(__CYGWIN__)
   /* Set the working copy administrative directory name. */
   if (getenv("SVN_ASP_DOT_NET_HACK"))
     {
-      err = svn_wc_set_adm_dir("_svn", pool);
-      if (err)
-        return svn_cmdline_handle_exit_error(err, pool, "svnversion: ");
+      SVN_ERR(svn_wc_set_adm_dir("_svn", pool));
     }
 #endif
 
-  err = svn_cmdline__getopt_init(&os, argc, argv, pool);
-  if (err)
-    return svn_cmdline_handle_exit_error(err, pool, "svnversion: ");
+  SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
 
   os->interleave = 1;
   while (1)
@@ -183,7 +169,11 @@ main(int argc, const char *argv[])
       if (APR_STATUS_IS_EOF(status))
         break;
       if (status != APR_SUCCESS)
-        usage(pool);  /* this will exit() */
+        {
+          *exit_code = EXIT_FAILURE;
+          usage(pool);
+          return SVN_NO_ERROR;
+        }
 
       switch (opt)
         {
@@ -198,35 +188,39 @@ main(int argc, const char *argv[])
           break;
         case 'h':
           help(options, pool);
-          break;
+          return SVN_NO_ERROR;
         case SVNVERSION_OPT_VERSION:
           is_version = TRUE;
           break;
         default:
-          usage(pool);  /* this will exit() */
+          *exit_code = EXIT_FAILURE;
+          usage(pool);
+          return SVN_NO_ERROR;
         }
     }
 
   if (is_version)
     {
-      SVN_INT_ERR(version(quiet, pool));
-      exit(0);
+      SVN_ERR(version(quiet, pool));
+      return SVN_NO_ERROR;
     }
   if (os->ind > argc || os->ind < argc - 2)
-    usage(pool);  /* this will exit() */
+    {
+      *exit_code = EXIT_FAILURE;
+      usage(pool);
+      return SVN_NO_ERROR;
+    }
 
-  SVN_INT_ERR(svn_utf_cstring_to_utf8(&wc_path,
-                                      (os->ind < argc) ? os->argv[os->ind]
-                                                       : ".",
-                                      pool));
-
-  SVN_INT_ERR(svn_opt__arg_canonicalize_path(&wc_path, wc_path, pool));
-  SVN_INT_ERR(svn_dirent_get_absolute(&local_abspath, wc_path, pool));
-  SVN_INT_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool));
+  SVN_ERR(svn_utf_cstring_to_utf8(&wc_path,
+                                  (os->ind < argc) ? os->argv[os->ind] : ".",
+                                  pool));
+
+  SVN_ERR(svn_opt__arg_canonicalize_path(&wc_path, wc_path, pool));
+  SVN_ERR(svn_dirent_get_absolute(&local_abspath, wc_path, pool));
+  SVN_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool));
 
   if (os->ind+1 < argc)
-    SVN_INT_ERR(svn_utf_cstring_to_utf8(&trail_url, os->argv[os->ind+1],
-                                        pool));
+    SVN_ERR(svn_utf_cstring_to_utf8(&trail_url, os->argv[os->ind+1], pool));
   else
     trail_url = NULL;
 
@@ -241,63 +235,87 @@ main(int argc, const char *argv[])
 
       svn_error_clear(err);
 
-      SVN_INT_ERR(svn_io_check_special_path(local_abspath, &kind, &special,
-                                            pool));
+      SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &special, pool));
 
       if (special)
-        SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned symlink%s"),
-                                       no_newline ? "" : "\n"));
+        SVN_ERR(svn_cmdline_printf(pool, _("Unversioned symlink%s"),
+                                   no_newline ? "" : "\n"));
       else if (kind == svn_node_dir)
-        SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned directory%s"),
-                                       no_newline ? "" : "\n"));
+        SVN_ERR(svn_cmdline_printf(pool, _("Unversioned directory%s"),
+                                   no_newline ? "" : "\n"));
       else if (kind == svn_node_file)
-        SVN_INT_ERR(svn_cmdline_printf(pool, _("Unversioned file%s"),
-                                       no_newline ? "" : "\n"));
+        SVN_ERR(svn_cmdline_printf(pool, _("Unversioned file%s"),
+                                   no_newline ? "" : "\n"));
       else
         {
-          SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool,
-                                          kind == svn_node_none
-                                           ? _("'%s' doesn't exist\n")
-                                           : _("'%s' is of unknown type\n"),
-                                          svn_dirent_local_style(local_abspath,
-                                                                 pool)));
-          svn_pool_destroy(pool);
-          return EXIT_FAILURE;
+          SVN_ERR(svn_cmdline_fprintf(stderr, pool,
+                                      kind == svn_node_none
+                                       ? _("'%s' doesn't exist\n")
+                                       : _("'%s' is of unknown type\n"),
+                                      svn_dirent_local_style(local_abspath,
+                                                             pool)));
+          *exit_code = EXIT_FAILURE;
+          return SVN_NO_ERROR;
         }
-      svn_pool_destroy(pool);
-      return EXIT_SUCCESS;
+      return SVN_NO_ERROR;
     }
 
-  SVN_INT_ERR(err);
+  SVN_ERR(err);
 
   if (! SVN_IS_VALID_REVNUM(res->min_rev))
     {
       /* Local uncommitted modifications, no revision info was found. */
-      SVN_INT_ERR(svn_cmdline_printf(pool, _("Uncommitted local addition, "
-                                             "copy or move%s"),
-                                             no_newline ? "" : "\n"));
-      svn_pool_destroy(pool);
-      return EXIT_SUCCESS;
+      SVN_ERR(svn_cmdline_printf(pool, _("Uncommitted local addition, "
+                                         "copy or move%s"),
+                                 no_newline ? "" : "\n"));
+      return SVN_NO_ERROR;
     }
 
   /* Build compact '123[:456]M?S?' string. */
-  SVN_INT_ERR(svn_cmdline_printf(pool, "%ld", res->min_rev));
+  SVN_ERR(svn_cmdline_printf(pool, "%ld", res->min_rev));
   if (res->min_rev != res->max_rev)
-    SVN_INT_ERR(svn_cmdline_printf(pool, ":%ld", res->max_rev));
+    SVN_ERR(svn_cmdline_printf(pool, ":%ld", res->max_rev));
   if (res->modified)
-    SVN_INT_ERR(svn_cmdline_fputs("M", stdout, pool));
+    SVN_ERR(svn_cmdline_fputs("M", stdout, pool));
   if (res->switched)
-    SVN_INT_ERR(svn_cmdline_fputs("S", stdout, pool));
+    SVN_ERR(svn_cmdline_fputs("S", stdout, pool));
   if (res->sparse_checkout)
-    SVN_INT_ERR(svn_cmdline_fputs("P", stdout, pool));
+    SVN_ERR(svn_cmdline_fputs("P", stdout, pool));
 
   if (! no_newline)
-    SVN_INT_ERR(svn_cmdline_fputs("\n", stdout, pool));
+    SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
 
-  svn_pool_destroy(pool);
+  return SVN_NO_ERROR;
+}
 
-  /* Flush stdout to make sure that the user will see any printing errors. */
-  SVN_INT_ERR(svn_cmdline_fflush(stdout));
+int
+main(int argc, const char *argv[])
+{
+  apr_pool_t *pool;
+  int exit_code = EXIT_SUCCESS;
+  svn_error_t *err;
 
-  return EXIT_SUCCESS;
+  /* Initialize the app. */
+  if (svn_cmdline_init("svnversion", stderr) != EXIT_SUCCESS)
+    return EXIT_FAILURE;
+
+  /* Create our top-level pool.  Use a separate mutexless allocator,
+   * given this application is single threaded.
+   */
+  pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+
+  err = sub_main(&exit_code, argc, argv, pool);
+
+  /* Flush stdout and report if it fails. It would be flushed on exit anyway
+     but this makes sure that output is not silently lost if it fails. */
+  err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
+
+  if (err)
+    {
+      exit_code = EXIT_FAILURE;
+      svn_cmdline_handle_exit_error(err, NULL, "svnversion: ");
+    }
+
+  svn_pool_destroy(pool);
+  return exit_code;
 }

Modified: subversion/branches/fsfs-improvements/subversion/tests/cmdline/checkout_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/cmdline/checkout_tests.py?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/cmdline/checkout_tests.py (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/cmdline/checkout_tests.py Wed Nov 27 07:53:29 2013
@@ -1116,8 +1116,25 @@ def checkout_wc_from_drive(sbox):
       'B/F'               : Item(status='A '),
       'B/lambda'          : Item(status='A '),
     })
+
+    expected_wc = wc.State('', {
+      'C'         : Item(),
+      'B/E/beta'  : Item(contents="This is the file 'beta'.\n"),
+      'B/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
+      'B/lambda'  : Item(contents="This is the file 'lambda'.\n"),
+      'B/F'       : Item(),
+      'D/H/omega' : Item(contents="This is the file 'omega'.\n"),
+      'D/H/psi'   : Item(contents="This is the file 'psi'.\n"),
+      'D/H/chi'   : Item(contents="This is the file 'chi'.\n"),
+      'D/G/rho'   : Item(contents="This is the file 'rho'.\n"),
+      'D/G/tau'   : Item(contents="This is the file 'tau'.\n"),
+      'D/G/pi'    : Item(contents="This is the file 'pi'.\n"),
+      'D/gamma'   : Item(contents="This is the file 'gamma'.\n"),
+      'mu'        : Item(contents="This is the file 'mu'.\n"),    
+    })
+    
     svntest.actions.run_and_verify_checkout(repo_url + '/A', wc2_dir,
-                                            expected_output, None,
+                                            expected_output, expected_wc,
                                             None, None, None, None)
 
     wc3_dir = sbox.add_wc_path('3')
@@ -1133,8 +1150,18 @@ def checkout_wc_from_drive(sbox):
       'gamma'             : Item(status='A '),
     })
 
+    expected_wc = wc.State('', {
+      'H/chi'   : Item(contents="This is the file 'chi'.\n"),
+      'H/psi'   : Item(contents="This is the file 'psi'.\n"),
+      'H/omega' : Item(contents="This is the file 'omega'.\n"),
+      'G/pi'    : Item(contents="This is the file 'pi'.\n"),
+      'G/tau'   : Item(contents="This is the file 'tau'.\n"),
+      'G/rho'   : Item(contents="This is the file 'rho'.\n"),
+      'gamma'   : Item(contents="This is the file 'gamma'.\n"),    
+    })
+
     svntest.actions.run_and_verify_checkout(repo_url + '/A/D', wc3_dir,
-                                            expected_output, None,
+                                            expected_output, expected_wc,
                                             None, None, None, None)
 
   finally:

Modified: subversion/branches/fsfs-improvements/subversion/tests/cmdline/davautocheck.sh
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/cmdline/davautocheck.sh?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/cmdline/davautocheck.sh (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/cmdline/davautocheck.sh Wed Nov 27 07:53:29 2013
@@ -429,9 +429,17 @@ User                $(id -un)
 Group               $(id -gn)
 __EOF__
 else
+HTTPD_LOCK="$HTTPD_ROOT/lock"
+mkdir "$HTTPD_LOCK" \
+  || fail "couldn't create lock directory '$HTTPD_LOCK'"
   cat >> "$HTTPD_CFG" <<__EOF__
-# TODO: maybe uncomment this for prefork,worker MPMs only?
-# Mutex file:lock mpm-accept
+# worker and prefork MUST have a mpm-accept lockfile in 2.3.0+
+<IfModule worker.c>
+  Mutex "file:$HTTPD_LOCK" mpm-accept
+</IfModule>
+<IfModule prefork.c>
+  Mutex "file:$HTTPD_LOCK" mpm-accept
+</IfModule>
 __EOF__
 fi
 
@@ -447,7 +455,7 @@ cat >> "$HTTPD_CFG" <<__EOF__
 Listen              $HTTPD_PORT
 ServerName          localhost
 PidFile             "$HTTPD_PID"
-LogFormat           "%h %l %u %t \"%r\" %>s %b" common
+LogFormat           "%h %l %u %t \"%r\" %>s %b \"%f\"" common
 CustomLog           "$HTTPD_ACCESS_LOG" common
 ErrorLog            "$HTTPD_ERROR_LOG"
 LogLevel            debug

Modified: subversion/branches/fsfs-improvements/subversion/tests/cmdline/diff_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/cmdline/diff_tests.py?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/cmdline/diff_tests.py (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/cmdline/diff_tests.py Wed Nov 27 07:53:29 2013
@@ -4648,6 +4648,26 @@ def diff_local_missing_obstruction(sbox)
                                      'diff', wc_dir)
 
 
+@Issue(4444)
+def diff_move_inside_copy(sbox):
+  "diff copied-along child that contains a moved file"
+  sbox.build(read_only=True)
+  wc_dir = sbox.wc_dir
+
+  d_path = 'A/D'
+  d_copy = 'A/D-copy'
+  h_path = 'A/D-copy/H'
+  chi_path = '%s/chi' % h_path
+  chi_moved = '%s/chi-moved' % h_path
+
+  sbox.simple_copy(d_path, d_copy)
+  sbox.simple_move(chi_path, chi_moved)
+  sbox.simple_append(chi_moved, 'a new line')
+
+  # Bug: Diffing the copied-along parent directory asserts
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'diff', sbox.ospath(h_path))
+
 ########################################################################
 #Run the tests
 
@@ -4729,6 +4749,7 @@ test_list = [ None,
               diff_repos_empty_file_addition,
               diff_missing_tree_conflict_victim,
               diff_local_missing_obstruction,
+              diff_move_inside_copy,
               ]
 
 if __name__ == '__main__':

Modified: subversion/branches/fsfs-improvements/subversion/tests/cmdline/move_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/cmdline/move_tests.py?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/cmdline/move_tests.py (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/cmdline/move_tests.py Wed Nov 27 07:53:29 2013
@@ -1433,7 +1433,6 @@ def move_many_update_delete(sbox):
   # Would be nice if we could run the resolver as a separate step, 
   # but 'svn resolve' just fails for any value but working
 
-@XFail()
 def move_many_update_add(sbox):
   "move many and add-on-update"
 
@@ -1457,7 +1456,7 @@ def move_many_update_add(sbox):
      'B/A/A'             : Item(status='  ', treeconflict='U'),
      'B/A/A/BB'          : Item(status='  ', treeconflict='A'),
      # And while resolving
-     'A/A/'              : Item(status='  ', treeconflict='C')
+     'A/A'               : Item(status='  ', treeconflict='C')
     })
 
   expected_status.tweak('',
@@ -1485,9 +1484,17 @@ def move_many_update_add(sbox):
   expected_status.tweak('',
                         'B', 'B/A', 'B/A/A', 'B/A/A/A',
                         'C', 'C/A', 'C/A/A', 'C/A/A/A',
+                        'B/A/A/BB',
                         wc_rev='4')
 
+  expected_status.add({
+        'C/A/A/BB'          : Item(status='D ', wc_rev='4'),
+    })
+
+  expected_status.tweak('A/A/A', treeconflict='C')
+
   expected_output = svntest.wc.State(wc_dir, {
+     'A/A/A'             : Item(status='  ', treeconflict='C'),
      'C/A'               : Item(status='  ', treeconflict='C'),
      'C/A/A'             : Item(status='  ', treeconflict='U'),
      'C/A/A/BB'          : Item(status='  ', treeconflict='A'),

Modified: subversion/branches/fsfs-improvements/subversion/tests/cmdline/redirect_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/cmdline/redirect_tests.py?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/cmdline/redirect_tests.py (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/cmdline/redirect_tests.py Wed Nov 27 07:53:29 2013
@@ -140,6 +140,44 @@ def redirected_update(sbox):
   verify_url(wc_dir, sbox.repo_url)
 
 #----------------------------------------------------------------------
+@SkipUnless(svntest.main.is_ra_type_dav)
+def redirected_nonroot_update(sbox):
+  "redirected update of non-repos-root wc"
+
+  sbox.build(create_wc=False)
+  wc_dir = sbox.wc_dir
+  checkout_url = sbox.repo_url + '/A'
+  relocate_url = sbox.redirected_root_url() + '/A'
+
+  # Checkout a subdir of the repository root.
+  exit_code, out, err = svntest.main.run_svn(None, 'co',
+                                             checkout_url, wc_dir)
+  if err:
+    raise svntest.Failure
+  
+  # Relocate (by cheating) the working copy to the redirect URL.  When
+  # we then update, we'll expect to find ourselves automagically back
+  # to the original URL.  (This is because we can't easily introduce a
+  # redirect to the Apache configuration from the test suite here.)
+  svntest.actions.no_relocate_validation()
+  exit_code, out, err = svntest.main.run_svn(None, 'sw', '--relocate',
+                                             checkout_url, relocate_url,
+                                             wc_dir)
+  svntest.actions.do_relocate_validation()
+
+  # Now update the working copy.
+  exit_code, out, err = svntest.main.run_svn(None, 'up', wc_dir)
+  if err:
+    raise svntest.Failure
+  if not re.match("^Updating '.*':", out[0]):
+    raise svntest.Failure
+  if not redirect_regex.match(out[1]):
+    raise svntest.Failure
+
+  # Verify that we have the expected URL.
+  verify_url(wc_dir, checkout_url)
+
+#----------------------------------------------------------------------
 
 ########################################################################
 # Run the tests
@@ -149,6 +187,7 @@ test_list = [ None,
               temporary_redirect,
               redirected_checkout,
               redirected_update,
+              redirected_nonroot_update,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/fsfs-improvements/subversion/tests/cmdline/stat_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/cmdline/stat_tests.py?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/cmdline/stat_tests.py (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/cmdline/stat_tests.py Wed Nov 27 07:53:29 2013
@@ -2119,6 +2119,95 @@ def status_path_handling(sbox):
   expected_status = svntest.actions.get_virginal_state(rel_wc_dir, 1)
   svntest.actions.run_and_verify_status(rel_wc_dir, expected_status)
 
+def status_move_missing_direct(sbox):
+  "move information when status is called directly"
+  
+  sbox.build()
+  sbox.simple_copy('A', 'Z')
+  sbox.simple_commit('')
+  sbox.simple_update('')
+  
+  sbox.simple_move('Z', 'ZZ')
+  sbox.simple_move('A', 'Z')
+  sbox.simple_move('Z/B', 'ZB')
+  sbox.simple_mkdir('Z/B')
+  sbox.simple_move('ZB/E', 'Z/B/E')
+
+  # Somehow 'svn status' now shows different output for 'ZB/E'
+  # when called directly and via an ancestor, as this handles
+  # multi-layer in a different way
+  
+  # Note that the status output may change over different Subversion revisions,
+  # but the status on a node should be identical anyway 'svn status' is called
+  # on it.
+  
+  expected_output = [
+    'A  +    %s\n' % sbox.ospath('ZB'),
+    '        > moved from %s\n' % os.path.join('..', 'Z', 'B'),    
+    'D  +    %s\n' % sbox.ospath('ZB/E'),
+    '        > moved to %s\n' % os.path.join('..', 'Z', 'B', 'E'),
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'status',
+                                     sbox.ospath('ZB'), '--depth', 'immediates')
+
+  # And calling svn status on just 'ZB/E' should have the same result for this node
+  # except that we calculate the relative path from a different base
+  expected_output = [
+    'D  +    %s\n' % sbox.ospath('ZB/E'),
+    '        > moved to %s\n' % os.path.join('..', '..', 'Z', 'B', 'E'),
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'status',
+                                     sbox.ospath('ZB/E'), '--depth', 'empty')
+
+def status_move_missing_direct_base(sbox):
+  "move when status is called directly with base"
+  
+  sbox.build()
+  sbox.simple_copy('A', 'Z')
+  sbox.simple_mkdir('Q')
+  sbox.simple_mkdir('Q/ZB')
+  sbox.simple_mkdir('Q/ZB/E')
+  sbox.simple_commit('')
+  sbox.simple_update('')
+  
+  sbox.simple_rm('Q')
+  sbox.simple_mkdir('Q')
+  
+  sbox.simple_move('Z', 'ZZ')
+  sbox.simple_move('A', 'Z')
+  sbox.simple_move('Z/B', 'Q/ZB')
+  sbox.simple_mkdir('Z/B')
+  sbox.simple_move('Q/ZB/E', 'Z/B/E')
+
+  # Somehow 'svn status' now shows different output for 'Q/ZB/E'
+  # when called directly and via an ancestor, as this handles
+  # multi-layer in a different way
+  
+  # Note that the status output may change over different Subversion revisions,
+  # but the status on a node should be identical anyway 'svn status' is called
+  # on it.
+  
+  # This test had a different result as status_move_missing_direct at the time of
+  # writing this test.
+  
+  expected_output = [
+    'A  +    %s\n' % sbox.ospath('Q/ZB'),
+    '        > moved from %s\n' % os.path.join('..', '..', 'Z', 'B'),
+    'D  +    %s\n' % sbox.ospath('Q/ZB/E'),
+    '        > moved to %s\n' % os.path.join('..', '..', 'Z', 'B', 'E'),
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'status',
+                                     sbox.ospath('Q/ZB'), '--depth', 'immediates')
+
+  # And calling svn status on just 'ZB/E' should have the same result for this node,
+  # except that the moved_to information is calculated from the node itself
+  expected_output = [
+    'D  +    %s\n' % sbox.ospath('Q/ZB/E'),
+    '        > moved to %s\n' % os.path.join('..', '..', '..', 'Z', 'B', 'E'),
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'status',
+                                     sbox.ospath('Q/ZB/E'), '--depth', 'empty')
+
 ########################################################################
 # Run the tests
 
@@ -2167,6 +2256,8 @@ test_list = [ None,
               status_case_changed,
               move_update_timestamps,
               status_path_handling,
+              status_move_missing_direct,
+              status_move_missing_direct_base,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/fsfs-improvements/subversion/tests/cmdline/svnadmin_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/cmdline/svnadmin_tests.py?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/cmdline/svnadmin_tests.py (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/cmdline/svnadmin_tests.py Wed Nov 27 07:53:29 2013
@@ -1931,10 +1931,8 @@ def verify_invalid_path_changes(sbox):
 
   sbox.build(create_wc = False)
   repo_url = sbox.repo_url
-  B_url = sbox.repo_url + '/B'
-  C_url = sbox.repo_url + '/C'
 
-  # Create A/B/E/bravo in r2.
+  # Create a number of revisions each adding a single path
   for r in range(2,20):
     svntest.actions.run_and_verify_svn(None, None, [],
                                        'mkdir', '-m', 'log_msg',
@@ -1953,11 +1951,13 @@ def verify_invalid_path_changes(sbox):
 
   # del non-existent node
   set_changed_path_list(fsfs_file(sbox.repo_dir, 'revs', '6'),
-                        "_0.0.t5-2 del-dir false false /C\n\n")
+                        "_0.0.t5-2 delete-dir false false /C\n\n")
 
   # del existent node of the wrong kind
+  # THIS WILL NOT BE DETECTED
+  # since dump mechanism and file don't care about the types of deleted nodes
   set_changed_path_list(fsfs_file(sbox.repo_dir, 'revs', '8'),
-                        "_0.0.t7-2 dev-file false false /B3\n\n")
+                        "_0.0.t7-2 delete-file false false /B3\n\n")
 
   # copy from non-existent node
   set_changed_path_list(fsfs_file(sbox.repo_dir, 'revs', '10'),
@@ -1995,7 +1995,7 @@ def verify_invalid_path_changes(sbox):
                                            ".*Verified revision 5.",
                                            ".*Error verifying revision 6.",
                                            ".*Verified revision 7.",
-                                           ".*Error verifying revision 8.",
+                                           ".*Verified revision 8.",
                                            ".*Verified revision 9.",
                                            ".*Error verifying revision 10.",
                                            ".*Verified revision 11.",
@@ -2037,6 +2037,58 @@ def verify_invalid_path_changes(sbox):
                                    None, errput, None, "svnadmin: E165011:.*"):
     raise svntest.Failure
 
+
+def verify_denormalized_names(sbox):
+  "detect denormalized names and name collisions"
+
+  sbox.build(create_wc = False)
+  svntest.main.safe_rmtree(sbox.repo_dir, True)
+  svntest.main.create_repos(sbox.repo_dir)
+
+  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
+                                   'svnadmin_tests_data',
+                                   'normalization_check.dump')
+  load_dumpstream(sbox, open(dumpfile_location).read())
+
+  exit_code, output, errput = svntest.main.run_svnadmin(
+    "verify", "--check-ucs-normalization", sbox.repo_dir)
+
+  expected_output_regex_list = [
+    ".*Verified revision 0.",
+                                                # A/{Eacute}
+    "WARNING 0x0003: Denormalized directory name 'A/.*'",
+                                           # A/{icircumflex}{odiaeresis}ta
+    "WARNING 0x0003: Denormalized file name 'A/.*ta'",
+    ".*Verified revision 1.",
+    ".*Verified revision 2.",
+    ".*Verified revision 3.",
+                                           # A/{Eacute}/{aring}lpha
+    "WARNING 0x0003: Denormalized file name 'A/.*/.*lpha'",
+    "WARNING 0x0004: Duplicate representation of path 'A/.*/.*lpha'",
+    ".*Verified revision 4.",
+    ".*Verified revision 5.",
+                                       # Q/{aring}lpha
+    "WARNING 0x0005: Denormalized path '/Q/.*lpha'"
+                                  # A/{Eacute}
+    " in svn:mergeinfo property of 'A/.*'",
+                                                      # Q/{aring}lpha
+    "WARNING 0x0006: Duplicate representation of path '/Q/.*lpha'"
+                                  # A/{Eacute}
+    " in svn:mergeinfo property of 'A/.*'",
+    ".*Verified revision 6."]
+
+  # The BDB backend doesn't do global metadata verification.
+  if not svntest.main.is_fs_type_bdb():
+    expected_output_regex_list.insert(0, ".*Verifying repository metadata")
+
+  exp_out = svntest.verify.RegexListOutput(expected_output_regex_list)
+
+  if svntest.verify.verify_outputs(
+      "Unexpected error while running 'svnadmin verify'.",
+      output, errput, exp_out, None):
+    raise svntest.Failure
+
+
 ########################################################################
 # Run the tests
 
@@ -2075,6 +2127,7 @@ test_list = [ None,
               recover_old,
               verify_keep_going,
               verify_invalid_path_changes,
+              verify_denormalized_names,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/fsfs-improvements/subversion/tests/cmdline/svntest/main.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/cmdline/svntest/main.py?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/cmdline/svntest/main.py (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/cmdline/svntest/main.py Wed Nov 27 07:53:29 2013
@@ -2145,7 +2145,11 @@ def execute_tests(test_list, serial_only
   # Remove all scratchwork: the 'pristine' repository, greek tree, etc.
   # This ensures that an 'import' will happen the next time we run.
   if not options.is_child_process and not options.keep_local_tmp:
-    safe_rmtree(temp_dir, 1)
+    try:
+      safe_rmtree(temp_dir, 1)
+    except:
+      logger.error("ERROR: cleanup of '%s' directory failed." % temp_dir)
+      exit_code = 1
 
   # Cleanup after ourselves.
   svntest.sandbox.cleanup_deferred_test_paths()

Modified: subversion/branches/fsfs-improvements/subversion/tests/cmdline/switch_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/cmdline/switch_tests.py?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/cmdline/switch_tests.py (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/cmdline/switch_tests.py Wed Nov 27 07:53:29 2013
@@ -1041,7 +1041,7 @@ def switch_change_repos_root(sbox):
 
   # Test 1: A switch that changes to a non-existing repo shouldn't work.
   expected_err = ".*Unable to open repository.*|.*Could not open.*|"\
-                 ".*No repository found.*"
+                 ".*Could not find.*|.*No repository found.*"
   svntest.actions.run_and_verify_svn(None, None,
                                      expected_err,
                                      'switch', '--ignore-ancestry',

Modified: subversion/branches/fsfs-improvements/subversion/tests/libsvn_client/client-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/libsvn_client/client-test.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/libsvn_client/client-test.c (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/libsvn_client/client-test.c Wed Nov 27 07:53:29 2013
@@ -735,7 +735,7 @@ test_foreign_repos_copy(const svn_test_o
   SVN_ERR(create_greek_repos(&repos_url, "foreign-copy1", opts, pool));
   SVN_ERR(create_greek_repos(&repos2_url, "foreign-copy2", opts, pool));
 
-  wc_path = svn_test_data_path("test-wc-add", pool);
+  wc_path = svn_test_data_path("test-foreign-repos-copy", pool);
 
   wc_path = svn_dirent_join(wc_path, "foreign-wc", pool);
 
@@ -771,6 +771,9 @@ test_foreign_repos_copy(const svn_test_o
 
 /* ========================================================================== */
 
+
+int svn_test_max_threads = 3;
+
 struct svn_test_descriptor_t test_funcs[] =
   {
     SVN_TEST_NULL,
@@ -778,13 +781,13 @@ struct svn_test_descriptor_t test_funcs[
                    "test svn_client__elide_mergeinfo_catalog"),
     SVN_TEST_PASS2(test_args_to_target_array,
                    "test svn_client_args_to_target_array"),
-    SVN_TEST_OPTS_PASS(test_patch, "test svn_client_patch"),
     SVN_TEST_OPTS_PASS(test_wc_add_scenarios, "test svn_wc_add3 scenarios"),
+    SVN_TEST_OPTS_PASS(test_foreign_repos_copy, "test foreign repository copy"),
+    SVN_TEST_OPTS_PASS(test_patch, "test svn_client_patch"),
     SVN_TEST_OPTS_PASS(test_copy_crash, "test a crash in svn_client_copy5"),
 #ifdef TEST16K_ADD
     SVN_TEST_OPTS_PASS(test_16k_add, "test adding 16k files"),
 #endif
     SVN_TEST_OPTS_PASS(test_youngest_common_ancestor, "test youngest_common_ancestor"),
-    SVN_TEST_OPTS_PASS(test_foreign_repos_copy, "test foreign repository copy"),
     SVN_TEST_NULL
   };

Modified: subversion/branches/fsfs-improvements/subversion/tests/libsvn_delta/random-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/libsvn_delta/random-test.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/libsvn_delta/random-test.c (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/libsvn_delta/random-test.c Wed Nov 27 07:53:29 2013
@@ -512,6 +512,8 @@ random_combine_test(apr_pool_t *pool)
 
 /* The test table.  */
 
+int svn_test_max_threads = 1;
+
 struct svn_test_descriptor_t test_funcs[] =
   {
     SVN_TEST_NULL,

Modified: subversion/branches/fsfs-improvements/subversion/tests/libsvn_delta/window-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/libsvn_delta/window-test.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/libsvn_delta/window-test.c (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/libsvn_delta/window-test.c Wed Nov 27 07:53:29 2013
@@ -100,6 +100,8 @@ stream_window_test(apr_pool_t *pool)
 
 /* The test table.  */
 
+int svn_test_max_threads = 1;
+
 struct svn_test_descriptor_t test_funcs[] =
   {
     SVN_TEST_NULL,

Modified: subversion/branches/fsfs-improvements/subversion/tests/libsvn_diff/diff-diff3-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/libsvn_diff/diff-diff3-test.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/libsvn_diff/diff-diff3-test.c (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/libsvn_diff/diff-diff3-test.c Wed Nov 27 07:53:29 2013
@@ -2950,6 +2950,9 @@ two_way_issue_3362_v2(apr_pool_t *pool)
 
 /* ========================================================================== */
 
+
+int svn_test_max_threads = 4;
+
 struct svn_test_descriptor_t test_funcs[] =
   {
     SVN_TEST_NULL,

Modified: subversion/branches/fsfs-improvements/subversion/tests/libsvn_diff/parse-diff-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/libsvn_diff/parse-diff-test.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/libsvn_diff/parse-diff-test.c (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/libsvn_diff/parse-diff-test.c Wed Nov 27 07:53:29 2013
@@ -961,6 +961,9 @@ test_parse_unidiff_lacking_trailing_eol(
 
 /* ========================================================================== */
 
+
+int svn_test_max_threads = 1;
+
 struct svn_test_descriptor_t test_funcs[] =
   {
     SVN_TEST_NULL,

Modified: subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs/fs-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs/fs-test.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs/fs-test.c (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs/fs-test.c Wed Nov 27 07:53:29 2013
@@ -1020,7 +1020,7 @@ static svn_error_t *
 check_entry_present(svn_fs_root_t *root, const char *path,
                     const char *name, apr_pool_t *pool)
 {
-  svn_boolean_t present;
+  svn_boolean_t present = FALSE;
   SVN_ERR(check_entry(root, path, name, &present, pool));
 
   if (! present)
@@ -1037,7 +1037,7 @@ static svn_error_t *
 check_entry_absent(svn_fs_root_t *root, const char *path,
                    const char *name, apr_pool_t *pool)
 {
-  svn_boolean_t present;
+  svn_boolean_t present = TRUE;
   SVN_ERR(check_entry(root, path, name, &present, pool));
 
   if (present)
@@ -5078,6 +5078,8 @@ test_fs_info_format(const svn_test_opts_
 
 /* The test table.  */
 
+int svn_test_max_threads = 8;
+
 struct svn_test_descriptor_t test_funcs[] =
   {
     SVN_TEST_NULL,
@@ -5093,6 +5095,12 @@ struct svn_test_descriptor_t test_funcs[
                        "check that transaction names are not reused"),
     SVN_TEST_OPTS_PASS(write_and_read_file,
                        "write and read a file's contents"),
+    SVN_TEST_OPTS_PASS(almostmedium_file_integrity,
+                       "create and modify almostmedium file"),
+    SVN_TEST_OPTS_PASS(medium_file_integrity,
+                       "create and modify medium file"),
+    SVN_TEST_OPTS_PASS(large_file_integrity,
+                       "create and modify large file"),
     SVN_TEST_OPTS_PASS(create_mini_tree_transaction,
                        "test basic file and subdirectory creation"),
     SVN_TEST_OPTS_PASS(create_greek_tree_transaction,
@@ -5124,12 +5132,6 @@ struct svn_test_descriptor_t test_funcs[
                        "check old revisions"),
     SVN_TEST_OPTS_PASS(check_all_revisions,
                        "after each commit, check all revisions"),
-    SVN_TEST_OPTS_PASS(almostmedium_file_integrity,
-                       "create and modify almostmedium file"),
-    SVN_TEST_OPTS_PASS(medium_file_integrity,
-                       "create and modify medium file"),
-    SVN_TEST_OPTS_PASS(large_file_integrity,
-                       "create and modify large file"),
     SVN_TEST_OPTS_PASS(check_root_revision,
                        "ensure accurate storage of root node"),
     SVN_TEST_OPTS_PASS(test_node_created_rev,

Modified: subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs/locks-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs/locks-test.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs/locks-test.c (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs/locks-test.c Wed Nov 27 07:53:29 2013
@@ -608,9 +608,9 @@ lock_expiration(const svn_test_opts_t *o
   SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
   SVN_ERR(svn_fs_set_access(fs, access));
 
-  /* Lock /A/D/G/rho, with an expiration 3 seconds from now. */
+  /* Lock /A/D/G/rho, with an expiration 2 seconds from now. */
   SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0,
-                      apr_time_now() + apr_time_from_sec(3),
+                      apr_time_now() + apr_time_from_sec(2),
                       SVN_INVALID_REVNUM, FALSE, pool));
 
   /* Become nobody. */
@@ -640,9 +640,9 @@ lock_expiration(const svn_test_opts_t *o
                                        num_expected_paths, pool));
   }
 
-  /* Sleep 5 seconds, so the lock auto-expires.  Anonymous commit
+  /* Sleep 2 seconds, so the lock auto-expires.  Anonymous commit
      should then succeed. */
-  apr_sleep(apr_time_from_sec(5));
+  apr_sleep(apr_time_from_sec(3));
 
   /* Verify that the lock auto-expired even in the recursive case. */
   {
@@ -792,9 +792,13 @@ lock_out_of_date(const svn_test_opts_t *
 
 /* The test table.  */
 
+int svn_test_max_threads = 2;
+
 struct svn_test_descriptor_t test_funcs[] =
   {
     SVN_TEST_NULL,
+    SVN_TEST_OPTS_PASS(lock_expiration,
+                       "test that locks can expire"),
     SVN_TEST_OPTS_PASS(lock_only,
                        "lock only"),
     SVN_TEST_OPTS_PASS(lookup_lock_by_path,
@@ -811,8 +815,6 @@ struct svn_test_descriptor_t test_funcs[
                        "test that locking is enforced in final commit step"),
     SVN_TEST_OPTS_PASS(lock_dir_propchange,
                        "dir propchange can be committed with locked child"),
-    SVN_TEST_OPTS_PASS(lock_expiration,
-                       "test that locks can expire"),
     SVN_TEST_OPTS_PASS(lock_break_steal_refresh,
                        "breaking, stealing, refreshing a lock"),
     SVN_TEST_OPTS_PASS(lock_out_of_date,

Modified: subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs_base/changes-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs_base/changes-test.c?rev=1545955&r1=1545954&r2=1545955&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs_base/changes-test.c (original)
+++ subversion/branches/fsfs-improvements/subversion/tests/libsvn_fs_base/changes-test.c Wed Nov 27 07:53:29 2013
@@ -193,7 +193,7 @@ changes_fetch_raw(const svn_test_opts_t 
   struct changes_args args;
 
   /* Create a new fs and repos */
-  SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-changes-fetch", opts,
+  SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-changes-fetch-raw", opts,
                                   pool));
 
   /* First, verify that we can request changes for an arbitrary key
@@ -903,6 +903,8 @@ changes_bad_sequences(const svn_test_opt
 
 /* The test table.  */
 
+int svn_test_max_threads = 4;
+
 struct svn_test_descriptor_t test_funcs[] =
   {
     SVN_TEST_NULL,