summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra_svn
diff options
context:
space:
mode:
authorPeter Wemm <peter@FreeBSD.org>2018-05-08 03:44:38 +0000
committerPeter Wemm <peter@FreeBSD.org>2018-05-08 03:44:38 +0000
commit3faf8d6bffc5d0fb2525ba37bb504c53366caf9d (patch)
tree7e47911263e75034b767fe34b2f8d3d17e91f66d /subversion/libsvn_ra_svn
parenta55fb3c0d5eca7d887798125d5b95942b1f01d4b (diff)
Diffstat (limited to 'subversion/libsvn_ra_svn')
-rw-r--r--subversion/libsvn_ra_svn/client.c610
-rw-r--r--subversion/libsvn_ra_svn/cram.c4
-rw-r--r--subversion/libsvn_ra_svn/cyrus_auth.c89
-rw-r--r--subversion/libsvn_ra_svn/deprecated.c51
-rw-r--r--subversion/libsvn_ra_svn/editorp.c415
-rw-r--r--subversion/libsvn_ra_svn/internal_auth.c10
-rw-r--r--subversion/libsvn_ra_svn/marshal.c880
-rw-r--r--subversion/libsvn_ra_svn/protocol21
-rw-r--r--subversion/libsvn_ra_svn/ra_svn.h47
-rw-r--r--subversion/libsvn_ra_svn/wrapped_sasl.c197
10 files changed, 1825 insertions, 499 deletions
diff --git a/subversion/libsvn_ra_svn/client.c b/subversion/libsvn_ra_svn/client.c
index a4939ab2ad53b..ab1aa58cdc487 100644
--- a/subversion/libsvn_ra_svn/client.c
+++ b/subversion/libsvn_ra_svn/client.c
@@ -51,6 +51,7 @@
#include "svn_private_config.h"
#include "private/svn_fspath.h"
+#include "private/svn_string_private.h"
#include "private/svn_subr_private.h"
#include "../libsvn_ra/ra_loader.h"
@@ -188,7 +189,7 @@ static svn_error_t *make_connection(const char *hostname, unsigned short port,
/* Set *DIFFS to an array of svn_prop_t, allocated in POOL, based on the
property diffs in LIST, received from the server. */
-static svn_error_t *parse_prop_diffs(const apr_array_header_t *list,
+static svn_error_t *parse_prop_diffs(const svn_ra_svn__list_t *list,
apr_pool_t *pool,
apr_array_header_t **diffs)
{
@@ -199,26 +200,27 @@ static svn_error_t *parse_prop_diffs(const apr_array_header_t *list,
for (i = 0; i < list->nelts; i++)
{
svn_prop_t *prop;
- svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
+ 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,
_("Prop diffs element not a list"));
prop = apr_array_push(*diffs);
- SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "c(?s)", &prop->name,
- &prop->value));
+ SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "c(?s)",
+ &prop->name, &prop->value));
}
return SVN_NO_ERROR;
}
/* Parse a lockdesc, provided in LIST as specified by the protocol into
LOCK, allocated in POOL. */
-static svn_error_t *parse_lock(const apr_array_header_t *list, apr_pool_t *pool,
+static svn_error_t *parse_lock(const svn_ra_svn__list_t *list,
+ apr_pool_t *pool,
svn_lock_t **lock)
{
const char *cdate, *edate;
*lock = svn_lock_create(pool);
- SVN_ERR(svn_ra_svn__parse_tuple(list, pool, "ccc(?c)c(?c)", &(*lock)->path,
+ SVN_ERR(svn_ra_svn__parse_tuple(list, "ccc(?c)c(?c)", &(*lock)->path,
&(*lock)->token, &(*lock)->owner,
&(*lock)->comment, &cdate, &edate));
(*lock)->path = svn_fspath__canonicalize((*lock)->path, pool);
@@ -241,7 +243,7 @@ static svn_error_t *handle_auth_request(svn_ra_svn__session_baton_t *sess,
apr_pool_t *pool)
{
svn_ra_svn_conn_t *conn = sess->conn;
- apr_array_header_t *mechlist;
+ svn_ra_svn__list_t *mechlist;
const char *realm;
SVN_ERR(svn_ra_svn__read_cmd_response(conn, pool, "lc", &mechlist, &realm));
@@ -470,9 +472,9 @@ static void handle_child_process_error(apr_pool_t *pool, apr_status_t status,
in_stream = svn_stream_from_aprfile2(in_file, FALSE, pool);
out_stream = svn_stream_from_aprfile2(out_file, FALSE, pool);
- conn = svn_ra_svn_create_conn4(NULL, in_stream, out_stream,
+ conn = svn_ra_svn_create_conn5(NULL, in_stream, out_stream,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0,
- 0, pool);
+ 0, 0, 0, pool);
err = svn_error_wrap_apr(status, _("Error in child process: %s"), desc);
svn_error_clear(svn_ra_svn__write_cmd_failure(conn, pool, err));
svn_error_clear(err);
@@ -541,13 +543,13 @@ static svn_error_t *make_tunnel(const char **args, svn_ra_svn_conn_t **conn,
apr_file_inherit_unset(proc->out);
/* Guard against dotfile output to stdout on the server. */
- *conn = svn_ra_svn_create_conn4(NULL,
+ *conn = svn_ra_svn_create_conn5(NULL,
svn_stream_from_aprfile2(proc->out, FALSE,
pool),
svn_stream_from_aprfile2(proc->in, FALSE,
pool),
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
- 0, 0, pool);
+ 0, 0, 0, 0, pool);
err = svn_ra_svn__skip_leading_garbage(*conn, pool);
if (err)
return svn_error_quick_wrap(
@@ -625,14 +627,20 @@ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p,
svn_ra_svn_conn_t *conn;
apr_socket_t *sock;
apr_uint64_t minver, maxver;
- apr_array_header_t *mechlist, *server_caplist, *repos_caplist;
+ svn_ra_svn__list_t *mechlist, *server_caplist, *repos_caplist;
const char *client_string = NULL;
apr_pool_t *pool = result_pool;
+ svn_ra_svn__parent_t *parent;
+
+ parent = apr_pcalloc(pool, sizeof(*parent));
+ parent->client_url = svn_stringbuf_create(url, pool);
+ parent->server_url = svn_stringbuf_create(url, pool);
+ parent->path = svn_stringbuf_create_empty(pool);
sess = apr_palloc(pool, sizeof(*sess));
sess->pool = pool;
sess->is_tunneled = (tunnel_name != NULL);
- sess->url = apr_pstrdup(pool, url);
+ sess->parent = parent;
sess->user = uri->user;
sess->hostname = uri->hostname;
sess->tunnel_name = tunnel_name;
@@ -673,9 +681,9 @@ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p,
apr_pool_cleanup_register(pool, td, close_tunnel_cleanup,
apr_pool_cleanup_null);
- conn = svn_ra_svn_create_conn4(NULL, td->response, td->request,
+ conn = svn_ra_svn_create_conn5(NULL, td->response, td->request,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
- 0, 0, pool);
+ 0, 0, 0, 0, pool);
SVN_ERR(svn_ra_svn__skip_leading_garbage(conn, pool));
}
}
@@ -687,9 +695,9 @@ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p,
SVN_ERR(make_connection(uri->hostname,
uri->port ? uri->port : SVN_RA_SVN_PORT,
&sock, pool));
- conn = svn_ra_svn_create_conn4(sock, NULL, NULL,
+ conn = svn_ra_svn_create_conn5(sock, NULL, NULL,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
- 0, 0, pool);
+ 0, 0, 0, 0, pool);
}
/* Build the useragent string, querying the client for any
@@ -727,7 +735,7 @@ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p,
return svn_error_createf(SVN_ERR_RA_SVN_BAD_VERSION, NULL,
_("Server only supports versions up to %d"),
(int) maxver);
- SVN_ERR(svn_ra_svn_set_capabilities(conn, server_caplist));
+ SVN_ERR(svn_ra_svn__set_capabilities(conn, server_caplist));
/* All released versions of Subversion support edit-pipeline,
* so we do not support servers that do not. */
@@ -739,10 +747,11 @@ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p,
* capability list, and the URL, and subsequently there is an auth
* request. */
/* Client-side capabilities list: */
- SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "n(wwwwww)cc(?c)",
+ SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "n(wwwwwww)cc(?c)",
(apr_uint64_t) 2,
SVN_RA_SVN_CAP_EDIT_PIPELINE,
SVN_RA_SVN_CAP_SVNDIFF1,
+ SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED,
SVN_RA_SVN_CAP_ABSENT_ENTRIES,
SVN_RA_SVN_CAP_DEPTH,
SVN_RA_SVN_CAP_MERGEINFO,
@@ -760,7 +769,7 @@ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p,
SVN_ERR(svn_ra_svn__read_cmd_response(conn, pool, "c?c?l", &conn->uuid,
&conn->repos_root, &repos_caplist));
if (repos_caplist)
- SVN_ERR(svn_ra_svn_set_capabilities(conn, repos_caplist));
+ SVN_ERR(svn_ra_svn__set_capabilities(conn, repos_caplist));
if (conn->repos_root)
{
@@ -912,23 +921,29 @@ static svn_error_t *ra_svn_dup_session(svn_ra_session_t *new_session,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_reparent(svn_ra_session_t *ra_session,
- const char *url,
- apr_pool_t *pool)
+/* Send the "reparent to URL" command to the server for RA_SESSION and
+ update the session state. Use SCRATCH_POOL for tempoaries.
+ */
+static svn_error_t *
+reparent_server(svn_ra_session_t *ra_session,
+ const char *url,
+ apr_pool_t *scratch_pool)
{
svn_ra_svn__session_baton_t *sess = ra_session->priv;
+ svn_ra_svn__parent_t *parent = sess->parent;
svn_ra_svn_conn_t *conn = sess->conn;
svn_error_t *err;
apr_pool_t *sess_pool;
svn_ra_svn__session_baton_t *new_sess;
apr_uri_t uri;
- SVN_ERR(svn_ra_svn__write_cmd_reparent(conn, pool, url));
- err = handle_auth_request(sess, pool);
+ /* Send the request to the server. */
+ SVN_ERR(svn_ra_svn__write_cmd_reparent(conn, scratch_pool, url));
+ err = handle_auth_request(sess, scratch_pool);
if (! err)
{
- SVN_ERR(svn_ra_svn__read_cmd_response(conn, pool, ""));
- sess->url = apr_pstrdup(sess->pool, url);
+ SVN_ERR(svn_ra_svn__read_cmd_response(conn, scratch_pool, ""));
+ svn_stringbuf_set(parent->server_url, url);
return SVN_NO_ERROR;
}
else if (err->apr_err != SVN_ERR_RA_SVN_UNKNOWN_CMD)
@@ -959,11 +974,143 @@ static svn_error_t *ra_svn_reparent(svn_ra_session_t *ra_session,
return SVN_NO_ERROR;
}
+/* Make sure that RA_SESSION's client and server-side parent infp are in
+ sync. Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+ensure_exact_server_parent(svn_ra_session_t *ra_session,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_svn__session_baton_t *sess = ra_session->priv;
+ svn_ra_svn__parent_t *parent = sess->parent;
+
+ /* During e.g. a checkout operation, many requests will be sent for the
+ same URL that was used to create the session. So, both sides are
+ often already in sync. */
+ if (svn_stringbuf_compare(parent->client_url, parent->server_url))
+ return SVN_NO_ERROR;
+
+ /* Actually reparent the server to the session URL. */
+ SVN_ERR(reparent_server(ra_session, parent->client_url->data,
+ scratch_pool));
+ svn_stringbuf_setempty(parent->path);
+
+ return SVN_NO_ERROR;
+}
+
+/* Return a copy of PATH, adjusted to the RA_SESSION's server parent URL.
+ Allocate the result in RESULT_POOL. */
+static const char *
+reparent_path(svn_ra_session_t *ra_session,
+ const char *path,
+ apr_pool_t *result_pool)
+{
+ svn_ra_svn__session_baton_t *sess = ra_session->priv;
+ svn_ra_svn__parent_t *parent = sess->parent;
+
+ return svn_relpath_join(parent->path->data, path, result_pool);
+}
+
+/* Return a copy of PATHS, containing the same const char * paths but
+ adjusted to the RA_SESSION's server parent URL. Returns NULL if
+ PATHS is NULL. Allocate the result in RESULT_POOL. */
+static apr_array_header_t *
+reparent_path_array(svn_ra_session_t *ra_session,
+ const apr_array_header_t *paths,
+ apr_pool_t *result_pool)
+{
+ int i;
+ apr_array_header_t *result;
+
+ if (!paths)
+ return NULL;
+
+ result = apr_array_copy(result_pool, paths);
+ for (i = 0; i < result->nelts; ++i)
+ {
+ const char **path = &APR_ARRAY_IDX(result, i, const char *);
+ *path = reparent_path(ra_session, *path, result_pool);
+ }
+
+ return result;
+}
+
+/* Return a copy of PATHS, containing the same paths for keys but adjusted
+ to the RA_SESSION's server parent URL. Keeps the values as-are and
+ returns NULL if PATHS is NULL. Allocate the result in RESULT_POOL. */
+static apr_hash_t *
+reparent_path_hash(svn_ra_session_t *ra_session,
+ apr_hash_t *paths,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *result;
+ apr_hash_index_t *hi;
+
+ if (!paths)
+ return NULL;
+
+ result = svn_hash__make(result_pool);
+ for (hi = apr_hash_first(scratch_pool, paths); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = apr_hash_this_key(hi);
+ svn_hash_sets(result,
+ reparent_path(ra_session, path, result_pool),
+ apr_hash_this_val(hi));
+ }
+
+ return result;
+}
+
+static svn_error_t *ra_svn_reparent(svn_ra_session_t *ra_session,
+ const char *url,
+ apr_pool_t *pool)
+{
+ svn_ra_svn__session_baton_t *sess = ra_session->priv;
+ svn_ra_svn__parent_t *parent = sess->parent;
+ svn_ra_svn_conn_t *conn = sess->conn;
+ const char *path;
+
+ /* Eliminate reparent requests if they are to a sub-path of the
+ server's current parent path. */
+ path = svn_uri_skip_ancestor(parent->server_url->data, url, pool);
+ if (!path)
+ {
+ /* Send the request to the server.
+
+ If within the same repository, reparent to the repo root
+ because this will maximize the chance to turn future reparent
+ requests into a client-side update of the rel path. */
+ path = conn->repos_root
+ ? svn_uri_skip_ancestor(conn->repos_root, url, pool)
+ : NULL;
+
+ if (path)
+ SVN_ERR(reparent_server(ra_session, conn->repos_root, pool));
+ else
+ SVN_ERR(reparent_server(ra_session, url, pool));
+ }
+
+ /* Update the local PARENT information.
+ PARENT.SERVER_BASE_URL is already up-to-date. */
+ svn_stringbuf_set(parent->client_url, url);
+ if (path)
+ svn_stringbuf_set(parent->path, path);
+ else
+ svn_stringbuf_setempty(parent->path);
+
+ return SVN_NO_ERROR;
+}
+
static svn_error_t *ra_svn_get_session_url(svn_ra_session_t *session,
- const char **url, apr_pool_t *pool)
+ const char **url,
+ apr_pool_t *pool)
{
svn_ra_svn__session_baton_t *sess = session->priv;
- *url = apr_pstrdup(pool, sess->url);
+ svn_ra_svn__parent_t *parent = sess->parent;
+
+ *url = apr_pstrmemdup(pool, parent->client_url->data,
+ parent->client_url->len);
+
return SVN_NO_ERROR;
}
@@ -1069,7 +1216,7 @@ static svn_error_t *ra_svn_rev_proplist(svn_ra_session_t *session, svn_revnum_t
{
svn_ra_svn__session_baton_t *sess_baton = session->priv;
svn_ra_svn_conn_t *conn = sess_baton->conn;
- apr_array_header_t *proplist;
+ svn_ra_svn__list_t *proplist;
SVN_ERR(svn_ra_svn__write_cmd_rev_proplist(conn, pool, rev));
SVN_ERR(handle_auth_request(sess_baton, pool));
@@ -1172,6 +1319,9 @@ static svn_error_t *ra_svn_commit(svn_ra_session_t *session,
svn_string_create(sess_baton->useragent, pool));
}
+ /* Callbacks may assume that all data is relative the sessions's URL. */
+ SVN_ERR(ensure_exact_server_parent(session, pool));
+
/* Tell the server we're starting the commit.
Send log message here for backwards compatibility with servers
before 1.5. */
@@ -1216,12 +1366,12 @@ static svn_error_t *ra_svn_commit(svn_ra_session_t *session,
return SVN_NO_ERROR;
}
-/* Parse IPROPLIST, an array of svn_ra_svn_item_t structures, as a list of
+/* Parse IPROPLIST, an array of svn_ra_svn__item_t structures, as a list of
const char * repos relative paths and properties for those paths, storing
the result as an array of svn_prop_inherited_item_t *items. */
static svn_error_t *
parse_iproplist(apr_array_header_t **inherited_props,
- const apr_array_header_t *iproplist,
+ const svn_ra_svn__list_t *iproplist,
svn_ra_session_t *session,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
@@ -1247,14 +1397,13 @@ parse_iproplist(apr_array_header_t **inherited_props,
for (i = 0; i < iproplist->nelts; i++)
{
- apr_array_header_t *iprop_list;
+ svn_ra_svn__list_t *iprop_list;
char *parent_rel_path;
apr_hash_t *iprops;
apr_hash_index_t *hi;
svn_prop_inherited_item_t *new_iprop =
apr_palloc(result_pool, sizeof(*new_iprop));
- svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(iproplist, i,
- svn_ra_svn_item_t);
+ svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(iproplist, i);
if (elt->kind != SVN_RA_SVN_LIST)
return svn_error_create(
SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
@@ -1262,7 +1411,7 @@ parse_iproplist(apr_array_header_t **inherited_props,
svn_pool_clear(iterpool);
- SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, iterpool, "cl",
+ SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "cl",
&parent_rel_path, &iprop_list));
SVN_ERR(svn_ra_svn__parse_proplist(iprop_list, iterpool, &iprops));
new_iprop->path_or_url = apr_pstrdup(result_pool, parent_rel_path);
@@ -1292,12 +1441,13 @@ static svn_error_t *ra_svn_get_file(svn_ra_session_t *session, const char *path,
{
svn_ra_svn__session_baton_t *sess_baton = session->priv;
svn_ra_svn_conn_t *conn = sess_baton->conn;
- apr_array_header_t *proplist;
+ svn_ra_svn__list_t *proplist;
const char *expected_digest;
svn_checksum_t *expected_checksum = NULL;
svn_checksum_ctx_t *checksum_ctx;
apr_pool_t *iterpool;
+ path = reparent_path(session, path, pool);
SVN_ERR(svn_ra_svn__write_cmd_get_file(conn, pool, path, rev,
(props != NULL), (stream != NULL)));
SVN_ERR(handle_auth_request(sess_baton, pool));
@@ -1325,22 +1475,22 @@ static svn_error_t *ra_svn_get_file(svn_ra_session_t *session, const char *path,
iterpool = svn_pool_create(pool);
while (1)
{
- svn_ra_svn_item_t *item;
+ svn_ra_svn__item_t *item;
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_svn__read_item(conn, iterpool, &item));
if (item->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Non-string as part of file contents"));
- if (item->u.string->len == 0)
+ if (item->u.string.len == 0)
break;
if (expected_checksum)
- SVN_ERR(svn_checksum_update(checksum_ctx, item->u.string->data,
- item->u.string->len));
+ SVN_ERR(svn_checksum_update(checksum_ctx, item->u.string.data,
+ item->u.string.len));
- SVN_ERR(svn_stream_write(stream, item->u.string->data,
- &item->u.string->len));
+ SVN_ERR(svn_stream_write(stream, item->u.string.data,
+ &item->u.string.len));
}
svn_pool_destroy(iterpool);
@@ -1360,6 +1510,35 @@ static svn_error_t *ra_svn_get_file(svn_ra_session_t *session, const char *path,
return SVN_NO_ERROR;
}
+/* Write the protocol words that correspond to DIRENT_FIELDS to CONN
+ * and use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+send_dirent_fields(svn_ra_svn_conn_t *conn,
+ apr_uint32_t dirent_fields,
+ apr_pool_t *scratch_pool)
+{
+ if (dirent_fields & SVN_DIRENT_KIND)
+ SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool,
+ SVN_RA_SVN_DIRENT_KIND));
+ if (dirent_fields & SVN_DIRENT_SIZE)
+ SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool,
+ SVN_RA_SVN_DIRENT_SIZE));
+ if (dirent_fields & SVN_DIRENT_HAS_PROPS)
+ SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool,
+ SVN_RA_SVN_DIRENT_HAS_PROPS));
+ if (dirent_fields & SVN_DIRENT_CREATED_REV)
+ SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool,
+ SVN_RA_SVN_DIRENT_CREATED_REV));
+ if (dirent_fields & SVN_DIRENT_TIME)
+ SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool,
+ SVN_RA_SVN_DIRENT_TIME));
+ if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
+ SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool,
+ SVN_RA_SVN_DIRENT_LAST_AUTHOR));
+
+ return SVN_NO_ERROR;
+}
+
static svn_error_t *ra_svn_get_dir(svn_ra_session_t *session,
apr_hash_t **dirents,
svn_revnum_t *fetched_rev,
@@ -1371,23 +1550,13 @@ static svn_error_t *ra_svn_get_dir(svn_ra_session_t *session,
{
svn_ra_svn__session_baton_t *sess_baton = session->priv;
svn_ra_svn_conn_t *conn = sess_baton->conn;
- apr_array_header_t *proplist, *dirlist;
+ svn_ra_svn__list_t *proplist, *dirlist;
int i;
+ path = reparent_path(session, path, pool);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(c(?r)bb(!", "get-dir", path,
rev, (props != NULL), (dirents != NULL)));
- if (dirent_fields & SVN_DIRENT_KIND)
- SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_DIRENT_KIND));
- if (dirent_fields & SVN_DIRENT_SIZE)
- SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_DIRENT_SIZE));
- if (dirent_fields & SVN_DIRENT_HAS_PROPS)
- SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_DIRENT_HAS_PROPS));
- if (dirent_fields & SVN_DIRENT_CREATED_REV)
- SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_DIRENT_CREATED_REV));
- if (dirent_fields & SVN_DIRENT_TIME)
- SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_DIRENT_TIME));
- if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
- SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_DIRENT_LAST_AUTHOR));
+ SVN_ERR(send_dirent_fields(conn, dirent_fields, pool));
/* Always send the, nominally optional, want-iprops as "false" to
workaround a bug in svnserve 1.8.0-1.8.8 that causes the server
@@ -1416,12 +1585,12 @@ static svn_error_t *ra_svn_get_dir(svn_ra_session_t *session,
svn_dirent_t *dirent;
apr_uint64_t size;
svn_revnum_t crev;
- svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(dirlist, i, svn_ra_svn_item_t);
+ svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(dirlist, i);
if (elt->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Dirlist element not a list"));
- SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "cwnbr(?c)(?c)",
+ SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "cwnbr(?c)(?c)",
&name, &kind, &size, &has_props,
&crev, &cdate, &cauthor));
@@ -1481,12 +1650,14 @@ static svn_error_t *ra_svn_get_mergeinfo(svn_ra_session_t *session,
apr_pool_t *pool)
{
svn_ra_svn__session_baton_t *sess_baton = session->priv;
+ svn_ra_svn__parent_t *parent = sess_baton->parent;
svn_ra_svn_conn_t *conn = sess_baton->conn;
int i;
- apr_array_header_t *mergeinfo_tuple;
- svn_ra_svn_item_t *elt;
+ svn_ra_svn__list_t *mergeinfo_tuple;
+ svn_ra_svn__item_t *elt;
const char *path;
+ paths = reparent_path_array(session, paths, pool);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "get-mergeinfo"));
for (i = 0; i < paths->nelts; i++)
{
@@ -1509,16 +1680,23 @@ static svn_error_t *ra_svn_get_mergeinfo(svn_ra_session_t *session,
svn_mergeinfo_t for_path;
const char *to_parse;
- elt = &((svn_ra_svn_item_t *) mergeinfo_tuple->elts)[i];
+ elt = &SVN_RA_SVN__LIST_ITEM(mergeinfo_tuple, i);
if (elt->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Mergeinfo element is not a list"));
- SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "cc",
+ SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "cc",
&path, &to_parse));
SVN_ERR(svn_mergeinfo_parse(&for_path, to_parse, pool));
+
/* Correct for naughty servers that send "relative" paths
with leading slashes! */
- svn_hash_sets(*catalog, path[0] == '/' ? path + 1 :path, for_path);
+ if (path[0] == '/')
+ ++path;
+
+ /* Correct for the (potential) difference between client and
+ server-side session parent paths. */
+ path = svn_relpath_skip_ancestor(parent->path->data, path);
+ svn_hash_sets(*catalog, path, for_path);
}
}
@@ -1540,6 +1718,9 @@ static svn_error_t *ra_svn_update(svn_ra_session_t *session,
svn_ra_svn_conn_t *conn = sess_baton->conn;
svn_boolean_t recurse = DEPTH_TO_RECURSE(depth);
+ /* Callbacks may assume that all data is relative the sessions's URL. */
+ SVN_ERR(ensure_exact_server_parent(session, scratch_pool));
+
/* Tell the server we want to start an update. */
SVN_ERR(svn_ra_svn__write_cmd_update(conn, pool, rev, target, recurse,
depth, send_copyfrom_args,
@@ -1571,6 +1752,9 @@ ra_svn_switch(svn_ra_session_t *session,
svn_ra_svn_conn_t *conn = sess_baton->conn;
svn_boolean_t recurse = DEPTH_TO_RECURSE(depth);
+ /* Callbacks may assume that all data is relative the sessions's URL. */
+ SVN_ERR(ensure_exact_server_parent(session, scratch_pool));
+
/* Tell the server we want to start a switch. */
SVN_ERR(svn_ra_svn__write_cmd_switch(conn, pool, rev, target, recurse,
switch_url, depth,
@@ -1596,6 +1780,9 @@ static svn_error_t *ra_svn_status(svn_ra_session_t *session,
svn_ra_svn_conn_t *conn = sess_baton->conn;
svn_boolean_t recurse = DEPTH_TO_RECURSE(depth);
+ /* Callbacks may assume that all data is relative the sessions's URL. */
+ SVN_ERR(ensure_exact_server_parent(session, pool));
+
/* Tell the server we want to start a status operation. */
SVN_ERR(svn_ra_svn__write_cmd_status(conn, pool, target, recurse, rev,
depth));
@@ -1623,6 +1810,9 @@ static svn_error_t *ra_svn_diff(svn_ra_session_t *session,
svn_ra_svn_conn_t *conn = sess_baton->conn;
svn_boolean_t recurse = DEPTH_TO_RECURSE(depth);
+ /* Callbacks may assume that all data is relative the sessions's URL. */
+ SVN_ERR(ensure_exact_server_parent(session, pool));
+
/* Tell the server we want to start a diff. */
SVN_ERR(svn_ra_svn__write_cmd_diff(conn, pool, rev, target, recurse,
ignore_ancestry, versus_url,
@@ -1636,6 +1826,16 @@ static svn_error_t *ra_svn_diff(svn_ra_session_t *session,
return SVN_NO_ERROR;
}
+/* Return TRUE if ITEM matches the word "done". */
+static svn_boolean_t
+is_done_response(const svn_ra_svn__item_t *item)
+{
+ static const svn_string_t str_done = SVN__STATIC_STRING("done");
+
+ return ( item->kind == SVN_RA_SVN_WORD
+ && svn_string_compare(&item->u.word, &str_done));
+}
+
static svn_error_t *
perform_ra_svn_log(svn_error_t **outer_error,
@@ -1716,23 +1916,23 @@ perform_ra_svn_log(svn_error_t **outer_error,
apr_uint64_t has_children_param, invalid_revnum_param;
apr_uint64_t has_subtractive_merge_param;
svn_string_t *author, *date, *message;
- apr_array_header_t *cplist, *rplist;
+ svn_ra_svn__list_t *cplist, *rplist;
svn_log_entry_t *log_entry;
svn_boolean_t has_children;
svn_boolean_t subtractive_merge = FALSE;
apr_uint64_t revprop_count;
- svn_ra_svn_item_t *item;
+ svn_ra_svn__item_t *item;
apr_hash_t *cphash;
svn_revnum_t rev;
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_svn__read_item(conn, iterpool, &item));
- if (item->kind == SVN_RA_SVN_WORD && strcmp(item->u.word, "done") == 0)
+ if (is_done_response(item))
break;
if (item->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Log entry not a list"));
- SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, iterpool,
+ SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list,
"lr(?s)(?s)(?s)?BBnl?B",
&cplist, &rev, &author, &date,
&message, &has_children_param,
@@ -1774,13 +1974,12 @@ perform_ra_svn_log(svn_error_t **outer_error,
const char *copy_path, *action, *kind_str;
apr_uint64_t text_mods, prop_mods;
svn_revnum_t copy_rev;
- svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(cplist, i,
- svn_ra_svn_item_t);
+ svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(cplist, i);
if (elt->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Changed-path entry not a list"));
- SVN_ERR(svn_ra_svn__read_data_log_changed_entry(elt->u.list,
+ SVN_ERR(svn_ra_svn__read_data_log_changed_entry(&elt->u.list,
&cpath, &action, &copy_path,
&copy_rev, &kind_str,
&text_mods, &prop_mods));
@@ -1810,7 +2009,7 @@ perform_ra_svn_log(svn_error_t **outer_error,
- Except if the server sends more than a >= 1 limit top level items
- Or when the callback reported a SVN_ERR_CEASE_INVOCATION
in an earlier invocation. */
- if (! (limit && (nest_level == 0) && (++nreceived > limit))
+ if (! ((limit > 0) && (nest_level == 0) && (++nreceived > limit))
&& ! *outer_error)
{
svn_error_t *err;
@@ -1838,7 +2037,7 @@ perform_ra_svn_log(svn_error_t **outer_error,
SVN_PROP_REVISION_LOG, message);
err = receiver(receiver_baton, log_entry, iterpool);
- if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
+ if (svn_error_find_cause(err, SVN_ERR_CEASE_INVOCATION))
{
*outer_error = svn_error_trace(
svn_error_compose_create(*outer_error, err));
@@ -1878,6 +2077,16 @@ ra_svn_log(svn_ra_session_t *session,
svn_error_t *outer_error = NULL;
svn_error_t *err;
+ /* If we don't specify paths, the session's URL is implied.
+
+ Because the paths passed to callbacks are always relative the repos
+ root, there is no need *always* sync the parent URLs despite invoking
+ user-provided callbacks. */
+ if (paths)
+ paths = reparent_path_array(session, paths, pool);
+ else
+ SVN_ERR(ensure_exact_server_parent(session, pool));
+
err = svn_error_trace(perform_ra_svn_log(&outer_error,
session, paths,
start, end,
@@ -1903,6 +2112,7 @@ static svn_error_t *ra_svn_check_path(svn_ra_session_t *session,
svn_ra_svn_conn_t *conn = sess_baton->conn;
const char *kind_word;
+ path = reparent_path(session, path, pool);
SVN_ERR(svn_ra_svn__write_cmd_check_path(conn, pool, path, rev));
SVN_ERR(handle_auth_request(sess_baton, pool));
SVN_ERR(svn_ra_svn__read_cmd_response(conn, pool, "w", &kind_word));
@@ -1929,9 +2139,10 @@ static svn_error_t *ra_svn_stat(svn_ra_session_t *session,
{
svn_ra_svn__session_baton_t *sess_baton = session->priv;
svn_ra_svn_conn_t *conn = sess_baton->conn;
- apr_array_header_t *list = NULL;
+ svn_ra_svn__list_t *list = NULL;
svn_dirent_t *the_dirent;
+ path = reparent_path(session, path, pool);
SVN_ERR(svn_ra_svn__write_cmd_stat(conn, pool, path, rev));
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
N_("'stat' not implemented")));
@@ -1948,7 +2159,7 @@ static svn_error_t *ra_svn_stat(svn_ra_session_t *session,
svn_revnum_t crev;
apr_uint64_t size;
- SVN_ERR(svn_ra_svn__parse_tuple(list, pool, "wnbr(?c)(?c)",
+ SVN_ERR(svn_ra_svn__parse_tuple(list, "wnbr(?c)(?c)",
&kind, &size, &has_props,
&crev, &cdate, &cauthor));
@@ -1978,8 +2189,11 @@ static svn_error_t *ra_svn_get_locations(svn_ra_session_t *session,
svn_ra_svn_conn_t *conn = sess_baton->conn;
svn_revnum_t revision;
svn_boolean_t is_done;
+ apr_pool_t *iterpool;
int i;
+ path = reparent_path(session, path, pool);
+
/* Transmit the parameters. */
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(cr(!",
"get-locations", path, peg_revision));
@@ -1998,21 +2212,25 @@ static svn_error_t *ra_svn_get_locations(svn_ra_session_t *session,
/* Read the hash items. */
is_done = FALSE;
*locations = apr_hash_make(pool);
+ iterpool = svn_pool_create(pool);
while (!is_done)
{
- svn_ra_svn_item_t *item;
+ svn_ra_svn__item_t *item;
const char *ret_path;
- SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
- if (item->kind == SVN_RA_SVN_WORD && strcmp(item->u.word, "done") == 0)
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_ra_svn__read_item(conn, iterpool, &item));
+ if (is_done_response(item))
is_done = 1;
else if (item->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Location entry not a list"));
else
{
- SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, pool, "rc",
+ SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list, "rc",
&revision, &ret_path));
+
+ /* This also makes RET_PATH live in POOL rather than ITERPOOL. */
ret_path = svn_fspath__canonicalize(ret_path, pool);
apr_hash_set(*locations, apr_pmemdup(pool, &revision,
sizeof(revision)),
@@ -2020,20 +2238,23 @@ static svn_error_t *ra_svn_get_locations(svn_ra_session_t *session,
}
}
+ svn_pool_destroy(iterpool);
+
/* Read the response. This is so the server would have a chance to
* report an error. */
return svn_error_trace(svn_ra_svn__read_cmd_response(conn, pool, ""));
}
static svn_error_t *
-ra_svn_get_location_segments(svn_ra_session_t *session,
- const char *path,
- svn_revnum_t peg_revision,
- svn_revnum_t start_rev,
- svn_revnum_t end_rev,
- svn_location_segment_receiver_t receiver,
- void *receiver_baton,
- apr_pool_t *pool)
+perform_get_location_segments(svn_error_t **outer_error,
+ svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t peg_revision,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ svn_location_segment_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *pool)
{
svn_ra_svn__session_baton_t *sess_baton = session->priv;
svn_ra_svn_conn_t *conn = sess_baton->conn;
@@ -2055,12 +2276,12 @@ ra_svn_get_location_segments(svn_ra_session_t *session,
while (!is_done)
{
svn_revnum_t range_start, range_end;
- svn_ra_svn_item_t *item;
+ svn_ra_svn__item_t *item;
const char *ret_path;
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_svn__read_item(conn, iterpool, &item));
- if (item->kind == SVN_RA_SVN_WORD && strcmp(item->u.word, "done") == 0)
+ if (is_done_response(item))
is_done = 1;
else if (item->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
@@ -2069,7 +2290,7 @@ ra_svn_get_location_segments(svn_ra_session_t *session,
{
svn_location_segment_t *segment = apr_pcalloc(iterpool,
sizeof(*segment));
- SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, iterpool, "rr(?c)",
+ SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list, "rr(?c)",
&range_start, &range_end, &ret_path));
if (! (SVN_IS_VALID_REVNUM(range_start)
&& SVN_IS_VALID_REVNUM(range_end)))
@@ -2080,7 +2301,17 @@ ra_svn_get_location_segments(svn_ra_session_t *session,
segment->path = ret_path;
segment->range_start = range_start;
segment->range_end = range_end;
- SVN_ERR(receiver(segment, receiver_baton, iterpool));
+
+ if (!*outer_error)
+ {
+ svn_error_t *err = svn_error_trace(receiver(segment, receiver_baton,
+ iterpool));
+
+ if (svn_error_find_cause(err, SVN_ERR_CEASE_INVOCATION))
+ *outer_error = err;
+ else
+ SVN_ERR(err);
+ }
}
}
svn_pool_destroy(iterpool);
@@ -2092,6 +2323,27 @@ ra_svn_get_location_segments(svn_ra_session_t *session,
return SVN_NO_ERROR;
}
+static svn_error_t *
+ra_svn_get_location_segments(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t peg_revision,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ svn_location_segment_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *pool)
+{
+ svn_error_t *outer_err = SVN_NO_ERROR;
+ svn_error_t *err;
+
+ path = reparent_path(session, path, pool);
+ err = svn_error_trace(
+ perform_get_location_segments(&outer_err, session, path,
+ peg_revision, start_rev, end_rev,
+ receiver, receiver_baton, pool));
+ return svn_error_compose_create(outer_err, err);
+}
+
static svn_error_t *ra_svn_get_file_revs(svn_ra_session_t *session,
const char *path,
svn_revnum_t start, svn_revnum_t end,
@@ -2109,6 +2361,7 @@ static svn_error_t *ra_svn_get_file_revs(svn_ra_session_t *session,
rev_pool = svn_pool_create(pool);
chunk_pool = svn_pool_create(pool);
+ path = reparent_path(session, path, pool);
SVN_ERR(svn_ra_svn__write_cmd_get_file_revs(sess_baton->conn, pool,
path, start, end,
include_merged_revisions));
@@ -2119,10 +2372,10 @@ static svn_error_t *ra_svn_get_file_revs(svn_ra_session_t *session,
while (1)
{
- apr_array_header_t *rev_proplist, *proplist;
+ svn_ra_svn__list_t *rev_proplist, *proplist;
apr_uint64_t merged_rev_param;
apr_array_header_t *props;
- svn_ra_svn_item_t *item;
+ svn_ra_svn__item_t *item;
apr_hash_t *rev_props;
svn_revnum_t rev;
const char *p;
@@ -2133,7 +2386,7 @@ static svn_error_t *ra_svn_get_file_revs(svn_ra_session_t *session,
svn_pool_clear(rev_pool);
svn_pool_clear(chunk_pool);
SVN_ERR(svn_ra_svn__read_item(sess_baton->conn, rev_pool, &item));
- if (item->kind == SVN_RA_SVN_WORD && strcmp(item->u.word, "done") == 0)
+ if (is_done_response(item))
break;
/* Either we've got a correct revision or we will error out below. */
had_revision = TRUE;
@@ -2141,7 +2394,7 @@ static svn_error_t *ra_svn_get_file_revs(svn_ra_session_t *session,
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Revision entry not a list"));
- SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, rev_pool,
+ SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list,
"crll?B", &p, &rev, &rev_proplist,
&proplist, &merged_rev_param));
p = svn_fspath__canonicalize(p, rev_pool);
@@ -2157,7 +2410,7 @@ static svn_error_t *ra_svn_get_file_revs(svn_ra_session_t *session,
if (item->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Text delta chunk not a string"));
- has_txdelta = item->u.string->len > 0;
+ has_txdelta = item->u.string.len > 0;
SVN_ERR(handler(handler_baton, p, rev, rev_props, merged_rev,
has_txdelta ? &d_handler : NULL, &d_baton,
@@ -2173,13 +2426,13 @@ static svn_error_t *ra_svn_get_file_revs(svn_ra_session_t *session,
rev_pool);
else
stream = NULL;
- while (item->u.string->len > 0)
+ while (item->u.string.len > 0)
{
apr_size_t size;
- size = item->u.string->len;
+ size = item->u.string.len;
if (stream)
- SVN_ERR(svn_stream_write(stream, item->u.string->data, &size));
+ SVN_ERR(svn_stream_write(stream, item->u.string.data, &size));
svn_pool_clear(chunk_pool);
SVN_ERR(svn_ra_svn__read_item(sess_baton->conn, chunk_pool,
@@ -2221,7 +2474,7 @@ static svn_error_t *ra_svn_lock_compat(svn_ra_session_t *session,
{
svn_ra_svn__session_baton_t *sess = session->priv;
svn_ra_svn_conn_t* conn = sess->conn;
- apr_array_header_t *list;
+ svn_ra_svn__list_t *list;
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(pool);
@@ -2292,7 +2545,7 @@ static svn_error_t *ra_svn_unlock_compat(svn_ra_session_t *session,
const void *key;
const char *path;
void *val;
- const char *token;
+ const svn_string_t *token;
svn_error_t *err, *callback_err = NULL;
svn_pool_clear(iterpool);
@@ -2300,7 +2553,7 @@ static svn_error_t *ra_svn_unlock_compat(svn_ra_session_t *session,
apr_hash_this(hi, &key, NULL, &val);
path = key;
if (strcmp(val, "") != 0)
- token = val;
+ token = svn_string_create(val, iterpool);
else
token = NULL;
@@ -2347,6 +2600,7 @@ static svn_error_t *ra_svn_lock(svn_ra_session_t *session,
svn_error_t *err;
apr_pool_t *iterpool = svn_pool_create(pool);
+ path_revs = reparent_path_hash(session, path_revs, pool, pool);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((?c)b(!", "lock-many",
comment, steal_lock));
@@ -2384,13 +2638,13 @@ static svn_error_t *ra_svn_lock(svn_ra_session_t *session,
/* Loop over responses to get lock information. */
for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi))
{
- svn_ra_svn_item_t *elt;
+ svn_ra_svn__item_t *elt;
const void *key;
const char *path;
svn_error_t *callback_err;
const char *status;
svn_lock_t *lock;
- apr_array_header_t *list;
+ svn_ra_svn__list_t *list;
apr_hash_this(hi, &key, NULL, NULL);
path = key;
@@ -2402,18 +2656,17 @@ static svn_error_t *ra_svn_lock(svn_ra_session_t *session,
the middle of the request list. If this happens, it will
transmit "done" to end the lock-info early, and then the
overall command response will talk about the fatal error. */
- if (elt->kind == SVN_RA_SVN_WORD && strcmp(elt->u.word, "done") == 0)
+ if (is_done_response(elt))
break;
if (elt->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Lock response not a list"));
- SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, iterpool, "wl", &status,
- &list));
+ SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "wl", &status, &list));
if (strcmp(status, "failure") == 0)
- err = svn_ra_svn__handle_failure_status(list, iterpool);
+ err = svn_ra_svn__handle_failure_status(list);
else if (strcmp(status, "success") == 0)
{
SVN_ERR(parse_lock(list, iterpool, &lock));
@@ -2440,10 +2693,10 @@ static svn_error_t *ra_svn_lock(svn_ra_session_t *session,
read the final "done" from the server. */
if (!hi)
{
- svn_ra_svn_item_t *elt;
+ svn_ra_svn__item_t *elt;
SVN_ERR(svn_ra_svn__read_item(conn, pool, &elt));
- if (elt->kind != SVN_RA_SVN_WORD || strcmp(elt->u.word, "done") != 0)
+ if (!is_done_response(elt))
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Didn't receive end marker for lock "
"responses"));
@@ -2472,6 +2725,7 @@ static svn_error_t *ra_svn_unlock(svn_ra_session_t *session,
svn_error_t *err;
const char *path;
+ path_tokens = reparent_path_hash(session, path_tokens, pool, pool);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(b(!", "unlock-many",
break_lock));
@@ -2513,11 +2767,11 @@ static svn_error_t *ra_svn_unlock(svn_ra_session_t *session,
/* Loop over responses to unlock files. */
for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
{
- svn_ra_svn_item_t *elt;
+ svn_ra_svn__item_t *elt;
const void *key;
svn_error_t *callback_err;
const char *status;
- apr_array_header_t *list;
+ svn_ra_svn__list_t *list;
svn_pool_clear(iterpool);
@@ -2527,7 +2781,7 @@ static svn_error_t *ra_svn_unlock(svn_ra_session_t *session,
the middle of the request list. If this happens, it will
transmit "done" to end the lock-info early, and then the
overall command response will talk about the fatal error. */
- if (elt->kind == SVN_RA_SVN_WORD && (strcmp(elt->u.word, "done") == 0))
+ if (is_done_response(elt))
break;
apr_hash_this(hi, &key, NULL, NULL);
@@ -2537,14 +2791,13 @@ static svn_error_t *ra_svn_unlock(svn_ra_session_t *session,
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Unlock response not a list"));
- SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, iterpool, "wl", &status,
- &list));
+ SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "wl", &status, &list));
if (strcmp(status, "failure") == 0)
- err = svn_ra_svn__handle_failure_status(list, iterpool);
+ err = svn_ra_svn__handle_failure_status(list);
else if (strcmp(status, "success") == 0)
{
- SVN_ERR(svn_ra_svn__parse_tuple(list, iterpool, "c", &path));
+ SVN_ERR(svn_ra_svn__parse_tuple(list, "c", &path));
err = SVN_NO_ERROR;
}
else
@@ -2567,10 +2820,10 @@ static svn_error_t *ra_svn_unlock(svn_ra_session_t *session,
read the final "done" from the server. */
if (!hi)
{
- svn_ra_svn_item_t *elt;
+ svn_ra_svn__item_t *elt;
SVN_ERR(svn_ra_svn__read_item(conn, pool, &elt));
- if (elt->kind != SVN_RA_SVN_WORD || strcmp(elt->u.word, "done") != 0)
+ if (! is_done_response(elt))
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Didn't receive end marker for unlock "
"responses"));
@@ -2590,8 +2843,9 @@ static svn_error_t *ra_svn_get_lock(svn_ra_session_t *session,
{
svn_ra_svn__session_baton_t *sess = session->priv;
svn_ra_svn_conn_t* conn = sess->conn;
- apr_array_header_t *list;
+ svn_ra_svn__list_t *list;
+ path = reparent_path(session, path, pool);
SVN_ERR(svn_ra_svn__write_cmd_get_lock(conn, pool, path));
/* Servers before 1.2 doesn't support locking. Check this here. */
@@ -2635,12 +2889,13 @@ static svn_error_t *ra_svn_get_locks(svn_ra_session_t *session,
{
svn_ra_svn__session_baton_t *sess = session->priv;
svn_ra_svn_conn_t* conn = sess->conn;
- apr_array_header_t *list;
+ svn_ra_svn__list_t *list;
const char *full_url, *abs_path;
int i;
/* Figure out the repository abspath from PATH. */
- full_url = svn_path_url_add_component2(sess->url, path, pool);
+ full_url = svn_path_url_add_component2(sess->parent->client_url->data,
+ path, pool);
SVN_ERR(path_relative_to_root(session, &abs_path, full_url, pool));
abs_path = svn_fspath__canonicalize(abs_path, pool);
@@ -2658,12 +2913,12 @@ static svn_error_t *ra_svn_get_locks(svn_ra_session_t *session,
for (i = 0; i < list->nelts; ++i)
{
svn_lock_t *lock;
- svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
+ 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,
_("Lock element not a list"));
- SVN_ERR(parse_lock(elt->u.list, pool, &lock));
+ SVN_ERR(parse_lock(&elt->u.list, pool, &lock));
/* Filter out unwanted paths. Since Subversion only allows
locks on files, we can treat depth=immediates the same as
@@ -2701,6 +2956,9 @@ static svn_error_t *ra_svn_replay(svn_ra_session_t *session,
{
svn_ra_svn__session_baton_t *sess = session->priv;
+ /* Complex EDITOR callbacks may rely on client and server parent path
+ being in sync. */
+ SVN_ERR(ensure_exact_server_parent(session, pool));
SVN_ERR(svn_ra_svn__write_cmd_replay(sess->conn, pool, revision,
low_water_mark, send_deltas));
@@ -2731,6 +2989,9 @@ ra_svn_replay_range(svn_ra_session_t *session,
svn_revnum_t rev;
svn_boolean_t drive_aborted = FALSE;
+ /* Complex EDITOR callbacks may rely on client and server parent path
+ being in sync. */
+ SVN_ERR(ensure_exact_server_parent(session, pool));
SVN_ERR(svn_ra_svn__write_cmd_replay_range(sess->conn, pool,
start_revision, end_revision,
low_water_mark, send_deltas));
@@ -2746,16 +3007,22 @@ ra_svn_replay_range(svn_ra_session_t *session,
void *edit_baton;
apr_hash_t *rev_props;
const char *word;
- apr_array_header_t *list;
+ svn_ra_svn__list_t *list;
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_svn__read_tuple(sess->conn, iterpool,
"wl", &word, &list));
+
if (strcmp(word, "revprops") != 0)
- return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
- _("Expected 'revprops', found '%s'"),
- word);
+ {
+ if (strcmp(word, "failure") == 0)
+ SVN_ERR(svn_ra_svn__handle_failure_status(list));
+
+ return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+ _("Expected 'revprops', found '%s'"),
+ word);
+ }
SVN_ERR(svn_ra_svn__parse_proplist(list, iterpool, &rev_props));
@@ -2805,6 +3072,7 @@ ra_svn_has_capability(svn_ra_session_t *session,
SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS},
{SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE},
+ {SVN_RA_CAPABILITY_LIST, SVN_RA_SVN_CAP_LIST},
{NULL, NULL} /* End of list marker */
};
@@ -2838,6 +3106,8 @@ ra_svn_get_deleted_rev(svn_ra_session_t *session,
svn_ra_svn__session_baton_t *sess_baton = session->priv;
svn_ra_svn_conn_t *conn = sess_baton->conn;
+ path = reparent_path(session, path, pool);
+
/* Transmit the parameters. */
SVN_ERR(svn_ra_svn__write_cmd_get_deleted_rev(conn, pool, path,
peg_revision, end_revision));
@@ -2872,9 +3142,10 @@ ra_svn_get_inherited_props(svn_ra_session_t *session,
{
svn_ra_svn__session_baton_t *sess_baton = session->priv;
svn_ra_svn_conn_t *conn = sess_baton->conn;
- apr_array_header_t *iproplist;
+ svn_ra_svn__list_t *iproplist;
svn_boolean_t iprop_capable;
+ path = reparent_path(session, path, scratch_pool);
SVN_ERR(ra_svn_has_capability(session, &iprop_capable,
SVN_RA_CAPABILITY_INHERITED_PROPS,
scratch_pool));
@@ -2894,6 +3165,83 @@ ra_svn_get_inherited_props(svn_ra_session_t *session,
return SVN_NO_ERROR;
}
+static svn_error_t *
+ra_svn_list(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ const apr_array_header_t *patterns,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_ra_dirent_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_svn__session_baton_t *sess_baton = session->priv;
+ svn_ra_svn_conn_t *conn = sess_baton->conn;
+ int i;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+ path = reparent_path(session, path, scratch_pool);
+
+ /* Send the list request. */
+ SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w(c(?r)w(!", "list",
+ path, revision, svn_depth_to_word(depth)));
+ SVN_ERR(send_dirent_fields(conn, dirent_fields, scratch_pool));
+
+ if (patterns)
+ {
+ SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "!)(!"));
+
+ for (i = 0; i < patterns->nelts; ++i)
+ {
+ const char *pattern = APR_ARRAY_IDX(patterns, i, const char *);
+ SVN_ERR(svn_ra_svn__write_cstring(conn, scratch_pool, pattern));
+ }
+ }
+
+ SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "!))"));
+
+ /* Handle auth request by server */
+ SVN_ERR(handle_auth_request(sess_baton, scratch_pool));
+
+ /* Read and process list response. */
+ while (1)
+ {
+ svn_ra_svn__item_t *item;
+ const char *dirent_path;
+ const char *kind_word, *date;
+ svn_dirent_t dirent = { 0 };
+
+ svn_pool_clear(iterpool);
+
+ /* Read the next dirent or bail out on "done", respectively */
+ SVN_ERR(svn_ra_svn__read_item(conn, iterpool, &item));
+ if (is_done_response(item))
+ break;
+ if (item->kind != SVN_RA_SVN_LIST)
+ return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+ _("List entry not a list"));
+ SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list,
+ "cw?(?n)(?b)(?r)(?c)(?c)",
+ &dirent_path, &kind_word, &dirent.size,
+ &dirent.has_props, &dirent.created_rev,
+ &date, &dirent.last_author));
+
+ /* Convert data. */
+ dirent.kind = svn_node_kind_from_word(kind_word);
+ if (date)
+ SVN_ERR(svn_time_from_cstring(&dirent.time, date, iterpool));
+
+ /* Invoke RECEIVER */
+ SVN_ERR(receiver(dirent_path, &dirent, receiver_baton, iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ /* Read the actual command response. */
+ SVN_ERR(svn_ra_svn__read_cmd_response(conn, scratch_pool, ""));
+ return SVN_NO_ERROR;
+}
+
static const svn_ra__vtable_t ra_svn_vtable = {
svn_ra_svn_version,
ra_svn_get_description,
@@ -2931,8 +3279,12 @@ static const svn_ra__vtable_t ra_svn_vtable = {
ra_svn_has_capability,
ra_svn_replay_range,
ra_svn_get_deleted_rev,
+ ra_svn_get_inherited_props,
+ NULL /* ra_set_svn_ra_open */,
+ ra_svn_list,
ra_svn_register_editor_shim_callbacks,
- ra_svn_get_inherited_props
+ NULL /* commit_ev2 */,
+ NULL /* replay_range_ev2 */
};
svn_error_t *
diff --git a/subversion/libsvn_ra_svn/cram.c b/subversion/libsvn_ra_svn/cram.c
index b92f37b4a1727..0a694b46d027e 100644
--- a/subversion/libsvn_ra_svn/cram.c
+++ b/subversion/libsvn_ra_svn/cram.c
@@ -140,7 +140,7 @@ svn_error_t *svn_ra_svn_cram_server(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
char hostbuf[APRMAXHOSTLEN + 1];
unsigned char cdigest[APR_MD5_DIGESTSIZE], sdigest[APR_MD5_DIGESTSIZE];
const char *challenge, *sep, *password;
- svn_ra_svn_item_t *item;
+ svn_ra_svn__item_t *item;
svn_string_t *resp;
*success = FALSE;
@@ -160,7 +160,7 @@ svn_error_t *svn_ra_svn_cram_server(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
if (item->kind != SVN_RA_SVN_STRING) /* Very wrong; don't report failure */
return SVN_NO_ERROR;
- resp = item->u.string;
+ resp = &item->u.string;
sep = strrchr(resp->data, ' ');
if (!sep || resp->len - (sep + 1 - resp->data) != APR_MD5_DIGESTSIZE * 2
|| !hex_decode(cdigest, sep + 1))
diff --git a/subversion/libsvn_ra_svn/cyrus_auth.c b/subversion/libsvn_ra_svn/cyrus_auth.c
index ac94a485a44fd..66cefa66cc41f 100644
--- a/subversion/libsvn_ra_svn/cyrus_auth.c
+++ b/subversion/libsvn_ra_svn/cyrus_auth.c
@@ -68,7 +68,7 @@ static apr_status_t sasl_done_cb(void *data)
apr_initialize()/apr_terminate() more than once. */
svn_ra_svn__sasl_status = 0;
if (svn_atomic_dec(&sasl_ctx_count) == 0)
- sasl_done();
+ svn_sasl__done();
return APR_SUCCESS;
}
@@ -174,10 +174,10 @@ svn_ra_svn__sasl_common_init(apr_pool_t *pool)
apr_pool_cleanup_register(sasl_pool, NULL, sasl_done_cb,
apr_pool_cleanup_null);
#if APR_HAS_THREADS
- sasl_set_mutex(sasl_mutex_alloc_cb,
- sasl_mutex_lock_cb,
- sasl_mutex_unlock_cb,
- sasl_mutex_free_cb);
+ svn_sasl__set_mutex(sasl_mutex_alloc_cb,
+ sasl_mutex_lock_cb,
+ sasl_mutex_unlock_cb,
+ sasl_mutex_free_cb);
free_mutexes = apr_array_make(sasl_pool, 0, sizeof(svn_mutex__t *));
SVN_ERR(svn_mutex__init(&array_mutex, TRUE, sasl_pool));
@@ -223,7 +223,7 @@ get_sasl_error(sasl_conn_t *sasl_ctx, int result, apr_pool_t *result_pool)
return apr_psprintf(result_pool,
_("SASL authentication error: %s%s"),
- sasl_errdetail(sasl_ctx), sasl_errno_msg);
+ svn_sasl__errdetail(sasl_ctx), sasl_errno_msg);
}
static svn_error_t *sasl_init_cb(void *baton, apr_pool_t *pool)
@@ -232,7 +232,7 @@ static svn_error_t *sasl_init_cb(void *baton, apr_pool_t *pool)
SVN_ERR(svn_ra_svn__sasl_common_init(pool));
clear_sasl_errno();
- result = sasl_client_init(NULL);
+ result = svn_sasl__client_init(NULL);
if (result != SASL_OK)
{
const char *sasl_errno_msg = get_sasl_errno_msg(result, pool);
@@ -240,7 +240,7 @@ static svn_error_t *sasl_init_cb(void *baton, apr_pool_t *pool)
return svn_error_createf
(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
_("Could not initialized the SASL library: %s%s"),
- sasl_errstring(result, NULL, NULL),
+ svn_sasl__errstring(result, NULL, NULL),
sasl_errno_msg);
}
@@ -257,9 +257,9 @@ svn_error_t *svn_ra_svn__sasl_init(void)
static apr_status_t sasl_dispose_cb(void *data)
{
sasl_conn_t *sasl_ctx = data;
- sasl_dispose(&sasl_ctx);
+ svn_sasl__dispose(&sasl_ctx);
if (svn_atomic_dec(&sasl_ctx_count) == 0)
- sasl_done();
+ svn_sasl__done();
return APR_SUCCESS;
}
@@ -403,17 +403,17 @@ static svn_error_t *new_sasl_ctx(sasl_conn_t **sasl_ctx,
int result;
clear_sasl_errno();
- result = sasl_client_new(SVN_RA_SVN_SASL_NAME,
- hostname, local_addrport, remote_addrport,
- callbacks, SASL_SUCCESS_DATA,
- sasl_ctx);
+ result = svn_sasl__client_new(SVN_RA_SVN_SASL_NAME,
+ hostname, local_addrport, remote_addrport,
+ callbacks, SASL_SUCCESS_DATA,
+ sasl_ctx);
if (result != SASL_OK)
{
const char *sasl_errno_msg = get_sasl_errno_msg(result, pool);
return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
_("Could not create SASL context: %s%s"),
- sasl_errstring(result, NULL, NULL),
+ svn_sasl__errstring(result, NULL, NULL),
sasl_errno_msg);
}
svn_atomic_inc(&sasl_ctx_count);
@@ -427,8 +427,8 @@ static svn_error_t *new_sasl_ctx(sasl_conn_t **sasl_ctx,
should be the username, but since SASL doesn't seem
to use it on the client side, any non-empty string will do. */
clear_sasl_errno();
- result = sasl_setprop(*sasl_ctx,
- SASL_AUTH_EXTERNAL, " ");
+ result = svn_sasl__setprop(*sasl_ctx,
+ SASL_AUTH_EXTERNAL, " ");
if (result != SASL_OK)
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
get_sasl_error(*sasl_ctx, result, pool));
@@ -436,7 +436,7 @@ static svn_error_t *new_sasl_ctx(sasl_conn_t **sasl_ctx,
/* Set security properties. */
svn_ra_svn__default_secprops(&secprops);
- sasl_setprop(*sasl_ctx, SASL_SEC_PROPS, &secprops);
+ svn_sasl__setprop(*sasl_ctx, SASL_SEC_PROPS, &secprops);
return SVN_NO_ERROR;
}
@@ -460,12 +460,12 @@ static svn_error_t *try_auth(svn_ra_svn__session_baton_t *sess,
{
again = FALSE;
clear_sasl_errno();
- result = sasl_client_start(sasl_ctx,
- mechstring,
- &client_interact,
- &out,
- &outlen,
- &mech);
+ result = svn_sasl__client_start(sasl_ctx,
+ mechstring,
+ &client_interact,
+ &out,
+ &outlen,
+ &mech);
switch (result)
{
case SASL_OK:
@@ -531,12 +531,12 @@ static svn_error_t *try_auth(svn_ra_svn__session_baton_t *sess,
in = svn_base64_decode_string(in, pool);
clear_sasl_errno();
- result = sasl_client_step(sasl_ctx,
- in->data,
- (const unsigned int) in->len,
- &client_interact,
- &out, /* Filled in by SASL. */
- &outlen);
+ result = svn_sasl__client_step(sasl_ctx,
+ in->data,
+ (const unsigned int) in->len,
+ &client_interact,
+ &out, /* Filled in by SASL. */
+ &outlen);
if (result != SASL_OK && result != SASL_CONTINUE)
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
@@ -620,9 +620,9 @@ static svn_error_t *sasl_read_cb(void *baton, char *buffer, apr_size_t *len)
return SVN_NO_ERROR;
}
clear_sasl_errno();
- result = sasl_decode(sasl_baton->ctx, buffer, (unsigned int) len2,
- &sasl_baton->read_buf,
- &sasl_baton->read_len);
+ result = svn_sasl__decode(sasl_baton->ctx, buffer, (unsigned int) len2,
+ &sasl_baton->read_buf,
+ &sasl_baton->read_len);
if (result != SASL_OK)
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
get_sasl_error(sasl_baton->ctx, result,
@@ -662,9 +662,9 @@ sasl_write_cb(void *baton, const char *buffer, apr_size_t *len)
/* Make sure we don't write too much. */
*len = (*len > sasl_baton->maxsize) ? sasl_baton->maxsize : *len;
clear_sasl_errno();
- result = sasl_encode(sasl_baton->ctx, buffer, (unsigned int) *len,
- &sasl_baton->write_buf,
- &sasl_baton->write_len);
+ result = svn_sasl__encode(sasl_baton->ctx, buffer, (unsigned int) *len,
+ &sasl_baton->write_buf,
+ &sasl_baton->write_len);
if (result != SASL_OK)
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
@@ -725,7 +725,7 @@ svn_error_t *svn_ra_svn__enable_sasl_encryption(svn_ra_svn_conn_t *conn,
/* Get the strength of the security layer. */
clear_sasl_errno();
- result = sasl_getprop(sasl_ctx, SASL_SSF, (void*) &ssfp);
+ result = svn_sasl__getprop(sasl_ctx, SASL_SSF, (void*) &ssfp);
if (result != SASL_OK)
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
get_sasl_error(sasl_ctx, result, pool));
@@ -745,7 +745,7 @@ svn_error_t *svn_ra_svn__enable_sasl_encryption(svn_ra_svn_conn_t *conn,
/* Find out the maximum input size for sasl_encode. */
clear_sasl_errno();
- result = sasl_getprop(sasl_ctx, SASL_MAXOUTBUF, &maxsize);
+ result = svn_sasl__getprop(sasl_ctx, SASL_MAXOUTBUF, &maxsize);
if (result != SASL_OK)
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
get_sasl_error(sasl_ctx, result, pool));
@@ -756,9 +756,10 @@ svn_error_t *svn_ra_svn__enable_sasl_encryption(svn_ra_svn_conn_t *conn,
if (conn->read_end > conn->read_ptr)
{
clear_sasl_errno();
- result = sasl_decode(sasl_ctx, conn->read_ptr,
- (unsigned int) (conn->read_end - conn->read_ptr),
- &sasl_baton->read_buf, &sasl_baton->read_len);
+ result = svn_sasl__decode(
+ sasl_ctx, conn->read_ptr,
+ (unsigned int) (conn->read_end - conn->read_ptr),
+ &sasl_baton->read_buf, &sasl_baton->read_len);
if (result != SASL_OK)
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
get_sasl_error(sasl_ctx, result, pool));
@@ -828,7 +829,7 @@ svn_error_t *svn_ra_svn__get_addresses(const char **local_addrport,
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)
{
apr_pool_t *subpool;
@@ -856,11 +857,11 @@ svn_ra_svn__do_cyrus_auth(svn_ra_svn__session_baton_t *sess,
/* Create a string containing the list of mechanisms, separated by spaces. */
for (i = 0; i < mechlist->nelts; i++)
{
- svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(mechlist, i, svn_ra_svn_item_t);
+ svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(mechlist, i);
mechstring = apr_pstrcat(pool,
mechstring,
i == 0 ? "" : " ",
- elt->u.word, SVN_VA_NULL);
+ elt->u.word.data, SVN_VA_NULL);
}
}
diff --git a/subversion/libsvn_ra_svn/deprecated.c b/subversion/libsvn_ra_svn/deprecated.c
index 7f0c8fde2edce..a4ff18e180e1d 100644
--- a/subversion/libsvn_ra_svn/deprecated.c
+++ b/subversion/libsvn_ra_svn/deprecated.c
@@ -110,7 +110,12 @@ svn_ra_svn_read_item(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
svn_ra_svn_item_t **item)
{
- return svn_error_trace(svn_ra_svn__read_item(conn, pool, item));
+ svn_ra_svn__item_t *temp;
+ SVN_ERR(svn_ra_svn__read_item(conn, pool, &temp));
+ *item = apr_pcalloc(pool, sizeof(**item));
+ svn_ra_svn__to_public_item(*item, temp, pool);
+
+ return SVN_NO_ERROR;
}
svn_error_t *
@@ -127,9 +132,10 @@ svn_ra_svn_parse_tuple(const apr_array_header_t *list,
{
va_list va;
svn_error_t *err;
+ svn_ra_svn__list_t *internal = svn_ra_svn__to_private_array(list, pool);
va_start(va, fmt);
- err = svn_ra_svn__parse_tuple(list, pool, fmt, va);
+ err = svn_ra_svn__parse_tuple(internal, fmt, va);
va_end(va);
return svn_error_trace(err);
@@ -155,7 +161,9 @@ svn_ra_svn_parse_proplist(const apr_array_header_t *list,
apr_pool_t *pool,
apr_hash_t **props)
{
- return svn_error_trace(svn_ra_svn__parse_proplist(list, pool, props));
+ svn_ra_svn__list_t *internal
+ = svn_ra_svn__to_private_array(list, pool);
+ return svn_error_trace(svn_ra_svn__parse_proplist(internal, pool, props));
}
svn_error_t *
@@ -180,8 +188,23 @@ svn_ra_svn_handle_commands2(svn_ra_svn_conn_t *conn,
void *baton,
svn_boolean_t error_on_disconnect)
{
+ apr_size_t i, count = 0;
+ svn_ra_svn__cmd_entry_t *internal;
+
+ while (commands[count].cmdname)
+ count++;
+
+ internal = apr_pcalloc(pool, count * sizeof(*internal));
+ for (i = 0; i < count; ++i)
+ {
+ internal[i].cmdname = commands[i].cmdname;
+ internal[i].handler = NULL;
+ internal[i].deprecated_handler = commands[i].handler;
+ internal[i].terminate = commands[i].terminate;
+ }
+
return svn_error_trace(svn_ra_svn__handle_commands2(conn, pool,
- commands, baton,
+ internal, baton,
error_on_disconnect));
}
@@ -191,9 +214,9 @@ svn_ra_svn_handle_commands(svn_ra_svn_conn_t *conn,
const svn_ra_svn_cmd_entry_t *commands,
void *baton)
{
- return svn_error_trace(svn_ra_svn__handle_commands2(conn, pool,
- commands, baton,
- FALSE));
+ return svn_error_trace(svn_ra_svn_handle_commands2(conn, pool,
+ commands, baton,
+ FALSE));
}
svn_error_t *
@@ -239,6 +262,20 @@ svn_ra_svn_write_cmd_failure(svn_ra_svn_conn_t *conn,
/* From marshal.c */
svn_ra_svn_conn_t *
+svn_ra_svn_create_conn4(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_pool_t *pool)
+{
+ return svn_ra_svn_create_conn5(sock, in_stream, out_stream,
+ compression_level, zero_copy_limit,
+ error_check_interval, 0, 0, pool);
+}
+
+svn_ra_svn_conn_t *
svn_ra_svn_create_conn3(apr_socket_t *sock,
apr_file_t *in_file,
apr_file_t *out_file,
diff --git a/subversion/libsvn_ra_svn/editorp.c b/subversion/libsvn_ra_svn/editorp.c
index 88af89859fc48..10c8a2b04a63d 100644
--- a/subversion/libsvn_ra_svn/editorp.c
+++ b/subversion/libsvn_ra_svn/editorp.c
@@ -39,8 +39,11 @@
#include "svn_pools.h"
#include "svn_private_config.h"
+#include "private/svn_atomic.h"
#include "private/svn_fspath.h"
#include "private/svn_editor.h"
+#include "private/svn_string_private.h"
+#include "private/svn_subr_private.h"
#include "ra_svn.h"
@@ -57,7 +60,7 @@ typedef struct ra_svn_edit_baton_t {
svn_ra_svn_conn_t *conn;
svn_ra_svn_edit_callback callback; /* Called on successful completion. */
void *callback_baton;
- int next_token;
+ apr_uint64_t next_token;
svn_boolean_t got_status;
} ra_svn_edit_baton_t;
@@ -66,13 +69,19 @@ typedef struct ra_svn_baton_t {
svn_ra_svn_conn_t *conn;
apr_pool_t *pool;
ra_svn_edit_baton_t *eb;
- const char *token;
+ svn_string_t *token;
} ra_svn_baton_t;
+/* Forward declaration. */
+typedef struct ra_svn_token_entry_t ra_svn_token_entry_t;
+
typedef struct ra_svn_driver_state_t {
const svn_delta_editor_t *editor;
void *edit_baton;
apr_hash_t *tokens;
+
+ /* Entry for the last token seen. May be NULL. */
+ ra_svn_token_entry_t *last_token;
svn_boolean_t *aborted;
svn_boolean_t done;
apr_pool_t *pool;
@@ -90,26 +99,33 @@ typedef struct ra_svn_driver_state_t {
field in this structure is vestigial for files, and we use it for a
different purpose instead: at apply-textdelta time, we set it to a
subpool of the file pool, which is destroyed in textdelta-end. */
-typedef struct ra_svn_token_entry_t {
+struct ra_svn_token_entry_t {
svn_string_t *token;
void *baton;
svn_boolean_t is_file;
svn_stream_t *dstream; /* svndiff stream for apply_textdelta */
apr_pool_t *pool;
-} ra_svn_token_entry_t;
+};
/* --- CONSUMING AN EDITOR BY PASSING EDIT OPERATIONS OVER THE NET --- */
-static const char *make_token(char type, ra_svn_edit_baton_t *eb,
- apr_pool_t *pool)
+static svn_string_t *
+make_token(char type,
+ ra_svn_edit_baton_t *eb,
+ apr_pool_t *pool)
{
- return apr_psprintf(pool, "%c%d", type, eb->next_token++);
+ apr_size_t len;
+ char buffer[1 + SVN_INT64_BUFFER_SIZE];
+ buffer[0] = type;
+ len = 1 + svn__ui64toa(&buffer[1], eb->next_token++);
+
+ return svn_string_ncreate(buffer, len, pool);
}
static ra_svn_baton_t *ra_svn_make_baton(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
ra_svn_edit_baton_t *eb,
- const char *token)
+ svn_string_t *token)
{
ra_svn_baton_t *b;
@@ -171,7 +187,7 @@ static svn_error_t *ra_svn_open_root(void *edit_baton, svn_revnum_t rev,
apr_pool_t *pool, void **root_baton)
{
ra_svn_edit_baton_t *eb = edit_baton;
- const char *token = make_token('d', eb, pool);
+ svn_string_t *token = make_token('d', eb, pool);
SVN_ERR(check_for_error(eb, pool));
SVN_ERR(svn_ra_svn__write_cmd_open_root(eb->conn, pool, rev, token));
@@ -196,7 +212,7 @@ static svn_error_t *ra_svn_add_dir(const char *path, void *parent_baton,
apr_pool_t *pool, void **child_baton)
{
ra_svn_baton_t *b = parent_baton;
- const char *token = make_token('d', b->eb, pool);
+ svn_string_t *token = make_token('d', b->eb, pool);
SVN_ERR_ASSERT((copy_path && SVN_IS_VALID_REVNUM(copy_rev))
|| (!copy_path && !SVN_IS_VALID_REVNUM(copy_rev)));
@@ -212,7 +228,7 @@ static svn_error_t *ra_svn_open_dir(const char *path, void *parent_baton,
void **child_baton)
{
ra_svn_baton_t *b = parent_baton;
- const char *token = make_token('d', b->eb, pool);
+ svn_string_t *token = make_token('d', b->eb, pool);
SVN_ERR(check_for_error(b->eb, pool));
SVN_ERR(svn_ra_svn__write_cmd_open_dir(b->conn, pool, path, b->token,
@@ -265,7 +281,7 @@ static svn_error_t *ra_svn_add_file(const char *path,
void **file_baton)
{
ra_svn_baton_t *b = parent_baton;
- const char *token = make_token('c', b->eb, pool);
+ svn_string_t *token = make_token('c', b->eb, pool);
SVN_ERR_ASSERT((copy_path && SVN_IS_VALID_REVNUM(copy_rev))
|| (!copy_path && !SVN_IS_VALID_REVNUM(copy_rev)));
@@ -283,7 +299,7 @@ static svn_error_t *ra_svn_open_file(const char *path,
void **file_baton)
{
ra_svn_baton_t *b = parent_baton;
- const char *token = make_token('c', b->eb, pool);
+ svn_string_t *token = make_token('c', b->eb, pool);
SVN_ERR(check_for_error(b->eb, b->pool));
SVN_ERR(svn_ra_svn__write_cmd_open_file(b->conn, pool, path, b->token,
@@ -335,15 +351,9 @@ static svn_error_t *ra_svn_apply_textdelta(void *file_baton,
svn_stream_set_write(diff_stream, ra_svn_svndiff_handler);
svn_stream_set_close(diff_stream, ra_svn_svndiff_close_handler);
- /* If the connection does not support SVNDIFF1 or if we don't want to use
- * compression, use the non-compressing "version 0" implementation */
- if ( svn_ra_svn_compression_level(b->conn) > 0
- && svn_ra_svn_has_capability(b->conn, SVN_RA_SVN_CAP_SVNDIFF1))
- svn_txdelta_to_svndiff3(wh, wh_baton, diff_stream, 1,
- b->conn->compression_level, pool);
- else
- svn_txdelta_to_svndiff3(wh, wh_baton, diff_stream, 0,
- b->conn->compression_level, pool);
+ svn_txdelta_to_svndiff3(wh, wh_baton, diff_stream,
+ svn_ra_svn__svndiff_version(b->conn),
+ b->conn->compression_level, pool);
return SVN_NO_ERROR;
}
@@ -480,6 +490,7 @@ static ra_svn_token_entry_t *store_token(ra_svn_driver_state_t *ds,
entry->pool = pool;
apr_hash_set(ds->tokens, entry->token->data, entry->token->len, entry);
+ ds->last_token = entry;
return entry;
}
@@ -489,36 +500,59 @@ static svn_error_t *lookup_token(ra_svn_driver_state_t *ds,
svn_boolean_t is_file,
ra_svn_token_entry_t **entry)
{
- *entry = apr_hash_get(ds->tokens, token->data, token->len);
+ if (ds->last_token && svn_string_compare(ds->last_token->token, token))
+ {
+ *entry = ds->last_token;
+ }
+ else
+ {
+ *entry = apr_hash_get(ds->tokens, token->data, token->len);
+ ds->last_token = *entry;
+ }
+
if (!*entry || (*entry)->is_file != is_file)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Invalid file or dir token during edit"));
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_target_rev(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+/* Remove a TOKEN entry from DS. */
+static void remove_token(ra_svn_driver_state_t *ds,
+ svn_string_t *token)
+{
+ apr_hash_set(ds->tokens, token->data, token->len, NULL);
+
+ /* Reset this unconditionally. In most cases, LAST_TOKEN->TOKEN will
+ match TOKEN anyway and if it doesn't, lookup_token() will suffer only
+ a minor performance hit. */
+ ds->last_token = NULL;
+}
+
+static svn_error_t *
+ra_svn_handle_target_rev(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
svn_revnum_t rev;
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "r", &rev));
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "r", &rev));
SVN_CMD_ERR(ds->editor->set_target_revision(ds->edit_baton, rev, pool));
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_open_root(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_open_root(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
svn_revnum_t rev;
apr_pool_t *subpool;
svn_string_t *token;
void *root_baton;
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)s", &rev, &token));
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)s", &rev, &token));
subpool = svn_pool_create(ds->pool);
SVN_CMD_ERR(ds->editor->open_root(ds->edit_baton, rev, subpool,
&root_baton));
@@ -526,17 +560,18 @@ static svn_error_t *ra_svn_handle_open_root(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_delete_entry(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_delete_entry(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
const char *path;
svn_string_t *token;
svn_revnum_t rev;
ra_svn_token_entry_t *entry;
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)s",
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)s",
&path, &rev, &token));
SVN_ERR(lookup_token(ds, token, FALSE, &entry));
path = svn_relpath_canonicalize(path, pool);
@@ -544,10 +579,11 @@ static svn_error_t *ra_svn_handle_delete_entry(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
const char *path, *copy_path;
svn_string_t *token, *child_token;
@@ -556,7 +592,7 @@ static svn_error_t *ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *subpool;
void *child_baton;
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?cr)", &path, &token,
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "css(?cr)", &path, &token,
&child_token, &copy_path, &copy_rev));
SVN_ERR(lookup_token(ds, token, FALSE, &entry));
subpool = svn_pool_create(entry->pool);
@@ -578,10 +614,11 @@ static svn_error_t *ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
const char *path;
svn_string_t *token, *child_token;
@@ -590,7 +627,7 @@ static svn_error_t *ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *subpool;
void *child_baton;
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?r)", &path, &token,
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "css(?r)", &path, &token,
&child_token, &rev));
SVN_ERR(lookup_token(ds, token, FALSE, &entry));
subpool = svn_pool_create(entry->pool);
@@ -601,17 +638,18 @@ static svn_error_t *ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_change_dir_prop(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_change_dir_prop(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
svn_string_t *token;
const char *name;
svn_string_t *value;
ra_svn_token_entry_t *entry;
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "sc(?s)", &token, &name,
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "sc(?s)", &token, &name,
&value));
SVN_ERR(lookup_token(ds, token, FALSE, &entry));
SVN_CMD_ERR(ds->editor->change_dir_prop(entry->baton, name, value,
@@ -619,36 +657,38 @@ static svn_error_t *ra_svn_handle_change_dir_prop(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_close_dir(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_close_dir(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
svn_string_t *token;
ra_svn_token_entry_t *entry;
/* Parse and look up the directory token. */
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s", &token));
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "s", &token));
SVN_ERR(lookup_token(ds, token, FALSE, &entry));
/* Close the directory and destroy the baton. */
SVN_CMD_ERR(ds->editor->close_directory(entry->baton, pool));
- apr_hash_set(ds->tokens, token->data, token->len, NULL);
+ remove_token(ds, token);
svn_pool_destroy(entry->pool);
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_absent_dir(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_absent_dir(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
const char *path;
svn_string_t *token;
ra_svn_token_entry_t *entry;
/* Parse parameters and look up the directory token. */
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &path, &token));
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "cs", &path, &token));
SVN_ERR(lookup_token(ds, token, FALSE, &entry));
/* Call the editor. */
@@ -656,17 +696,18 @@ static svn_error_t *ra_svn_handle_absent_dir(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_add_file(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_add_file(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
const char *path, *copy_path;
svn_string_t *token, *file_token;
svn_revnum_t copy_rev;
ra_svn_token_entry_t *entry, *file_entry;
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?cr)", &path, &token,
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "css(?cr)", &path, &token,
&file_token, &copy_path, &copy_rev));
SVN_ERR(lookup_token(ds, token, FALSE, &entry));
ds->file_refs++;
@@ -691,17 +732,18 @@ static svn_error_t *ra_svn_handle_add_file(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_open_file(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_open_file(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
const char *path;
svn_string_t *token, *file_token;
svn_revnum_t rev;
ra_svn_token_entry_t *entry, *file_entry;
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?r)", &path, &token,
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "css(?r)", &path, &token,
&file_token, &rev));
SVN_ERR(lookup_token(ds, token, FALSE, &entry));
ds->file_refs++;
@@ -716,10 +758,11 @@ static svn_error_t *ra_svn_handle_open_file(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
svn_string_t *token;
ra_svn_token_entry_t *entry;
@@ -728,7 +771,7 @@ static svn_error_t *ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn,
char *base_checksum;
/* Parse arguments and look up the token. */
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s(?c)",
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "s(?c)",
&token, &base_checksum));
SVN_ERR(lookup_token(ds, token, TRUE, &entry));
if (entry->dstream)
@@ -741,17 +784,18 @@ static svn_error_t *ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_textdelta_chunk(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_textdelta_chunk(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
svn_string_t *token;
ra_svn_token_entry_t *entry;
svn_string_t *str;
/* Parse arguments and look up the token. */
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ss", &token, &str));
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "ss", &token, &str));
SVN_ERR(lookup_token(ds, token, TRUE, &entry));
if (!entry->dstream)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
@@ -760,16 +804,17 @@ static svn_error_t *ra_svn_handle_textdelta_chunk(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_textdelta_end(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_textdelta_end(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
svn_string_t *token;
ra_svn_token_entry_t *entry;
/* Parse arguments and look up the token. */
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s", &token));
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "s", &token));
SVN_ERR(lookup_token(ds, token, TRUE, &entry));
if (!entry->dstream)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
@@ -780,55 +825,58 @@ static svn_error_t *ra_svn_handle_textdelta_end(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_change_file_prop(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_change_file_prop(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
const char *name;
svn_string_t *token, *value;
ra_svn_token_entry_t *entry;
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "sc(?s)", &token, &name,
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "sc(?s)", &token, &name,
&value));
SVN_ERR(lookup_token(ds, token, TRUE, &entry));
SVN_CMD_ERR(ds->editor->change_file_prop(entry->baton, name, value, pool));
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_close_file(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_close_file(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
svn_string_t *token;
ra_svn_token_entry_t *entry;
const char *text_checksum;
/* Parse arguments and look up the file token. */
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s(?c)",
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "s(?c)",
&token, &text_checksum));
SVN_ERR(lookup_token(ds, token, TRUE, &entry));
/* Close the file and destroy the baton. */
SVN_CMD_ERR(ds->editor->close_file(entry->baton, text_checksum, pool));
- apr_hash_set(ds->tokens, token->data, token->len, NULL);
+ remove_token(ds, token);
if (--ds->file_refs == 0)
svn_pool_clear(ds->file_pool);
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_absent_file(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_absent_file(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
const char *path;
svn_string_t *token;
ra_svn_token_entry_t *entry;
/* Parse parameters and look up the parent directory token. */
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &path, &token));
+ SVN_ERR(svn_ra_svn__parse_tuple(params, "cs", &path, &token));
SVN_ERR(lookup_token(ds, token, FALSE, &entry));
/* Call the editor. */
@@ -836,22 +884,32 @@ static svn_error_t *ra_svn_handle_absent_file(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
-static svn_error_t *ra_svn_handle_close_edit(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_close_edit(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
SVN_CMD_ERR(ds->editor->close_edit(ds->edit_baton, pool));
ds->done = TRUE;
+#ifdef SVN_DEBUG
+ /* Before enabling this in non-maintainer mode:
+ * Note that this code is used on both client *and* server */
+ if (apr_hash_count(ds->tokens) != 0)
+ return svn_error_create(
+ SVN_ERR_FS_INCORRECT_EDITOR_COMPLETION, NULL,
+ _("Closing editor with directories or files open"));
+#endif
if (ds->aborted)
*ds->aborted = FALSE;
return svn_ra_svn__write_cmd_response(conn, pool, "");
}
-static svn_error_t *ra_svn_handle_abort_edit(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_abort_edit(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
ds->done = TRUE;
if (ds->aborted)
@@ -860,10 +918,11 @@ static svn_error_t *ra_svn_handle_abort_edit(svn_ra_svn_conn_t *conn,
return svn_ra_svn__write_cmd_response(conn, pool, "");
}
-static svn_error_t *ra_svn_handle_finish_replay(svn_ra_svn_conn_t *conn,
- apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds)
+static svn_error_t *
+ra_svn_handle_finish_replay(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds)
{
if (!ds->for_replay)
return svn_error_createf
@@ -875,11 +934,15 @@ static svn_error_t *ra_svn_handle_finish_replay(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
+/* Common function signature for all editor command handlers. */
+typedef svn_error_t *(*cmd_handler_t)(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_ra_svn__list_t *params,
+ ra_svn_driver_state_t *ds);
+
static const struct {
const char *cmd;
- svn_error_t *(*handler)(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
- const apr_array_header_t *params,
- ra_svn_driver_state_t *ds);
+ cmd_handler_t handler;
} ra_svn_edit_cmds[] = {
{ "change-file-prop", ra_svn_handle_change_file_prop },
{ "open-file", ra_svn_handle_open_file },
@@ -903,12 +966,98 @@ static const struct {
{ NULL }
};
+/* All editor commands are kept in a collision-free hash table. */
+
+/* Hash table entry.
+ It is similar to ra_svn_edit_cmds but uses our SVN string type. */
+typedef struct cmd_t {
+ svn_string_t cmd;
+ cmd_handler_t handler;
+} cmd_t;
+
+/* The actual hash table. It will be filled once before first usage.
+
+ If you add more commands, you may have to tweak the table size to
+ eliminate collisions. Alternatively, you may modify the hash function.
+
+ Be sure to initialize all elements with 0 as the has conflict detection
+ will rely on it (see init_cmd_hash).
+ */
+#define CMD_HASH_SIZE 67
+static cmd_t cmd_hash[CMD_HASH_SIZE] = { { { NULL } } };
+
+/* Init flag that controls CMD_HASH's atomic initialization. */
+static volatile svn_atomic_t cmd_hash_initialized = FALSE;
+
+/* Super-fast hash function that works very well with the structure of our
+ command words. It produces no conflicts for them.
+
+ Return the index within CMD_HASH that a command NAME of LEN chars would
+ be found. LEN > 0.
+ */
+static apr_size_t
+cmd_hash_func(const char *name,
+ apr_size_t len)
+{
+ apr_size_t value = (apr_byte_t)(name[0] - 'a') % 8
+ + 1 * (apr_byte_t)(name[len - 1] - 'a') % 8
+ + 10 * (len - 7);
+ return value % CMD_HASH_SIZE;
+}
+
+/* svn_atomic__init_once callback that fills the CMD_HASH table. It will
+ error out on hash collisions. BATON and POOL are not used. */
+static svn_error_t *
+init_cmd_hash(void *baton,
+ apr_pool_t *pool)
+{
+ int i;
+ for (i = 0; ra_svn_edit_cmds[i].cmd; i++)
+ {
+ apr_size_t len = strlen(ra_svn_edit_cmds[i].cmd);
+ apr_size_t value = cmd_hash_func(ra_svn_edit_cmds[i].cmd, len);
+ SVN_ERR_ASSERT(cmd_hash[value].cmd.data == NULL);
+
+ cmd_hash[value].cmd.data = ra_svn_edit_cmds[i].cmd;
+ cmd_hash[value].cmd.len = len;
+ cmd_hash[value].handler = ra_svn_edit_cmds[i].handler;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Return the command handler function for the command name CMD.
+ Return NULL if no such handler exists */
+static cmd_handler_t
+cmd_lookup(const char *cmd)
+{
+ apr_size_t value;
+ apr_size_t len = strlen(cmd);
+
+ /* Malicious data that our hash function may not like? */
+ if (len == 0)
+ return NULL;
+
+ /* Hash lookup. */
+ value = cmd_hash_func(cmd, len);
+
+ /* Hit? */
+ if (cmd_hash[value].cmd.len != len)
+ return NULL;
+
+ if (memcmp(cmd_hash[value].cmd.data, cmd, len))
+ return NULL;
+
+ /* Yes! */
+ return cmd_hash[value].handler;
+}
+
static svn_error_t *blocked_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
void *baton)
{
ra_svn_driver_state_t *ds = baton;
const char *cmd;
- apr_array_header_t *params;
+ svn_ra_svn__list_t *params;
/* We blocked trying to send an error. Read and discard an editing
* command in order to avoid deadlock. */
@@ -931,13 +1080,16 @@ svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn,
ra_svn_driver_state_t state;
apr_pool_t *subpool = svn_pool_create(pool);
const char *cmd;
- int i;
svn_error_t *err, *write_err;
- apr_array_header_t *params;
+ svn_ra_svn__list_t *params;
+
+ SVN_ERR(svn_atomic__init_once(&cmd_hash_initialized, init_cmd_hash, NULL,
+ pool));
state.editor = editor;
state.edit_baton = edit_baton;
- state.tokens = apr_hash_make(pool);
+ state.tokens = svn_hash__make(pool);
+ state.last_token = NULL;
state.aborted = aborted;
state.done = FALSE;
state.pool = pool;
@@ -948,15 +1100,18 @@ svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn,
while (!state.done)
{
svn_pool_clear(subpool);
+
+ /* WRT to applying I/O limits, treat each editor command as a separate
+ * protocol command. */
+ svn_ra_svn__reset_command_io_counters(conn);
if (editor)
{
+ cmd_handler_t handler;
SVN_ERR(svn_ra_svn__read_tuple(conn, subpool, "wl", &cmd, &params));
- for (i = 0; ra_svn_edit_cmds[i].cmd; i++)
- if (strcmp(cmd, ra_svn_edit_cmds[i].cmd) == 0)
- break;
+ handler = cmd_lookup(cmd);
- if (ra_svn_edit_cmds[i].cmd)
- err = (*ra_svn_edit_cmds[i].handler)(conn, subpool, params, &state);
+ if (handler)
+ err = (*handler)(conn, subpool, params, &state);
else if (strcmp(cmd, "failure") == 0)
{
/* While not really an editor command this can occur when
@@ -964,7 +1119,7 @@ svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn,
command */
if (aborted)
*aborted = TRUE;
- err = svn_ra_svn__handle_failure_status(params, pool);
+ err = svn_ra_svn__handle_failure_status(params);
return svn_error_compose_create(
err,
editor->abort_edit(edit_baton, subpool));
diff --git a/subversion/libsvn_ra_svn/internal_auth.c b/subversion/libsvn_ra_svn/internal_auth.c
index 8e63ab5b5fc3a..7f84c7bfdaad9 100644
--- a/subversion/libsvn_ra_svn/internal_auth.c
+++ b/subversion/libsvn_ra_svn/internal_auth.c
@@ -37,16 +37,16 @@
#include "ra_svn.h"
-svn_boolean_t svn_ra_svn__find_mech(const apr_array_header_t *mechlist,
+svn_boolean_t svn_ra_svn__find_mech(const svn_ra_svn__list_t *mechlist,
const char *mech)
{
int i;
- svn_ra_svn_item_t *elt;
+ svn_ra_svn__item_t *elt;
for (i = 0; i < mechlist->nelts; i++)
{
- elt = &APR_ARRAY_IDX(mechlist, i, svn_ra_svn_item_t);
- if (elt->kind == SVN_RA_SVN_WORD && strcmp(elt->u.word, mech) == 0)
+ elt = &SVN_RA_SVN__LIST_ITEM(mechlist, i);
+ if (elt->kind == SVN_RA_SVN_WORD && strcmp(elt->u.word.data, mech) == 0)
return TRUE;
}
return FALSE;
@@ -69,7 +69,7 @@ static svn_error_t *read_success(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
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)
{
svn_ra_svn_conn_t *conn = sess->conn;
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;
diff --git a/subversion/libsvn_ra_svn/protocol b/subversion/libsvn_ra_svn/protocol
index dfc1f3dc16591..dfa7bc4585ac2 100644
--- a/subversion/libsvn_ra_svn/protocol
+++ b/subversion/libsvn_ra_svn/protocol
@@ -188,6 +188,10 @@ capability and C indicates a client capability):
[CS] svndiff1 If both the client and server support svndiff version
1, this will be used as the on-the-wire format for
svndiff instead of svndiff version 0.
+[CS] accepts-svndiff2 This capability advertises support for accepting
+ svndiff2 deltas. The sender of a delta (= the editor
+ driver) may send it in any svndiff version the receiver
+ has announced it can accept.
[CS] absent-entries If the remote end announces support for this capability,
it will accept the absent-dir and absent-file editor
commands.
@@ -206,6 +210,8 @@ capability and C indicates a client capability):
retrieval of inherited properties via the get-dir and
get-file commands and also supports the get-iprops
command (see section 3.1.1).
+[S] list If the server presents this capability, it supports the
+ list command (see section 3.1.1).
3. Commands
-----------
@@ -487,6 +493,21 @@ second place for auth-request point as noted below.
response: ( inherited-props:iproplist )
New in svn 1.8. If rev is not specified, the youngest revision is used.
+ list
+ params: ( path:string [ rev:number ] depth:word
+ ( field:dirent-field ... ) ? ( pattern:string ... ) )
+ Before sending response, server sends dirents, ending with "done".
+ dirent: ( rel-path:string kind:node-kind
+ ? [ size:number ] [ has-props:bool ] [ created-rev:number ]
+ [ created-date:string ] [ last-author:string ] )
+ | done
+ dirent-field: kind | size | has-props | created-rev | time | last-author
+ | word
+ response: ( )
+ New in svn 1.10. If rev is not specified, the youngest revision is used.
+ If the dirent-fields don't contain "kind", "unknown" will be returned
+ in the kind field.
+
3.1.2. Editor Command Set
An edit operation produces only one response, at close-edit or
diff --git a/subversion/libsvn_ra_svn/ra_svn.h b/subversion/libsvn_ra_svn/ra_svn.h
index d9fe1b2758c9d..012d455b1359c 100644
--- a/subversion/libsvn_ra_svn/ra_svn.h
+++ b/subversion/libsvn_ra_svn/ra_svn.h
@@ -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;
@@ -119,12 +125,30 @@ struct svn_ra_svn_conn_st {
apr_pool_t *pool;
};
+/* The session's URL state for client and server side.
+ *
+ * This keeps track of the respective client-side and server-side "parent"
+ * URLs. It tells us whether we may have to send reparent commands to the
+ * server and how to tweak path parameters when we decided to handle
+ * reparent requests on the client side only. */
+typedef struct svn_ra_svn__parent_t {
+ /* Client-side session base URL, i.e. client's parent path. */
+ svn_stringbuf_t *client_url;
+
+ /* Server-side base URL, i.e. server's parent path. */
+ svn_stringbuf_t *server_url;
+
+ /* Relative path to add to a client-side parameter to translate it for the
+ * server-side. I.e. the relative path from SERVER_URL to CLIENT_URL. */
+ svn_stringbuf_t *path;
+} svn_ra_svn__parent_t;
+
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;
+ svn_ra_svn__parent_t *parent;
const char *user;
const char *hostname; /* The remote hostname. */
const char *realm_prefix;
@@ -151,6 +175,12 @@ void svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
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,
@@ -163,10 +193,9 @@ svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *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,
@@ -220,7 +249,7 @@ svn_ra_svn__stream_data_available(svn_ra_svn__stream_t *stream,
* 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
@@ -229,7 +258,7 @@ svn_ra_svn__do_cyrus_auth(svn_ra_svn__session_baton_t *sess,
* 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
@@ -239,8 +268,8 @@ svn_error_t *svn_ra_svn__auth_response(svn_ra_svn_conn_t *conn,
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. */
diff --git a/subversion/libsvn_ra_svn/wrapped_sasl.c b/subversion/libsvn_ra_svn/wrapped_sasl.c
new file mode 100644
index 0000000000000..e20fc98689a9c
--- /dev/null
+++ b/subversion/libsvn_ra_svn/wrapped_sasl.c
@@ -0,0 +1,197 @@
+/*
+ * wrapped_sasl.c : wrapped SASL API
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include "svn_private_config.h"
+#ifdef SVN_HAVE_SASL
+
+#include "private/ra_svn_wrapped_sasl.h"
+
+/* See the comment at the top of svn_wrapped_sasl.h */
+#ifdef __APPLE__
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+# endif
+#endif /* __APPLE__ */
+
+void
+svn_sasl__set_mutex(sasl_mutex_alloc_t *alloc,
+ sasl_mutex_lock_t *lock,
+ sasl_mutex_unlock_t *unlock,
+ sasl_mutex_free_t *free)
+{
+ sasl_set_mutex(alloc, lock, unlock, free);
+}
+
+void
+svn_sasl__done(void)
+{
+ sasl_done();
+}
+
+void
+svn_sasl__dispose(sasl_conn_t **pconn)
+{
+ sasl_dispose(pconn);
+}
+
+const char *
+svn_sasl__errstring(int saslerr, const char *langlist, const char **outlang)
+{
+ return sasl_errstring(saslerr, langlist, outlang);
+}
+
+const char *
+svn_sasl__errdetail(sasl_conn_t *conn)
+{
+ return sasl_errdetail(conn);
+}
+
+int
+svn_sasl__getprop(sasl_conn_t *conn, int propnum, const void **pvalue)
+{
+ return sasl_getprop(conn, propnum, pvalue);
+}
+
+int
+svn_sasl__setprop(sasl_conn_t *conn, int propnum, const void *value)
+{
+ return sasl_setprop(conn, propnum, value);
+}
+
+int
+svn_sasl__client_init(const sasl_callback_t *callbacks)
+{
+ return sasl_client_init(callbacks);
+}
+
+int
+svn_sasl__client_new(const char *service,
+ const char *serverFQDN,
+ const char *iplocalport,
+ const char *ipremoteport,
+ const sasl_callback_t *prompt_supp,
+ unsigned flags,
+ sasl_conn_t **pconn)
+{
+ return sasl_client_new(service, serverFQDN, iplocalport, ipremoteport,
+ prompt_supp, flags, pconn);
+}
+
+int
+svn_sasl__client_start(sasl_conn_t *conn,
+ const char *mechlist,
+ sasl_interact_t **prompt_need,
+ const char **clientout,
+ unsigned *clientoutlen,
+ const char **mech)
+{
+ return sasl_client_start(conn, mechlist, prompt_need,
+ clientout, clientoutlen, mech);
+}
+
+int
+svn_sasl__client_step(sasl_conn_t *conn,
+ const char *serverin,
+ unsigned serverinlen,
+ sasl_interact_t **prompt_need,
+ const char **clientout,
+ unsigned *clientoutlen)
+{
+ return sasl_client_step(conn, serverin, serverinlen, prompt_need,
+ clientout, clientoutlen);
+}
+
+int
+svn_sasl__server_init(const sasl_callback_t *callbacks,
+ const char *appname)
+{
+ return sasl_server_init(callbacks, appname);
+}
+
+int
+svn_sasl__server_new(const char *service,
+ const char *serverFQDN,
+ const char *user_realm,
+ const char *iplocalport,
+ const char *ipremoteport,
+ const sasl_callback_t *callbacks,
+ unsigned flags,
+ sasl_conn_t **pconn)
+{
+ return sasl_server_new(service, serverFQDN, user_realm,
+ iplocalport, ipremoteport, callbacks, flags, pconn);
+}
+
+int
+svn_sasl__listmech(sasl_conn_t *conn,
+ const char *user,
+ const char *prefix,
+ const char *sep,
+ const char *suffix,
+ const char **result,
+ unsigned *plen,
+ int *pcount)
+{
+ return sasl_listmech(conn, user, prefix, sep, suffix, result, plen, pcount);
+}
+
+int
+svn_sasl__server_start(sasl_conn_t *conn,
+ const char *mech,
+ const char *clientin,
+ unsigned clientinlen,
+ const char **serverout,
+ unsigned *serveroutlen)
+{
+ return sasl_server_start(conn, mech, clientin, clientinlen,
+ serverout, serveroutlen);
+}
+
+int
+svn_sasl__server_step(sasl_conn_t *conn,
+ const char *clientin,
+ unsigned clientinlen,
+ const char **serverout,
+ unsigned *serveroutlen)
+{
+ return sasl_server_step(conn, clientin, clientinlen,
+ serverout, serveroutlen);
+}
+
+int
+svn_sasl__encode(sasl_conn_t *conn,
+ const char *input, unsigned inputlen,
+ const char **output, unsigned *outputlen)
+{
+ return sasl_encode(conn, input, inputlen, output, outputlen);
+}
+
+int
+svn_sasl__decode(sasl_conn_t *conn,
+ const char *input, unsigned inputlen,
+ const char **output, unsigned *outputlen)
+{
+ return sasl_decode(conn, input, inputlen, output, outputlen);
+}
+
+#endif /* SVN_HAVE_SASL */