You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/11/30 11:24:23 UTC

svn commit: r1717223 [29/50] - in /subversion/branches/ra-git: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ contrib/hook-scripts/ notes/ notes/api-errata/1.9/ notes/move-tracking/ subversion/ subversion/bindings/ctypes-python/...

Modified: subversion/branches/ra-git/subversion/libsvn_ra_svn/marshal.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_svn/marshal.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_svn/marshal.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_svn/marshal.c Mon Nov 30 10:24:16 2015
@@ -60,9 +60,10 @@
 
 /* We don't use "words" longer than this in our protocol.  The longest word
  * we are currently using is only about 16 chars long but we leave room for
- * longer future capability and command names.
+ * longer future capability and command names.  See read_item() to understand
+ * why MAX_WORD_LENGTH - 1 should be a multiple of 8.
  */
-#define MAX_WORD_LENGTH 31
+#define MAX_WORD_LENGTH 25
 
 /* The generic parsers will use the following value to limit the recursion
  * depth to some reasonable value.  The current protocol implementation
@@ -71,6 +72,10 @@
  */
 #define ITEM_NESTING_LIMIT 64
 
+/* The protocol words for booleans. */
+static const svn_string_t str_true = SVN__STATIC_STRING("true");
+static const svn_string_t str_false = SVN__STATIC_STRING("false");
+
 /* Return the APR socket timeout to be used for the connection depending
  * on whether there is a blockage handler or zero copy has been activated. */
 static apr_interval_time_t
@@ -79,14 +84,109 @@ get_timeout(svn_ra_svn_conn_t *conn)
   return conn->block_handler ? 0 : -1;
 }
 
+/* --- Public / private API data conversion --- */
+
+void
+svn_ra_svn__to_public_item(svn_ra_svn_item_t *target,
+                           const svn_ra_svn__item_t *source,
+                           apr_pool_t *result_pool)
+{
+  target->kind = source->kind;
+  switch (source->kind)
+    {
+      case SVN_RA_SVN_STRING:
+        target->u.string = svn_string_dup(&source->u.string, result_pool);
+        break;
+      case SVN_RA_SVN_NUMBER:
+        target->u.number = source->u.number;
+        break;
+      case SVN_RA_SVN_WORD:
+        target->u.word = source->u.word.data;
+        break;
+      case SVN_RA_SVN_LIST:
+        target->u.list = svn_ra_svn__to_public_array(&source->u.list,
+                                                     result_pool);
+        break;
+    }
+}
+
+apr_array_header_t *
+svn_ra_svn__to_public_array(const svn_ra_svn__list_t *source,
+                            apr_pool_t *result_pool)
+{
+  apr_array_header_t *result = apr_array_make(result_pool, source->nelts,
+                                              sizeof(svn_ra_svn_item_t));
+
+  int i;
+  for (i = 0; i < source->nelts; ++i)
+    {
+      svn_ra_svn_item_t *sub_target = apr_array_push(result);
+      svn_ra_svn__item_t *sub_source = &SVN_RA_SVN__LIST_ITEM(source, i);
+
+      svn_ra_svn__to_public_item(sub_target, sub_source, result_pool);
+    }
+
+  return result;
+}
+
+void
+svn_ra_svn__to_private_item(svn_ra_svn__item_t *target,
+                            const svn_ra_svn_item_t *source,
+                            apr_pool_t *result_pool)
+{
+  target->kind = source->kind;
+  switch (source->kind)
+    {
+      case SVN_RA_SVN_STRING:
+        target->u.string = *source->u.string;
+        break;
+      case SVN_RA_SVN_NUMBER:
+        target->u.number = source->u.number;
+        break;
+      case SVN_RA_SVN_WORD:
+        target->u.word.data = source->u.word;
+        target->u.word.len = strlen(source->u.word);
+        break;
+      case SVN_RA_SVN_LIST:
+        target->u.list = *svn_ra_svn__to_private_array(source->u.list,
+                                                       result_pool);
+        break;
+    }
+}
+
+svn_ra_svn__list_t *
+svn_ra_svn__to_private_array(const apr_array_header_t *source,
+                             apr_pool_t *result_pool)
+{
+  int i;
+
+  svn_ra_svn__list_t *result = apr_pcalloc(result_pool, sizeof(*result));
+  result->nelts = source->nelts;
+  result->items = apr_palloc(result_pool,
+                             source->nelts * sizeof(*result->items));
+
+  for (i = 0; i < source->nelts; ++i)
+    {
+      svn_ra_svn__item_t *sub_target = &result->items[i];
+      svn_ra_svn_item_t *sub_source = &APR_ARRAY_IDX(source, i,
+                                                     svn_ra_svn_item_t);
+
+      svn_ra_svn__to_private_item(sub_target, sub_source, result_pool);
+    }
+
+  return result;
+}
+
 /* --- CONNECTION INITIALIZATION --- */
 
-svn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock,
+svn_ra_svn_conn_t *svn_ra_svn_create_conn5(apr_socket_t *sock,
                                            svn_stream_t *in_stream,
                                            svn_stream_t *out_stream,
                                            int compression_level,
                                            apr_size_t zero_copy_limit,
                                            apr_size_t error_check_interval,
+                                           apr_uint64_t max_in,
+                                           apr_uint64_t max_out,
                                            apr_pool_t *result_pool)
 {
   svn_ra_svn_conn_t *conn;
@@ -106,6 +206,10 @@ svn_ra_svn_conn_t *svn_ra_svn_create_con
   conn->written_since_error_check = 0;
   conn->error_check_interval = error_check_interval;
   conn->may_check_for_error = error_check_interval == 0;
+  conn->max_in = max_in;
+  conn->current_in = 0;
+  conn->max_out = max_out;
+  conn->current_out = 0;
   conn->block_handler = NULL;
   conn->block_baton = NULL;
   conn->capabilities = apr_hash_make(result_pool);
@@ -132,21 +236,31 @@ svn_ra_svn_conn_t *svn_ra_svn_create_con
   return conn;
 }
 
-svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
-                                         const apr_array_header_t *list)
+svn_error_t *
+svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
+                            const apr_array_header_t *list)
+{
+  svn_ra_svn__list_t *internal
+    = svn_ra_svn__to_private_array(list, list->pool);
+  return svn_error_trace(svn_ra_svn__set_capabilities(conn, internal));
+}
+
+svn_error_t *
+svn_ra_svn__set_capabilities(svn_ra_svn_conn_t *conn,
+                             const svn_ra_svn__list_t *list)
 {
   int i;
-  svn_ra_svn_item_t *item;
+  svn_ra_svn__item_t *item;
   const char *word;
 
   for (i = 0; i < list->nelts; i++)
     {
-      item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
+      item = &SVN_RA_SVN__LIST_ITEM(list, i);
       if (item->kind != SVN_RA_SVN_WORD)
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 _("Capability entry is not a word"));
-      word = apr_pstrdup(conn->pool, item->u.word);
-      svn_hash_sets(conn->capabilities, word, word);
+      word = apr_pstrmemdup(conn->pool, item->u.word.data, item->u.word.len);
+      apr_hash_set(conn->capabilities, word, item->u.word.len, word);
     }
   return SVN_NO_ERROR;
 }
@@ -204,8 +318,33 @@ svn_error_t *svn_ra_svn__data_available(
   return svn_ra_svn__stream_data_available(conn->stream, data_available);
 }
 
+void
+svn_ra_svn__reset_command_io_counters(svn_ra_svn_conn_t *conn)
+{
+  conn->current_in = 0;
+  conn->current_out = 0;
+}
+
+
 /* --- WRITE BUFFER MANAGEMENT --- */
 
+/* Return an error object if CONN exceeded its send or receive limits. */
+static svn_error_t *
+check_io_limits(svn_ra_svn_conn_t *conn)
+{
+  if (conn->max_in && (conn->current_in > conn->max_in))
+    return svn_error_create(SVN_ERR_RA_SVN_REQUEST_SIZE, NULL,
+                            "The client request size exceeds the "
+                            "configured limit");
+
+  if (conn->max_out && (conn->current_out > conn->max_out))
+    return svn_error_create(SVN_ERR_RA_SVN_RESPONSE_SIZE, NULL,
+                            "The server response size exceeds the "
+                            "configured limit");
+
+  return SVN_NO_ERROR;
+}
+
 /* Write data to socket or output file as appropriate. */
 static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
                                     const char *data, apr_size_t len)
@@ -215,6 +354,12 @@ static svn_error_t *writebuf_output(svn_
   apr_pool_t *subpool = NULL;
   svn_ra_svn__session_baton_t *session = conn->session;
 
+  /* Limit the size of the response, if a limit has been configured.
+   * This is to limit the server load in case users e.g. accidentally ran
+   * an export on the root folder. */
+  conn->current_out += len;
+  SVN_ERR(check_io_limits(conn));
+
   while (data < end)
     {
       count = end - data;
@@ -333,12 +478,19 @@ static svn_error_t *readbuf_input(svn_ra
 {
   svn_ra_svn__session_baton_t *session = conn->session;
 
+  /* First, give the user a chance to cancel the request before we do. */
   if (session && session->callbacks && session->callbacks->cancel_func)
     SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
 
+  /* Limit our memory usage, if a limit has been configured.  Note that
+   * we first read the whole request into memory before process it. */
+  SVN_ERR(check_io_limits(conn));
+
+  /* Actually fill the buffer. */
   SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
   if (*len == 0)
     return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
+  conn->current_in += *len;
 
   if (session)
     {
@@ -384,9 +536,13 @@ static svn_error_t *readbuf_fill(svn_ra_
   apr_size_t len;
 
   SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
+
+  /* Make sure we tell the other side everything we have to say before
+   * reading / waiting for an answer. */
   if (conn->write_pos)
     SVN_ERR(writebuf_flush(conn, pool));
 
+  /* Fill (some of the) buffer. */
   len = sizeof(conn->read_buf);
   SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
   conn->read_ptr = conn->read_buf;
@@ -512,22 +668,65 @@ svn_ra_svn__write_number(svn_ra_svn_conn
   return write_number(conn, pool, number, ' ');
 }
 
+/* Write string S of length LEN to TARGET and return the first position
+   after the written data.
+
+   NOTE: This function assumes that TARGET has enough room for S, the LEN
+         prefix and the required separators.  The available buffer size
+         should be SVN_INT64_BUFFER_SIZE + LEN + 1 to avoid any chance of
+         overflow.
+ */
+static char *
+write_ncstring_quick(char *target,
+                     const char *s,
+                     apr_size_t len)
+{
+  /* Write string length. */
+  if (len < 10)
+    {
+      *target = (char)(len + '0');
+      target++;
+    }
+  else
+    {
+      target += svn__ui64toa(target, len);
+    }
+
+  /* Separator & contents. */
+  target[0] = ':';
+  memcpy(target + 1, s, len);
+  target[len + 1] = ' ';
+
+  /* First location after the string. */
+  return target + len + 2;
+}
+
+
 static svn_error_t *
 svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn,
                            apr_pool_t *pool,
                            const char *s,
                            apr_size_t len)
 {
-  if (len < 10)
-    {
-      SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0')));
-      SVN_ERR(writebuf_writechar(conn, pool, ':'));
+  apr_size_t needed = SVN_INT64_BUFFER_SIZE + len + 1;
+
+  /* In most cases, there is enough left room in the WRITE_BUF
+     the we can serialize directly into it. */
+  if (conn->write_pos + needed < sizeof(conn->write_buf))
+    {
+      /* Quick path. */
+      conn->write_pos = write_ncstring_quick(conn->write_buf
+                                               + conn->write_pos, s, len)
+                      - conn->write_buf;
     }
   else
-    SVN_ERR(write_number(conn, pool, len, ':'));
+    {
+      /* Slower fallback code. */
+      SVN_ERR(write_number(conn, pool, len, ':'));
 
-  SVN_ERR(writebuf_write(conn, pool, s, len));
-  SVN_ERR(writebuf_writechar(conn, pool, ' '));
+      SVN_ERR(writebuf_write(conn, pool, s, len));
+      SVN_ERR(writebuf_writechar(conn, pool, ' '));
+    }
 
   return SVN_NO_ERROR;
 }
@@ -755,6 +954,52 @@ write_tuple_string_opt(svn_ra_svn_conn_t
   return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
 }
 
+/* Optimized sending code for the "(s?)" pattern. */
+static svn_error_t *
+write_tuple_string_opt_list(svn_ra_svn_conn_t *conn,
+                            apr_pool_t *pool,
+                            const svn_string_t *str)
+{
+  apr_size_t needed;
+
+  /* Special case. */
+  if (!str)
+    return writebuf_write(conn, pool, "( ) ", 4);
+
+  /* If there is at least this much the room left in the WRITE_BUF,
+     we can serialize directly into it. */
+  needed = 2                       /* open list */
+         + SVN_INT64_BUFFER_SIZE   /* string length + separator */
+         + str->len                /* string contents */
+         + 2;                      /* close list */
+
+  if (conn->write_pos + needed <= sizeof(conn->write_buf))
+    {
+      /* Quick path. */
+      /* Open list. */
+      char *p = conn->write_buf + conn->write_pos;
+      p[0] = '(';
+      p[1] = ' ';
+
+      /* Write string. */
+      p = write_ncstring_quick(p + 2, str->data, str->len);
+
+      /* Close list. */
+      p[0] = ')';
+      p[1] = ' ';
+      conn->write_pos = p + 2 - conn->write_buf;
+    }
+  else
+    {
+      /* Standard code path (fallback). */
+      SVN_ERR(svn_ra_svn__start_list(conn, pool));
+      SVN_ERR(svn_ra_svn__write_string(conn, pool, str));
+      SVN_ERR(svn_ra_svn__end_list(conn, pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 write_tuple_start_list(svn_ra_svn_conn_t *conn,
                        apr_pool_t *pool)
@@ -809,14 +1054,14 @@ static svn_error_t *
 write_cmd_add_node(svn_ra_svn_conn_t *conn,
                    apr_pool_t *pool,
                    const char *path,
-                   const char *parent_token,
-                   const char *token,
+                   const svn_string_t *parent_token,
+                   const svn_string_t *token,
                    const char *copy_path,
                    svn_revnum_t copy_rev)
 {
   SVN_ERR(write_tuple_cstring(conn, pool, path));
-  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, parent_token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
   SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
@@ -829,13 +1074,13 @@ static svn_error_t *
 write_cmd_open_node(svn_ra_svn_conn_t *conn,
                     apr_pool_t *pool,
                     const char *path,
-                    const char *parent_token,
-                    const char *token,
+                    const svn_string_t *parent_token,
+                    const svn_string_t *token,
                     svn_revnum_t rev)
 {
   SVN_ERR(write_tuple_cstring(conn, pool, path));
-  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, parent_token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
   SVN_ERR(write_tuple_end_list(conn, pool));
@@ -846,15 +1091,13 @@ write_cmd_open_node(svn_ra_svn_conn_t *c
 static svn_error_t *
 write_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
                            apr_pool_t *pool,
-                           const char *token,
+                           const svn_string_t *token,
                            const char *name,
                            const svn_string_t *value)
 {
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_cstring(conn, pool, name));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_string_opt(conn, pool, value));
-  SVN_ERR(write_tuple_end_list(conn, pool));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
 
   return SVN_NO_ERROR;
 }
@@ -863,10 +1106,10 @@ static svn_error_t *
 write_cmd_absent_node(svn_ra_svn_conn_t *conn,
                       apr_pool_t *pool,
                       const char *path,
-                      const char *token)
+                      const svn_string_t *token)
 {
   SVN_ERR(write_tuple_cstring(conn, pool, path));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
 
   return SVN_NO_ERROR;
 }
@@ -939,14 +1182,14 @@ svn_ra_svn__write_tuple(svn_ra_svn_conn_
  * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
  * data is allocated in POOL. */
 static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
-                                svn_ra_svn_item_t *item, apr_uint64_t len64)
+                                svn_ra_svn__item_t *item, apr_uint64_t len64)
 {
   apr_size_t len = (apr_size_t)len64;
   apr_size_t readbuf_len;
   char *dest;
 
   /* We can't store strings longer than the maximum size of apr_size_t,
-   * so check for wrapping */
+   * so check before using the truncated value. */
   if (len64 > APR_SIZE_MAX)
     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                             _("String length larger than maximum"));
@@ -955,11 +1198,22 @@ static svn_error_t *read_string(svn_ra_s
   if (conn->read_ptr + len <= conn->read_end)
     {
       item->kind = SVN_RA_SVN_STRING;
-      item->u.string = svn_string_ncreate(conn->read_ptr, len, pool);
+      item->u.string.data = apr_pstrmemdup(pool, conn->read_ptr, len);
+      item->u.string.len = len;
       conn->read_ptr += len;
     }
   else
     {
+      svn_stringbuf_t *stringbuf;
+
+      /* Don't even attempt to read anything that exceeds the I/O limit.
+       * So, we can terminate the transfer at an early point, saving
+       * everybody's time and resources. */
+      if (conn->max_in && (conn->max_in < len64))
+        return svn_error_create(SVN_ERR_RA_SVN_REQUEST_SIZE, NULL,
+                                "The client request size exceeds the "
+                                "configured limit");
+
       /* Read the string in chunks.  The chunk size is large enough to avoid
        * re-allocation in typical cases, and small enough to ensure we do
        * not pre-allocate an unreasonable amount of memory if (perhaps due
@@ -968,7 +1222,7 @@ static svn_error_t *read_string(svn_ra_s
        * start small and wait for all that data to actually show up.  This
        * does not fully prevent DOS attacks but makes them harder (you have
        * to actually send gigabytes of data). */
-      svn_stringbuf_t *stringbuf = svn_stringbuf_create_empty(pool);
+      stringbuf = svn_stringbuf_create_empty(pool);
 
       /* Read string data directly into the string structure.
        * Do it iteratively.  */
@@ -996,7 +1250,8 @@ static svn_error_t *read_string(svn_ra_s
 
       /* Return the string properly wrapped into an RA_SVN item. */
       item->kind = SVN_RA_SVN_STRING;
-      item->u.string = svn_stringbuf__morph_into_string(stringbuf);
+      item->u.string.data = stringbuf->data;
+      item->u.string.len = stringbuf->len;
     }
 
   return SVN_NO_ERROR;
@@ -1007,12 +1262,12 @@ static svn_error_t *read_string(svn_ra_s
  * to 0 for the first call and is used to enforce a recursion limit
  * on the parser. */
 static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
-                              svn_ra_svn_item_t *item, char first_char,
+                              svn_ra_svn__item_t *item, char first_char,
                               int level)
 {
   char c = first_char;
   apr_uint64_t val;
-  svn_ra_svn_item_t *listitem;
+  svn_ra_svn__item_t *listitem;
 
   if (++level >= ITEM_NESTING_LIMIT)
     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
@@ -1060,67 +1315,108 @@ static svn_error_t *read_item(svn_ra_svn
       char *p = buffer + 1;
 
       buffer[0] = c;
-      while (1)
+      if (conn->read_ptr + MAX_WORD_LENGTH <= conn->read_end)
         {
-          SVN_ERR(readbuf_getchar(conn, pool, p));
-          if (!svn_ctype_isalnum(*p) && *p != '-')
-            break;
+          /* Fast path: we can simply take a chunk from the read
+           * buffer and inspect it with no overflow checks etc.
+           *
+           * Copying these 24 bytes unconditionally is also faster
+           * than a variable-sized memcpy.  Note that P is at BUFFER[1].
+           */
+          memcpy(p, conn->read_ptr, MAX_WORD_LENGTH - 1);
+          *end = 0;
+
+          /* This will terminate at P == END because of *END == NUL. */
+          while (svn_ctype_isalnum(*p) || *p == '-')
+            ++p;
 
-          if (++p == end)
-            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
-                                    _("Word is too long"));
+          /* Only now do we mark data as actually read. */
+          conn->read_ptr += p - buffer;
         }
+      else
+        {
+          /* Slow path. Byte-by-byte copying and checking for
+           * input and output buffer boundaries. */
+          for (p = buffer + 1; p != end; ++p)
+            {
+              SVN_ERR(readbuf_getchar(conn, pool, p));
+              if (!svn_ctype_isalnum(*p) && *p != '-')
+                break;
+            }
+        }
+
+      if (p == end)
+        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+                                _("Word is too long"));
 
       c = *p;
       *p = '\0';
 
+      /* Store the word in ITEM. */
       item->kind = SVN_RA_SVN_WORD;
-      item->u.word = buffer;
+      item->u.word.data = buffer;
+      item->u.word.len = p - buffer;
     }
   else if (c == '(')
     {
-      /* Allocate an APR array with room for (initially) 4 items.
-       * We do this manually because lists are the most frequent protocol
-       * element, often used to frame a single, optional value.  We save
-       * about 20% of total protocol handling time. */
-      char *buffer = apr_palloc(pool, sizeof(apr_array_header_t)
-                                      + 4 * sizeof(svn_ra_svn_item_t));
-      svn_ra_svn_item_t *data
-        = (svn_ra_svn_item_t *)(buffer + sizeof(apr_array_header_t));
-
-      item->kind = SVN_RA_SVN_LIST;
-      item->u.list = (apr_array_header_t *)buffer;
-      item->u.list->elts = (char *)data;
-      item->u.list->pool = pool;
-      item->u.list->elt_size = sizeof(*data);
-      item->u.list->nelts = 0;
-      item->u.list->nalloc = 4;
-
-      listitem = data;
+      /* The largest struct that the protocol currently defines has 10
+       * elements (log-entry) and add some headroom for future extensions.
+       * At a maximum nesting level of 64 this use <= 18kB of stack.
+       *
+       * All system-defined data structures will fit into this and will be
+       * copied into ITEM after a single apr_palloc with no over-provision.
+       * Unbounded lists with more than 12 but less than 25 entries will
+       * also see only a single allocation from POOL.  However, there will
+       * be some over-provision.  Longer lists will see log N resizes and
+       * O(N) total cost.
+       */
+      svn_ra_svn__item_t stack_items[12];
+      svn_ra_svn__item_t *items = stack_items;
+      int capacity = sizeof(stack_items) / sizeof(stack_items[0]);
+      int count = 0;
 
       /* Read in the list items. */
+      item->kind = SVN_RA_SVN_LIST;
       while (1)
         {
           SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
           if (c == ')')
             break;
 
-          /* increase array capacity if necessary */
-          if (item->u.list->nelts == item->u.list->nalloc)
+          /* Auto-expand the list. */
+          if (count == capacity)
             {
-              data = apr_palloc(pool, 2 * item->u.list->nelts * sizeof(*data));
-              memcpy(data, item->u.list->elts, item->u.list->nelts * sizeof(*data));
-              item->u.list->elts = (char *)data;
-              item->u.list->nalloc *= 2;
-              listitem = data + item->u.list->nelts;
+              svn_ra_svn__item_t *new_items
+                = apr_palloc(pool, 2 * capacity * sizeof(*new_items));
+              memcpy(new_items, items, capacity * sizeof(*new_items));
+              items = new_items;
+              capacity = 2 * capacity;
             }
 
-          /* read next protocol item */
+          listitem = &items[count];
+          ++count;
+
           SVN_ERR(read_item(conn, pool, listitem, c, level));
+        }
+
+      /* Store the list in ITEM - if not empty (= default). */
+      if (count)
+        {
+          item->u.list.nelts = count;
 
-          listitem++;
-          item->u.list->nelts++;
+          /* If we haven't allocated from POOL, yet, do it now. */
+          if (items == stack_items)
+            item->u.list.items = apr_pmemdup(pool, items,
+                                             count * sizeof(*items));
+          else
+            item->u.list.items = items;
         }
+      else
+        {
+          item->u.list.items = NULL;
+          item->u.list.nelts = 0;
+        }
+
       SVN_ERR(readbuf_getchar(conn, pool, &c));
     }
 
@@ -1222,7 +1518,7 @@ read_command_only(svn_ra_svn_conn_t *con
 svn_error_t *
 svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
                       apr_pool_t *pool,
-                      svn_ra_svn_item_t **item)
+                      svn_ra_svn__item_t **item)
 {
   char c;
 
@@ -1272,36 +1568,38 @@ svn_ra_svn__skip_leading_garbage(svn_ra_
 
 /* --- READING AND PARSING TUPLES --- */
 
-/* Parse a tuple of svn_ra_svn_item_t *'s.  Advance *FMT to the end of the
+/* Parse a tuple of svn_ra_svn__item_t *'s.  Advance *FMT to the end of the
  * tuple specification and advance AP by the corresponding arguments. */
-static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *pool,
-                                 const char **fmt, va_list *ap)
+static svn_error_t *
+vparse_tuple(const svn_ra_svn__list_t *items,
+             const char **fmt,
+             va_list *ap)
 {
   int count, nesting_level;
-  svn_ra_svn_item_t *elt;
+  svn_ra_svn__item_t *elt;
 
   for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
     {
       /* '?' just means the tuple may stop; skip past it. */
       if (**fmt == '?')
         (*fmt)++;
-      elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t);
+      elt = &SVN_RA_SVN__LIST_ITEM(items, count);
       if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
         {
           (*fmt)++;
-          SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap));
+          SVN_ERR(vparse_tuple(&elt->u.list, fmt, ap));
         }
       else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
-        *va_arg(*ap, const char **) = elt->u.string->data;
+        *va_arg(*ap, const char **) = elt->u.string.data;
       else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
-        *va_arg(*ap, svn_string_t **) = elt->u.string;
+        *va_arg(*ap, svn_string_t **) = &elt->u.string;
       else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
-        *va_arg(*ap, const char **) = elt->u.word;
+        *va_arg(*ap, const char **) = elt->u.word.data;
       else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
         {
-          if (strcmp(elt->u.word, "true") == 0)
+          if (svn_string_compare(&elt->u.word, &str_true))
             *va_arg(*ap, svn_boolean_t *) = TRUE;
-          else if (strcmp(elt->u.word, "false") == 0)
+          else if (svn_string_compare(&elt->u.word, &str_false))
             *va_arg(*ap, svn_boolean_t *) = FALSE;
           else
             break;
@@ -1312,24 +1610,24 @@ static svn_error_t *vparse_tuple(const a
         *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
       else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
         {
-          if (strcmp(elt->u.word, "true") == 0)
+          if (svn_string_compare(&elt->u.word, &str_true))
             *va_arg(*ap, apr_uint64_t *) = TRUE;
-          else if (strcmp(elt->u.word, "false") == 0)
+          else if (svn_string_compare(&elt->u.word, &str_false))
             *va_arg(*ap, apr_uint64_t *) = FALSE;
           else
             break;
         }
       else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD)
         {
-          if (strcmp(elt->u.word, "true") == 0)
+          if (svn_string_compare(&elt->u.word, &str_true))
             *va_arg(*ap, svn_tristate_t *) = svn_tristate_true;
-          else if (strcmp(elt->u.word, "false") == 0)
+          else if (svn_string_compare(&elt->u.word, &str_false))
             *va_arg(*ap, svn_tristate_t *) = svn_tristate_false;
           else
             break;
         }
       else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
-        *va_arg(*ap, apr_array_header_t **) = elt->u.list;
+        *va_arg(*ap, svn_ra_svn__list_t **) = &elt->u.list;
       else if (**fmt == ')')
         return SVN_NO_ERROR;
       else
@@ -1355,7 +1653,7 @@ static svn_error_t *vparse_tuple(const a
               *va_arg(*ap, const char **) = NULL;
               break;
             case 'l':
-              *va_arg(*ap, apr_array_header_t **) = NULL;
+              *va_arg(*ap, svn_ra_svn__list_t **) = NULL;
               break;
             case 'B':
             case 'n':
@@ -1383,15 +1681,14 @@ static svn_error_t *vparse_tuple(const a
 }
 
 svn_error_t *
-svn_ra_svn__parse_tuple(const apr_array_header_t *list,
-                        apr_pool_t *pool,
+svn_ra_svn__parse_tuple(const svn_ra_svn__list_t *list,
                         const char *fmt, ...)
 {
   svn_error_t *err;
   va_list ap;
 
   va_start(ap, fmt);
-  err = vparse_tuple(list, pool, &fmt, &ap);
+  err = vparse_tuple(list, &fmt, &ap);
   va_end(ap);
   return err;
 }
@@ -1402,7 +1699,7 @@ svn_ra_svn__read_tuple(svn_ra_svn_conn_t
                        const char *fmt, ...)
 {
   va_list ap;
-  svn_ra_svn_item_t *item;
+  svn_ra_svn__item_t *item;
   svn_error_t *err;
 
   SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
@@ -1410,7 +1707,7 @@ svn_ra_svn__read_tuple(svn_ra_svn_conn_t
     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                             _("Malformed network data"));
   va_start(ap, fmt);
-  err = vparse_tuple(item->u.list, pool, &fmt, &ap);
+  err = vparse_tuple(&item->u.list, &fmt, &ap);
   va_end(ap);
   return err;
 }
@@ -1429,24 +1726,23 @@ svn_ra_svn__read_command_only(svn_ra_svn
 
 
 svn_error_t *
-svn_ra_svn__parse_proplist(const apr_array_header_t *list,
+svn_ra_svn__parse_proplist(const svn_ra_svn__list_t *list,
                            apr_pool_t *pool,
                            apr_hash_t **props)
 {
   svn_string_t *name;
   svn_string_t *value;
-  svn_ra_svn_item_t *elt;
+  svn_ra_svn__item_t *elt;
   int i;
 
   *props = svn_hash__make(pool);
   for (i = 0; i < list->nelts; i++)
     {
-      elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
+      elt = &SVN_RA_SVN__LIST_ITEM(list, i);
       if (elt->kind != SVN_RA_SVN_LIST)
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 _("Proplist element not a list"));
-      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss",
-                                      &name, &value));
+      SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "ss", &name, &value));
       apr_hash_set(*props, name->data, name->len, value);
     }
 
@@ -1471,15 +1767,14 @@ svn_error_t *svn_ra_svn__locate_real_err
   return this_link;
 }
 
-svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
-                                               apr_pool_t *pool)
+svn_error_t *
+svn_ra_svn__handle_failure_status(const svn_ra_svn__list_t *params)
 {
   const char *message, *file;
   svn_error_t *err = NULL;
-  svn_ra_svn_item_t *elt;
+  svn_ra_svn__item_t *elt;
   int i;
   apr_uint64_t apr_err, line;
-  apr_pool_t *subpool = svn_pool_create(pool);
 
   if (params->nelts == 0)
     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
@@ -1488,12 +1783,11 @@ svn_error_t *svn_ra_svn__handle_failure_
   /* Rebuild the error list from the end, to avoid reversing the order. */
   for (i = params->nelts - 1; i >= 0; i--)
     {
-      svn_pool_clear(subpool);
-      elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t);
+      elt = &SVN_RA_SVN__LIST_ITEM(params, i);
       if (elt->kind != SVN_RA_SVN_LIST)
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 _("Malformed error list"));
-      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, subpool, "nccn",
+      SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "nccn",
                                       &apr_err, &message, &file, &line));
       /* The message field should have been optional, but we can't
          easily change that, so "" means a nonexistent message. */
@@ -1512,8 +1806,6 @@ svn_error_t *svn_ra_svn__handle_failure_
         }
     }
 
-  svn_pool_destroy(subpool);
-
   /* If we get here, then we failed to find a real error in the error
      chain that the server proported to be sending us.  That's bad. */
   if (! err)
@@ -1530,20 +1822,20 @@ svn_ra_svn__read_cmd_response(svn_ra_svn
 {
   va_list ap;
   const char *status;
-  apr_array_header_t *params;
+  svn_ra_svn__list_t *params;
   svn_error_t *err;
 
   SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, &params));
   if (strcmp(status, "success") == 0)
     {
       va_start(ap, fmt);
-      err = vparse_tuple(params, pool, &fmt, &ap);
+      err = vparse_tuple(params, &fmt, &ap);
       va_end(ap);
       return err;
     }
   else if (strcmp(status, "failure") == 0)
     {
-      return svn_error_trace(svn_ra_svn__handle_failure_status(params, pool));
+      return svn_error_trace(svn_ra_svn__handle_failure_status(params));
     }
 
   return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
@@ -1557,7 +1849,12 @@ svn_ra_svn__has_command(svn_boolean_t *h
                         svn_ra_svn_conn_t *conn,
                         apr_pool_t *pool)
 {
-  svn_error_t *err = svn_ra_svn__has_item(has_command, conn, pool);
+  svn_error_t *err;
+
+  /* Don't make whitespace between commands trigger I/O limitiations. */
+  svn_ra_svn__reset_command_io_counters(conn);
+
+  err = svn_ra_svn__has_item(has_command, conn, pool);
   if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
     {
       *terminated = TRUE;
@@ -1579,10 +1876,14 @@ svn_ra_svn__handle_command(svn_boolean_t
 {
   const char *cmdname;
   svn_error_t *err, *write_err;
-  apr_array_header_t *params;
-  const svn_ra_svn_cmd_entry_t *command;
+  svn_ra_svn__list_t *params;
+  const svn_ra_svn__cmd_entry_t *command;
 
   *terminate = FALSE;
+
+  /* Limit I/O for every command separately. */
+  svn_ra_svn__reset_command_io_counters(conn);
+
   err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, &params);
   if (err)
     {
@@ -1599,7 +1900,28 @@ svn_ra_svn__handle_command(svn_boolean_t
   command = svn_hash_gets(cmd_hash, cmdname);
   if (command)
     {
-      err = (*command->handler)(conn, pool, params, baton);
+      /* Call the standard command handler.
+       * If that is not set, then this is a lecagy API call and we invoke
+       * the legacy command handler. */
+      if (command->handler)
+        {
+          err = (*command->handler)(conn, pool, params, baton);
+        }
+      else
+        {
+          apr_array_header_t *deprecated_params
+            = svn_ra_svn__to_public_array(params, pool);
+          err = (*command->deprecated_handler)(conn, pool, deprecated_params,
+                                               baton);
+        }
+
+      /* The command implementation may have swallowed or wrapped the I/O
+       * error not knowing that we may no longer be able to send data.
+       *
+       * So, check again for the limit violations and exit the command
+       * processing quickly if we may have truncated data. */
+      err = svn_error_compose_create(check_io_limits(conn), err);
+
       *terminate = command->terminate;
     }
   else
@@ -1624,13 +1946,13 @@ svn_ra_svn__handle_command(svn_boolean_t
 svn_error_t *
 svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
                              apr_pool_t *pool,
-                             const svn_ra_svn_cmd_entry_t *commands,
+                             const svn_ra_svn__cmd_entry_t *commands,
                              void *baton,
                              svn_boolean_t error_on_disconnect)
 {
   apr_pool_t *subpool = svn_pool_create(pool);
   apr_pool_t *iterpool = svn_pool_create(subpool);
-  const svn_ra_svn_cmd_entry_t *command;
+  const svn_ra_svn__cmd_entry_t *command;
   apr_hash_t *cmd_hash = apr_hash_make(subpool);
 
   for (command = commands; command->cmdname; command++)
@@ -1673,13 +1995,13 @@ svn_error_t *
 svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
                                 apr_pool_t *pool,
                                 svn_revnum_t rev,
-                                const char *token)
+                                const svn_string_t *token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( "));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
   SVN_ERR(write_tuple_end_list(conn, pool));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
   return SVN_NO_ERROR;
@@ -1690,14 +2012,14 @@ svn_ra_svn__write_cmd_delete_entry(svn_r
                                    apr_pool_t *pool,
                                    const char *path,
                                    svn_revnum_t rev,
-                                   const char *token)
+                                   const svn_string_t *token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( "));
   SVN_ERR(write_tuple_cstring(conn, pool, path));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
   SVN_ERR(write_tuple_end_list(conn, pool));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
   return SVN_NO_ERROR;
@@ -1707,8 +2029,8 @@ svn_error_t *
 svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
                               apr_pool_t *pool,
                               const char *path,
-                              const char *parent_token,
-                              const char *token,
+                              const svn_string_t *parent_token,
+                              const svn_string_t *token,
                               const char *copy_path,
                               svn_revnum_t copy_rev)
 {
@@ -1724,8 +2046,8 @@ svn_error_t *
 svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
                                apr_pool_t *pool,
                                const char *path,
-                               const char *parent_token,
-                               const char *token,
+                               const svn_string_t *parent_token,
+                               const svn_string_t *token,
                                svn_revnum_t rev)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( "));
@@ -1738,7 +2060,7 @@ svn_ra_svn__write_cmd_open_dir(svn_ra_sv
 svn_error_t *
 svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
                                       apr_pool_t *pool,
-                                      const char *token,
+                                      const svn_string_t *token,
                                       const char *name,
                                       const svn_string_t *value)
 {
@@ -1752,10 +2074,10 @@ svn_ra_svn__write_cmd_change_dir_prop(sv
 svn_error_t *
 svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
                                 apr_pool_t *pool,
-                                const char *token)
+                                const svn_string_t *token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( "));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
   return SVN_NO_ERROR;
@@ -1765,7 +2087,7 @@ svn_error_t *
 svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
                                  apr_pool_t *pool,
                                  const char *path,
-                                 const char *parent_token)
+                                 const svn_string_t *parent_token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( "));
   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
@@ -1778,8 +2100,8 @@ svn_error_t *
 svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
                                apr_pool_t *pool,
                                const char *path,
-                               const char *parent_token,
-                               const char *token,
+                               const svn_string_t *parent_token,
+                               const svn_string_t *token,
                                const char *copy_path,
                                svn_revnum_t copy_rev)
 {
@@ -1795,8 +2117,8 @@ svn_error_t *
 svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
                                 apr_pool_t *pool,
                                 const char *path,
-                                const char *parent_token,
-                                const char *token,
+                                const svn_string_t *parent_token,
+                                const svn_string_t *token,
                                 svn_revnum_t rev)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( "));
@@ -1809,7 +2131,7 @@ svn_ra_svn__write_cmd_open_file(svn_ra_s
 svn_error_t *
 svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
                                        apr_pool_t *pool,
-                                       const char *token,
+                                       const svn_string_t *token,
                                        const char *name,
                                        const svn_string_t *value)
 {
@@ -1823,11 +2145,11 @@ svn_ra_svn__write_cmd_change_file_prop(s
 svn_error_t *
 svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
                                  apr_pool_t *pool,
-                                 const char *token,
+                                 const svn_string_t *token,
                                  const char *text_checksum)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( "));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
   SVN_ERR(write_tuple_end_list(conn, pool));
@@ -1840,7 +2162,7 @@ svn_error_t *
 svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
                                   apr_pool_t *pool,
                                   const char *path,
-                                  const char *parent_token)
+                                  const svn_string_t *parent_token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( "));
   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
@@ -1852,11 +2174,11 @@ svn_ra_svn__write_cmd_absent_file(svn_ra
 svn_error_t *
 svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
                                       apr_pool_t *pool,
-                                      const char *token,
+                                      const svn_string_t *token,
                                       const svn_string_t *chunk)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( "));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_string(conn, pool, chunk));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
@@ -1866,10 +2188,10 @@ svn_ra_svn__write_cmd_textdelta_chunk(sv
 svn_error_t *
 svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
                                     apr_pool_t *pool,
-                                    const char *token)
+                                    const svn_string_t *token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( "));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
   return SVN_NO_ERROR;
@@ -1878,11 +2200,11 @@ svn_ra_svn__write_cmd_textdelta_end(svn_
 svn_error_t *
 svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
                                       apr_pool_t *pool,
-                                      const char *token,
+                                      const svn_string_t *token,
                                       const char *base_checksum)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( "));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
   SVN_ERR(write_tuple_end_list(conn, pool));
@@ -2020,9 +2342,7 @@ svn_ra_svn__write_cmd_change_rev_prop2(s
   SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( "));
   SVN_ERR(write_tuple_revision(conn, pool, rev));
   SVN_ERR(write_tuple_cstring(conn, pool, name));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_string_opt(conn, pool, value));
-  SVN_ERR(write_tuple_end_list(conn, pool));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
   SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
@@ -2274,14 +2594,12 @@ svn_error_t *
 svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
                              apr_pool_t *pool,
                              const char *path,
-                             const char *token,
+                             const svn_string_t *token,
                              svn_boolean_t break_lock)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( "));
   SVN_ERR(write_tuple_cstring(conn, pool, path));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_cstring_opt(conn, pool, token));
-  SVN_ERR(write_tuple_end_list(conn, pool));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, token));
   SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
@@ -2431,6 +2749,49 @@ svn_error_t *svn_ra_svn__write_cmd_failu
   return writebuf_write_literal(conn, pool, ") ) ");
 }
 
+/* Initializer for static svn_string_t . */
+#define STATIC_SVN_STRING(x) { x, sizeof(x) - 1 }
+
+/* Return a pre-cooked serialized representation for the changed path
+   flags NODE_KIND, TEXT_MODIFIED and PROPS_MODIFIED.  If we don't
+   have a suitable pre-cooked string, return an empty string. */
+static const svn_string_t *
+changed_path_flags(svn_node_kind_t node_kind,
+                   svn_boolean_t text_modified,
+                   svn_boolean_t props_modified)
+{
+  const static svn_string_t file_flags[4]
+    = { STATIC_SVN_STRING(" ) ( 4:file false false ) ) "),
+        STATIC_SVN_STRING(" ) ( 4:file false true ) ) "),
+        STATIC_SVN_STRING(" ) ( 4:file true false ) ) "),
+        STATIC_SVN_STRING(" ) ( 4:file true true ) ) ") };
+
+  const static svn_string_t dir_flags[4]
+    = { STATIC_SVN_STRING(" ) ( 3:dir false false ) ) "),
+        STATIC_SVN_STRING(" ) ( 3:dir false true ) ) "),
+        STATIC_SVN_STRING(" ) ( 3:dir true false ) ) "),
+        STATIC_SVN_STRING(" ) ( 3:dir true true ) ) ") };
+
+  const static svn_string_t no_flags = STATIC_SVN_STRING("");
+
+  /* Select the array based on the NODE_KIND. */
+  const svn_string_t *flags;
+  if (node_kind == svn_node_file)
+    flags = file_flags;
+  else if (node_kind == svn_node_dir)
+    flags = dir_flags;
+  else
+    return &no_flags;
+
+  /* Select the correct array entry. */
+  if (text_modified)
+    flags += 2;
+  if (props_modified)
+    flags++;
+
+  return flags;
+}
+
 svn_error_t *
 svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
                                         apr_pool_t *pool,
@@ -2442,21 +2803,77 @@ svn_ra_svn__write_data_log_changed_path(
                                         svn_boolean_t text_modified,
                                         svn_boolean_t props_modified)
 {
-  SVN_ERR(write_tuple_start_list(conn, pool));
+  apr_size_t path_len = strlen(path);
+  apr_size_t copyfrom_len = copyfrom_path ? strlen(copyfrom_path) : 0;
+  const svn_string_t *flags_str = changed_path_flags(node_kind,
+                                                     text_modified,
+                                                     props_modified);
+
+  /* How much buffer space do we need (worst case)? */
+  apr_size_t needed = 2                 /* list start */
+                    + path_len + SVN_INT64_BUFFER_SIZE
+                                        /* path */
+                    + 2                 /* action */
+                    + 2 + copyfrom_len + 2 * SVN_INT64_BUFFER_SIZE
+                                        /* list start + copy-from info */
+                    + flags_str->len;   /* flags and closing lists */
+
+  /* If the remaining buffer is big enough and we've got all parts,
+     directly copy into the buffer. */
+  if (   (conn->write_pos + needed <= sizeof(conn->write_buf))
+      && (flags_str->len > 0))
+    {
+      /* Quick path. */
+      /* Open list. */
+      char *p = conn->write_buf + conn->write_pos;
+      p[0] = '(';
+      p[1] = ' ';
+
+      /* Write path. */
+      p = write_ncstring_quick(p + 2, path, path_len);
+
+      /* Action */
+      p[0] = action;
+      p[1] = ' ';
+      p[2] = '(';
+
+      /* Copy-from info (if given) */
+      if (copyfrom_path)
+        {
+          p[3] = ' ';
+          p = write_ncstring_quick(p + 4, copyfrom_path, copyfrom_len);
+          p += svn__ui64toa(p, copyfrom_rev);
+        }
+      else
+        {
+          p += 3;
+        }
 
-  SVN_ERR(write_tuple_cstring(conn, pool, path));
-  SVN_ERR(writebuf_writechar(conn, pool, action));
-  SVN_ERR(writebuf_writechar(conn, pool, ' '));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
-  SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
-  SVN_ERR(write_tuple_end_list(conn, pool));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
-  SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
-  SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
+      /* Close with flags. */
+      memcpy(p, flags_str->data, flags_str->len);
+      conn->write_pos = p + flags_str->len - conn->write_buf;
+    }
+  else
+    {
+      /* Standard code path (fallback). */
+      SVN_ERR(write_tuple_start_list(conn, pool));
 
-  return writebuf_write_literal(conn, pool, ") ) ");
+      SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, path, path_len));
+      SVN_ERR(writebuf_writechar(conn, pool, action));
+      SVN_ERR(writebuf_writechar(conn, pool, ' '));
+      SVN_ERR(write_tuple_start_list(conn, pool));
+      SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
+      SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
+      SVN_ERR(write_tuple_end_list(conn, pool));
+      SVN_ERR(write_tuple_start_list(conn, pool));
+      SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
+      SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
+      SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
+
+      SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
+    }
+
+  return SVN_NO_ERROR;
 }
 
 svn_error_t *
@@ -2471,15 +2888,9 @@ svn_ra_svn__write_data_log_entry(svn_ra_
                                  unsigned revprop_count)
 {
   SVN_ERR(write_tuple_revision(conn, pool, revision));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_string_opt(conn, pool, author));
-  SVN_ERR(write_tuple_end_list(conn, pool));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_string_opt(conn, pool, date));
-  SVN_ERR(write_tuple_end_list(conn, pool));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_string_opt(conn, pool, message));
-  SVN_ERR(write_tuple_end_list(conn, pool));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, author));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, date));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, message));
   SVN_ERR(write_tuple_boolean(conn, pool, has_children));
   SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum));
   SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count));
@@ -2497,13 +2908,13 @@ svn_ra_svn__write_data_log_entry(svn_ra_
 /* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_string(const apr_array_header_t *items,
+svn_ra_svn__read_string(const svn_ra_svn__list_t *items,
                         int idx,
                         svn_string_t **result)
 {
-  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
-  *result = elt->u.string;
+  *result = &elt->u.string;
 
   return SVN_NO_ERROR;
 }
@@ -2511,13 +2922,13 @@ svn_ra_svn__read_string(const apr_array_
 /* In *RESULT, return the C-style string at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_cstring(const apr_array_header_t *items,
+svn_ra_svn__read_cstring(const svn_ra_svn__list_t *items,
                          int idx,
                          const char **result)
 {
-  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
-  *result = elt->u.string->data;
+  *result = elt->u.string.data;
 
   return SVN_NO_ERROR;
 }
@@ -2525,13 +2936,13 @@ svn_ra_svn__read_cstring(const apr_array
 /* In *RESULT, return the word at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_word(const apr_array_header_t *items,
+svn_ra_svn__read_word(const svn_ra_svn__list_t *items,
                       int idx,
                       const char **result)
 {
-  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
-  *result = elt->u.word;
+  *result = elt->u.word.data;
 
   return SVN_NO_ERROR;
 }
@@ -2539,11 +2950,11 @@ svn_ra_svn__read_word(const apr_array_he
 /* In *RESULT, return the revision at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_revision(const apr_array_header_t *items,
+svn_ra_svn__read_revision(const svn_ra_svn__list_t *items,
                           int idx,
                           svn_revnum_t *result)
 {
-  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER);
   *result = (svn_revnum_t)elt->u.number;
 
@@ -2553,15 +2964,15 @@ svn_ra_svn__read_revision(const apr_arra
 /* In *RESULT, return the boolean at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_boolean(const apr_array_header_t *items,
+svn_ra_svn__read_boolean(const svn_ra_svn__list_t *items,
                          int idx,
                          apr_uint64_t *result)
 {
-  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
-  if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0)
+  if (svn_string_compare(&elt->u.word, &str_true))
     *result = TRUE;
-  else if (strcmp(elt->u.word, "false") == 0)
+  else if (svn_string_compare(&elt->u.word, &str_false))
     *result = FALSE;
   else
     CHECK_PROTOCOL_COND(FALSE);
@@ -2572,21 +2983,21 @@ svn_ra_svn__read_boolean(const apr_array
 /* In *RESULT, return the tuple at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_list(const apr_array_header_t *items,
+svn_ra_svn__read_list(const svn_ra_svn__list_t *items,
                       int idx,
-                      const apr_array_header_t **result)
+                      const svn_ra_svn__list_t **result)
 {
-  svn_ra_svn_item_t *elt  = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt  = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST);
 
-  *result = elt->u.list;
+  *result = &elt->u.list;
   return SVN_NO_ERROR;
 }
 
 /* Verify the tuple ITEMS contains at least MIN and at most MAX elements.
  */
 static svn_error_t *
-svn_ra_svn__read_check_array_size(const apr_array_header_t *items,
+svn_ra_svn__read_check_array_size(const svn_ra_svn__list_t *items,
                                   int min,
                                   int max)
 {
@@ -2595,7 +3006,7 @@ svn_ra_svn__read_check_array_size(const
 }
 
 svn_error_t *
-svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items,
+svn_ra_svn__read_data_log_changed_entry(const svn_ra_svn__list_t *items,
                                         svn_string_t **cpath,
                                         const char **action,
                                         const char **copy_path,
@@ -2604,7 +3015,7 @@ svn_ra_svn__read_data_log_changed_entry(
                                         apr_uint64_t *text_mods,
                                         apr_uint64_t *prop_mods)
 {
-  const apr_array_header_t *sub_items;
+  const svn_ra_svn__list_t *sub_items;
 
   /* initialize optional values */
   *copy_path = NULL;

Modified: subversion/branches/ra-git/subversion/libsvn_ra_svn/ra_svn.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_svn/ra_svn.h?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_svn/ra_svn.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_svn/ra_svn.h Mon Nov 30 10:24:16 2015
@@ -96,6 +96,12 @@ struct svn_ra_svn_conn_st {
   apr_size_t error_check_interval;
   svn_boolean_t may_check_for_error;
 
+  /* I/O limits and tracking */
+  apr_uint64_t max_in;
+  apr_uint64_t current_in;
+  apr_uint64_t max_out;
+  apr_uint64_t current_out;
+
   /* repository info */
   const char *uuid;
   const char *repos_root;
@@ -123,6 +129,7 @@ struct svn_ra_svn__session_baton_t {
   apr_pool_t *pool;
   svn_ra_svn_conn_t *conn;
   svn_boolean_t is_tunneled;
+  svn_auth_baton_t *auth_baton;
   const char *url;
   const char *user;
   const char *hostname; /* The remote hostname. */
@@ -150,6 +157,12 @@ void svn_ra_svn__set_block_handler(svn_r
 svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn,
                                        svn_boolean_t *data_available);
 
+/* Signal a new request / response pair on CONN.  That resets the I/O
+ * counters we use to limit the size of individual requests / response pairs.
+ */
+void
+svn_ra_svn__reset_command_io_counters(svn_ra_svn_conn_t *conn);
+
 /* CRAM-MD5 client implementation. */
 svn_error_t *svn_ra_svn__cram_client(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
                                      const char *user, const char *password,
@@ -162,10 +175,9 @@ svn_error_t *svn_ra_svn__locate_real_err
 
 /* Return an error chain based on @a params (which contains a
  * command response indicating failure).  The error chain will be
- * in the same order as the errors indicated in @a params.  Use
- * @a pool for temporary allocations. */
-svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
-                                               apr_pool_t *pool);
+ * in the same order as the errors indicated in @a params. */
+svn_error_t *
+svn_ra_svn__handle_failure_status(const svn_ra_svn__list_t *params);
 
 /* Returns a stream that reads/writes from/to SOCK. */
 svn_ra_svn__stream_t *svn_ra_svn__stream_from_sock(apr_socket_t *sock,
@@ -219,7 +231,7 @@ svn_ra_svn__stream_data_available(svn_ra
  * tokens. */
 svn_error_t *
 svn_ra_svn__do_cyrus_auth(svn_ra_svn__session_baton_t *sess,
-                          const apr_array_header_t *mechlist,
+                          const svn_ra_svn__list_t *mechlist,
                           const char *realm, apr_pool_t *pool);
 
 /* Same as svn_ra_svn__do_cyrus_auth, but uses the built-in implementation of
@@ -228,7 +240,7 @@ svn_ra_svn__do_cyrus_auth(svn_ra_svn__se
  * mechanism with the server. */
 svn_error_t *
 svn_ra_svn__do_internal_auth(svn_ra_svn__session_baton_t *sess,
-                             const apr_array_header_t *mechlist,
+                             const svn_ra_svn__list_t *mechlist,
                              const char *realm, apr_pool_t *pool);
 
 /* Having picked a mechanism, start authentication by writing out an
@@ -238,8 +250,8 @@ svn_error_t *svn_ra_svn__auth_response(s
                                        apr_pool_t *pool,
                                        const char *mech, const char *mech_arg);
 
-/* Looks for MECH as a word in MECHLIST (an array of svn_ra_svn_item_t). */
-svn_boolean_t svn_ra_svn__find_mech(const apr_array_header_t *mechlist,
+/* Looks for MECH as a word in MECHLIST. */
+svn_boolean_t svn_ra_svn__find_mech(const svn_ra_svn__list_t *mechlist,
                                     const char *mech);
 
 /* Initialize the SASL library. */

Modified: subversion/branches/ra-git/subversion/libsvn_repos/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/commit.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_repos/commit.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_repos/commit.c Mon Nov 30 10:24:16 2015
@@ -124,6 +124,7 @@ struct dir_baton
   svn_revnum_t base_rev;        /* the revision I'm based on  */
   svn_boolean_t was_copied; /* was this directory added with history? */
   apr_pool_t *pool; /* my personal pool, in which I am allocated. */
+  svn_boolean_t checked_write; /* TRUE after successfull write check */
 };
 
 
@@ -131,6 +132,7 @@ struct file_baton
 {
   struct edit_baton *edit_baton;
   const char *path; /* the -absolute- path to this file in the fs */
+  svn_boolean_t checked_write; /* TRUE after successfull write check */
 };
 
 
@@ -171,6 +173,30 @@ out_of_date(const char *path, svn_node_k
                            path);
 }
 
+/* Perform an out of date check for base_rev against created rev,
+   and a sanity check of base_rev. */
+static svn_error_t *
+check_out_of_date(struct edit_baton *eb,
+                  const char *path,
+                  svn_node_kind_t kind,
+                  svn_revnum_t base_rev,
+                  svn_revnum_t created_rev)
+{
+  if (base_rev < created_rev)
+    {
+      return out_of_date(path, kind);
+    }
+  else if (base_rev > created_rev)
+    {
+      if (base_rev > svn_fs_txn_base_revision(eb->txn))
+        return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 
+                                 _("No such revision %ld"),
+                                 base_rev);
+    }
+
+  return SVN_NO_ERROR;
+}
+
 
 static svn_error_t *
 invoke_commit_cb(svn_commit_callback2_t commit_cb,
@@ -188,11 +214,11 @@ invoke_commit_cb(svn_commit_callback2_t
   if (commit_cb == NULL)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_fs_revision_prop(&date, fs, revision, SVN_PROP_REVISION_DATE,
-                               scratch_pool));
-  SVN_ERR(svn_fs_revision_prop(&author, fs, revision,
-                               SVN_PROP_REVISION_AUTHOR,
-                               scratch_pool));
+  SVN_ERR(svn_fs_revision_prop2(&date, fs, revision, SVN_PROP_REVISION_DATE,
+                                TRUE, scratch_pool, scratch_pool));
+  SVN_ERR(svn_fs_revision_prop2(&author, fs, revision,
+                                SVN_PROP_REVISION_AUTHOR,
+                                TRUE, scratch_pool, scratch_pool));
 
   commit_info = svn_create_commit_info(scratch_pool);
 
@@ -364,14 +390,18 @@ add_file_or_directory(const char *path,
   /* Build a new child baton. */
   if (is_dir)
     {
-      *return_baton = make_dir_baton(eb, pb, full_path, was_copied,
-                                     SVN_INVALID_REVNUM, pool);
+      struct dir_baton *new_db = make_dir_baton(eb, pb, full_path, was_copied,
+                                                SVN_INVALID_REVNUM, pool);
+
+      new_db->checked_write = TRUE; /* Just created */
+      *return_baton = new_db;
     }
   else
     {
       struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb));
       new_fb->edit_baton = eb;
       new_fb->path = full_path;
+      new_fb->checked_write = TRUE; /* Just created */
       *return_baton = new_fb;
     }
 
@@ -392,11 +422,16 @@ open_root(void *edit_baton,
   struct edit_baton *eb = edit_baton;
   svn_revnum_t youngest;
 
-  /* Ignore BASE_REVISION.  We always build our transaction against
-     HEAD.  However, we will keep it in our dir baton for out of
-     dateness checks.  */
+  /* We always build our transaction against HEAD.  However, we will
+     sanity-check BASE_REVISION and keep it in our dir baton for out
+     of dateness checks.  */
   SVN_ERR(svn_fs_youngest_rev(&youngest, eb->fs, eb->pool));
 
+  if (base_revision > youngest)
+    return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+                             _("No such revision %ld (HEAD is %ld)"),
+                             base_revision, youngest);
+
   /* Unless we've been instructed to use a specific transaction, we'll
      make our own. */
   if (eb->txn_owner)
@@ -443,7 +478,6 @@ delete_entry(const char *path,
   struct dir_baton *parent = parent_baton;
   struct edit_baton *eb = parent->edit_baton;
   svn_node_kind_t kind;
-  svn_revnum_t cr_rev;
   svn_repos_authz_access_t required = svn_authz_write;
   const char *full_path;
 
@@ -468,14 +502,18 @@ delete_entry(const char *path,
 
   /* Now, make sure we're deleting the node we *think* we're
      deleting, else return an out-of-dateness error. */
-  SVN_ERR(svn_fs_node_created_rev(&cr_rev, eb->txn_root, full_path, pool));
-  if (SVN_IS_VALID_REVNUM(revision) && (revision < cr_rev))
-    return svn_error_trace(out_of_date(full_path, kind));
+  if (SVN_IS_VALID_REVNUM(revision))
+    {
+      svn_revnum_t cr_rev;
+
+      SVN_ERR(svn_fs_node_created_rev(&cr_rev, eb->txn_root, full_path, pool));
+      SVN_ERR(check_out_of_date(eb, full_path, kind, revision, cr_rev));
+    }
 
   /* This routine is a mindless wrapper.  We call svn_fs_delete()
      because that will delete files and recursively delete
      directories.  */
-  return svn_fs_delete(eb->txn_root, full_path, pool);
+  return svn_error_trace(svn_fs_delete(eb->txn_root, full_path, pool));
 }
 
 
@@ -530,18 +568,23 @@ apply_textdelta(void *file_baton,
                 void **handler_baton)
 {
   struct file_baton *fb = file_baton;
+  struct edit_baton *eb = fb->edit_baton;
 
-  /* Check for write authorization. */
-  SVN_ERR(check_authz(fb->edit_baton, fb->path,
-                      fb->edit_baton->txn_root,
-                      svn_authz_write, pool));
+  if (!fb->checked_write)
+    {
+      /* Check for write authorization. */
+      SVN_ERR(check_authz(eb, fb->path, eb->txn_root,
+                          svn_authz_write, pool));
+      fb->checked_write = TRUE;
+    }
 
-  return svn_fs_apply_textdelta(handler, handler_baton,
-                                fb->edit_baton->txn_root,
-                                fb->path,
-                                base_checksum,
-                                NULL,
-                                pool);
+  return svn_error_trace(
+          svn_fs_apply_textdelta(handler, handler_baton,
+                                 eb->txn_root,
+                                 fb->path,
+                                 base_checksum,
+                                 NULL,
+                                 pool));
 }
 
 
@@ -585,8 +628,9 @@ open_file(const char *path,
 
   /* If the node our caller has is an older revision number than the
      one in our transaction, return an out-of-dateness error. */
-  if (SVN_IS_VALID_REVNUM(base_revision) && (base_revision < cr_rev))
-    return svn_error_trace(out_of_date(full_path, svn_node_file));
+  if (SVN_IS_VALID_REVNUM(base_revision))
+    SVN_ERR(check_out_of_date(eb, full_path, svn_node_file,
+                              base_revision, cr_rev));
 
   /* Build a new file baton */
   new_fb = apr_pcalloc(pool, sizeof(*new_fb));
@@ -611,9 +655,13 @@ change_file_prop(void *file_baton,
   struct file_baton *fb = file_baton;
   struct edit_baton *eb = fb->edit_baton;
 
-  /* Check for write authorization. */
-  SVN_ERR(check_authz(eb, fb->path, eb->txn_root,
-                      svn_authz_write, pool));
+  if (!fb->checked_write)
+    {
+      /* Check for write authorization. */
+      SVN_ERR(check_authz(eb, fb->path, eb->txn_root,
+                          svn_authz_write, pool));
+      fb->checked_write = TRUE;
+    }
 
   return svn_repos_fs_change_node_prop(eb->txn_root, fb->path,
                                        name, value, pool);
@@ -658,19 +706,24 @@ change_dir_prop(void *dir_baton,
   struct edit_baton *eb = db->edit_baton;
 
   /* Check for write authorization. */
-  SVN_ERR(check_authz(eb, db->path, eb->txn_root,
-                      svn_authz_write, pool));
-
-  if (SVN_IS_VALID_REVNUM(db->base_rev))
+  if (!db->checked_write)
     {
-      /* Subversion rule:  propchanges can only happen on a directory
-         which is up-to-date. */
-      svn_revnum_t created_rev;
-      SVN_ERR(svn_fs_node_created_rev(&created_rev,
-                                      eb->txn_root, db->path, pool));
+      SVN_ERR(check_authz(eb, db->path, eb->txn_root,
+                          svn_authz_write, pool));
+
+      if (SVN_IS_VALID_REVNUM(db->base_rev))
+        {
+          /* Subversion rule:  propchanges can only happen on a directory
+             which is up-to-date. */
+          svn_revnum_t created_rev;
+          SVN_ERR(svn_fs_node_created_rev(&created_rev,
+                                          eb->txn_root, db->path, pool));
+
+          SVN_ERR(check_out_of_date(eb, db->path, svn_node_dir,
+                                    db->base_rev, created_rev));
+        }
 
-      if (db->base_rev < created_rev)
-        return svn_error_trace(out_of_date(db->path, svn_node_dir));
+      db->checked_write = TRUE; /* Skip on further prop changes */
     }
 
   return svn_repos_fs_change_node_prop(eb->txn_root, db->path,

Modified: subversion/branches/ra-git/subversion/libsvn_repos/delta.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/delta.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_repos/delta.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_repos/delta.c Mon Nov 30 10:24:16 2015
@@ -266,6 +266,13 @@ svn_repos_dir_delta2(svn_fs_root_t *src_
        _("Invalid editor anchoring; at least one of the "
          "input paths is not a directory and there was no source entry"));
 
+  /* Don't report / compare stale revprops.  However, revprop changes that
+   * are made by a 3rd party outside this delta operation, may not be
+   * detected as per our visibility guarantees.  Reset the revprop caches
+   * for both roots in case they belong to different svn_fs_t instances. */
+  SVN_ERR(svn_fs_refresh_revision_props(svn_fs_root_fs(tgt_root), pool));
+  SVN_ERR(svn_fs_refresh_revision_props(svn_fs_root_fs(src_root), pool));
+
   /* Set the global target revision if one can be determined. */
   if (svn_fs_is_revision_root(tgt_root))
     {
@@ -321,7 +328,7 @@ svn_repos_dir_delta2(svn_fs_root_t *src_
   SVN_ERR(svn_fs_node_relation(&relation, tgt_root, tgt_fullpath,
                                src_root, src_fullpath, pool));
 
-  if (relation == svn_fs_node_same)
+  if (relation == svn_fs_node_unchanged)
     {
       /* They are the same node!  No-op (you gotta love those). */
       goto cleanup;
@@ -491,8 +498,8 @@ delta_proplists(struct context *c,
           SVN_ERR(change_fn(c, object, SVN_PROP_ENTRY_COMMITTED_REV,
                             cr_str, subpool));
 
-          SVN_ERR(svn_fs_revision_proplist(&r_props, fs, committed_rev,
-                                           pool));
+          SVN_ERR(svn_fs_revision_proplist2(&r_props, fs, committed_rev,
+                                            FALSE, pool, subpool));
 
           /* Transmit the committed-date. */
           committed_date = svn_hash_gets(r_props, SVN_PROP_REVISION_DATE);

Modified: subversion/branches/ra-git/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/deprecated.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_repos/deprecated.c Mon Nov 30 10:24:16 2015
@@ -731,6 +731,33 @@ repos_notify_handler(void *baton,
   }
 }
 
+svn_error_t *
+svn_repos_dump_fs3(svn_repos_t *repos,
+                   svn_stream_t *stream,
+                   svn_revnum_t start_rev,
+                   svn_revnum_t end_rev,
+                   svn_boolean_t incremental,
+                   svn_boolean_t use_deltas,
+                   svn_repos_notify_func_t notify_func,
+                   void *notify_baton,
+                   svn_cancel_func_t cancel_func,
+                   void *cancel_baton,
+                   apr_pool_t *pool)
+{
+  return svn_error_trace(svn_repos_dump_fs4(repos,
+                                            stream,
+                                            start_rev,
+                                            end_rev,
+                                            incremental,
+                                            use_deltas,
+                                            TRUE,
+                                            TRUE,
+                                            notify_func,
+                                            notify_baton,
+                                            cancel_func,
+                                            cancel_baton,
+                                            pool));
+}
 
 svn_error_t *
 svn_repos_dump_fs2(svn_repos_t *repos,
@@ -774,9 +801,9 @@ svn_repos_verify_fs2(svn_repos_t *repos,
                                               end_rev,
                                               FALSE,
                                               FALSE,
-                                              FALSE,
                                               notify_func,
                                               notify_baton,
+                                              NULL, NULL,
                                               cancel_func,
                                               cancel_baton,
                                               pool));