summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra_svn/marshal.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_ra_svn/marshal.c')
-rw-r--r--subversion/libsvn_ra_svn/marshal.c880
1 files changed, 707 insertions, 173 deletions
diff --git a/subversion/libsvn_ra_svn/marshal.c b/subversion/libsvn_ra_svn/marshal.c
index 0778269fa404e..41caaa51a553c 100644
--- a/subversion/libsvn_ra_svn/marshal.c
+++ b/subversion/libsvn_ra_svn/marshal.c
@@ -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_conn4(apr_socket_t *sock,
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,25 +236,53 @@ svn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock,
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;
}
+int
+svn_ra_svn__svndiff_version(svn_ra_svn_conn_t *conn)
+{
+ /* If we don't want to use compression, use the non-compressing
+ * "version 0" implementation. */
+ if (svn_ra_svn_compression_level(conn) <= 0)
+ return 0;
+
+ /* Prefer SVNDIFF2 over SVNDIFF1. */
+ if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED))
+ return 2;
+ if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_SVNDIFF1))
+ return 1;
+
+ /* The connection does not support SVNDIFF1/2; default to "version 0". */
+ return 0;
+}
+
apr_pool_t *
svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn)
{
@@ -204,8 +336,33 @@ svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn,
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 +372,12 @@ static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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 +496,19 @@ static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
{
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 +554,13 @@ static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
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 +686,69 @@ svn_ra_svn__write_number(svn_ra_svn_conn_t *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)
+ /* Apart from LEN bytes of string contents, we need room for a number,
+ a colon and a space. */
+ apr_size_t max_fill = sizeof(conn->write_buf) - SVN_INT64_BUFFER_SIZE - 2;
+
+ /* In most cases, there is enough left room in the WRITE_BUF
+ the we can serialize directly into it. On platforms with
+ segmented memory, LEN might actually be close to APR_SIZE_MAX.
+ Blindly doing arithmetic on it might cause an overflow. */
+ if ((len <= max_fill) && (conn->write_pos <= max_fill - len))
{
- SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0')));
- SVN_ERR(writebuf_writechar(conn, pool, ':'));
+ /* 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 +976,55 @@ write_tuple_string_opt(svn_ra_svn_conn_t *conn,
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 max_fill;
+
+ /* Special case. */
+ if (!str)
+ return writebuf_write(conn, pool, "( ) ", 4);
+
+ /* If this how far we can fill the WRITE_BUF with string data and still
+ guarantee that the length info will fit in as well. */
+ max_fill = sizeof(conn->write_buf)
+ - 2 /* open list */
+ - SVN_INT64_BUFFER_SIZE /* string length + separator */
+ - 2; /* close list */
+
+ /* On platforms with segmented memory, STR->LEN might actually be
+ close to APR_SIZE_MAX. Blindly doing arithmetic on it might
+ cause an overflow. */
+ if ((str->len <= max_fill) && (conn->write_pos <= max_fill - str->len))
+ {
+ /* 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 +1079,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 +1099,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 +1116,13 @@ write_cmd_open_node(svn_ra_svn_conn_t *conn,
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 +1131,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;
}
@@ -892,10 +1160,17 @@ static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap)
: vwrite_tuple_string(conn, pool, ap));
else if (*fmt == '(' && !opt)
- SVN_ERR(write_tuple_start_list(conn, pool));
+ {
+ /* Optional sub-tuples are not supported.
+ * If OPT was set, we would fall through to the malfunction call. */
+ SVN_ERR(write_tuple_start_list(conn, pool));
+ }
else if (*fmt == ')')
{
SVN_ERR(write_tuple_end_list(conn, pool));
+
+ /* OPT could not have been set when opening the list (see above),
+ * hence this is correct and handles nested tuples just fine. */
opt = FALSE;
}
else if (*fmt == '?')
@@ -939,7 +1214,7 @@ svn_ra_svn__write_tuple(svn_ra_svn_conn_t *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;
@@ -947,7 +1222,7 @@ static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_size_t buflen;
/* 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"));
@@ -957,11 +1232,22 @@ static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
if (len <= buflen)
{
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
@@ -970,7 +1256,7 @@ static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
* 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. */
@@ -998,7 +1284,8 @@ static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
/* 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;
@@ -1009,12 +1296,12 @@ static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
* 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,
@@ -1062,36 +1349,108 @@ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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;
-
- if (++p == end)
- return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
- _("Word is too long"));
+ /* 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;
+
+ /* 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 == '(')
{
+ /* 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;
- item->u.list = apr_array_make(pool, 4, sizeof(svn_ra_svn_item_t));
while (1)
{
SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
if (c == ')')
break;
- listitem = apr_array_push(item->u.list);
+
+ /* Auto-expand the list. */
+ if (count == capacity)
+ {
+ 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;
+ }
+
+ 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;
+
+ /* 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));
}
@@ -1193,7 +1552,7 @@ read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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;
@@ -1243,36 +1602,38 @@ svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
/* --- 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;
@@ -1283,24 +1644,24 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po
*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
@@ -1326,7 +1687,7 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po
*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':
@@ -1335,6 +1696,9 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po
case '3':
*va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown;
break;
+ case 'b':
+ *va_arg(*ap, svn_boolean_t *) = FALSE;
+ break;
case '(':
nesting_level++;
break;
@@ -1354,15 +1718,14 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po
}
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;
}
@@ -1373,7 +1736,7 @@ svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
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));
@@ -1381,7 +1744,7 @@ svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
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;
}
@@ -1400,24 +1763,23 @@ svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
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);
}
@@ -1442,15 +1804,14 @@ svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *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,
@@ -1459,12 +1820,11 @@ svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
/* 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. */
@@ -1483,8 +1843,6 @@ svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
}
}
- 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)
@@ -1501,20 +1859,20 @@ svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
{
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,
@@ -1528,7 +1886,12 @@ svn_ra_svn__has_command(svn_boolean_t *has_command,
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;
@@ -1550,10 +1913,14 @@ svn_ra_svn__handle_command(svn_boolean_t *terminate,
{
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)
{
@@ -1570,7 +1937,28 @@ svn_ra_svn__handle_command(svn_boolean_t *terminate,
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
@@ -1595,13 +1983,13 @@ svn_ra_svn__handle_command(svn_boolean_t *terminate,
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++)
@@ -1644,13 +2032,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;
@@ -1661,14 +2049,14 @@ svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
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;
@@ -1678,8 +2066,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)
{
@@ -1695,8 +2083,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 ( "));
@@ -1709,7 +2097,7 @@ svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
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)
{
@@ -1723,10 +2111,10 @@ svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
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;
@@ -1736,7 +2124,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));
@@ -1749,8 +2137,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)
{
@@ -1766,8 +2154,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 ( "));
@@ -1780,7 +2168,7 @@ svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
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)
{
@@ -1794,11 +2182,11 @@ svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
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));
@@ -1811,7 +2199,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));
@@ -1823,11 +2211,11 @@ svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
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, ") ) "));
@@ -1837,10 +2225,10 @@ svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
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;
@@ -1849,11 +2237,11 @@ svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
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));
@@ -1991,9 +2379,7 @@ svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
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));
@@ -2245,14 +2631,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, ") ) "));
@@ -2402,10 +2786,53 @@ svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
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)
+{
+ static const 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 ) ) ") };
+
+ static const 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 ) ) ") };
+
+ static const 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,
- const char *path,
+ const svn_string_t *path,
char action,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
@@ -2413,21 +2840,83 @@ svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
svn_boolean_t text_modified,
svn_boolean_t props_modified)
{
- SVN_ERR(write_tuple_start_list(conn, pool));
+ apr_size_t path_len = path->len;
+ 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);
+ apr_size_t flags_len = flags_str->len;
+
+ /* How much buffer space can we use for non-string data (worst case)? */
+ apr_size_t max_fill = sizeof(conn->write_buf)
+ - 2 /* list start */
+ - 2 - SVN_INT64_BUFFER_SIZE /* path */
+ - 2 /* action */
+ - 2 /* list start */
+ - 2 - SVN_INT64_BUFFER_SIZE /* copy-from path */
+ - 1 - SVN_INT64_BUFFER_SIZE; /* copy-from rev */
+
+ /* If the remaining buffer is big enough and we've got all parts,
+ directly copy into the buffer. On platforms with segmented memory,
+ PATH_LEN + COPYFROM_LEN might actually be close to APR_SIZE_MAX.
+ Blindly doing arithmetic on them might cause an overflow.
+ The sum in here cannot overflow because WRITE_BUF is small, i.e.
+ MAX_FILL and WRITE_POS are much smaller than APR_SIZE_MAX. */
+ if ( (path_len <= max_fill) && (copyfrom_len <= max_fill)
+ && (conn->write_pos + path_len + copyfrom_len + flags_len <= max_fill)
+ && (flags_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->data, 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));
+
+ SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, path->data, 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 writebuf_write_literal(conn, pool, ") ) ");
+ return SVN_NO_ERROR;
}
svn_error_t *
@@ -2442,15 +2931,9 @@ svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
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));
@@ -2458,6 +2941,57 @@ svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
+svn_error_t *
+svn_ra_svn__write_dirent(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const char *path,
+ svn_dirent_t *dirent,
+ apr_uint32_t dirent_fields)
+{
+ const char *kind = (dirent_fields & SVN_DIRENT_KIND)
+ ? svn_node_kind_to_word(dirent->kind)
+ : "unknown";
+
+ if (dirent_fields & ~SVN_DIRENT_KIND)
+ {
+ SVN_ERR(write_tuple_start_list(conn, pool));
+ SVN_ERR(write_tuple_cstring(conn, pool, path));
+ SVN_ERR(writebuf_write(conn, pool, kind, strlen(kind)));
+
+ SVN_ERR(writebuf_write_literal(conn, pool, " ( "));
+ if (dirent_fields & SVN_DIRENT_SIZE)
+ SVN_ERR(svn_ra_svn__write_number(conn, pool, dirent->size));
+
+ SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
+ if (dirent_fields & SVN_DIRENT_HAS_PROPS)
+ SVN_ERR(write_tuple_boolean(conn, pool, dirent->has_props));
+
+ SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
+ if (dirent_fields & SVN_DIRENT_CREATED_REV)
+ SVN_ERR(write_tuple_revision(conn, pool, dirent->created_rev));
+
+ SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
+ if (dirent_fields & SVN_DIRENT_TIME)
+ SVN_ERR(write_tuple_cstring_opt(conn, pool,
+ svn_time_to_cstring(dirent->time, pool)));
+
+ SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
+ if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
+ SVN_ERR(write_tuple_cstring_opt(conn, pool, dirent->last_author));
+
+ SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
+ }
+ else
+ {
+ SVN_ERR(write_tuple_start_list(conn, pool));
+ SVN_ERR(write_tuple_cstring(conn, pool, path));
+ SVN_ERR(writebuf_write(conn, pool, kind, strlen(kind)));
+ SVN_ERR(writebuf_write_literal(conn, pool, " ) "));
+ }
+
+ return SVN_NO_ERROR;
+}
+
/* If condition COND is not met, return a "malformed network data" error.
*/
#define CHECK_PROTOCOL_COND(cond)\
@@ -2468,13 +3002,13 @@ svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
/* 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;
}
@@ -2482,13 +3016,13 @@ svn_ra_svn__read_string(const apr_array_header_t *items,
/* 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;
}
@@ -2496,13 +3030,13 @@ svn_ra_svn__read_cstring(const apr_array_header_t *items,
/* 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;
}
@@ -2510,11 +3044,11 @@ svn_ra_svn__read_word(const apr_array_header_t *items,
/* 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;
@@ -2524,15 +3058,15 @@ svn_ra_svn__read_revision(const apr_array_header_t *items,
/* 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);
@@ -2543,21 +3077,21 @@ svn_ra_svn__read_boolean(const apr_array_header_t *items,
/* 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)
{
@@ -2566,7 +3100,7 @@ svn_ra_svn__read_check_array_size(const apr_array_header_t *items,
}
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,
@@ -2575,7 +3109,7 @@ svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items,
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;