diff options
author | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 |
---|---|---|
committer | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 |
commit | 3faf8d6bffc5d0fb2525ba37bb504c53366caf9d (patch) | |
tree | 7e47911263e75034b767fe34b2f8d3d17e91f66d /subversion/libsvn_ra_svn | |
parent | a55fb3c0d5eca7d887798125d5b95942b1f01d4b (diff) |
Diffstat (limited to 'subversion/libsvn_ra_svn')
-rw-r--r-- | subversion/libsvn_ra_svn/client.c | 610 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/cram.c | 4 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/cyrus_auth.c | 89 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/deprecated.c | 51 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/editorp.c | 415 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/internal_auth.c | 10 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/marshal.c | 880 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/protocol | 21 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/ra_svn.h | 47 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/wrapped_sasl.c | 197 |
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, ©_path, ©_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, ©_path, ©_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, ©_path, ©_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, ¶ms)); - 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, ¶ms)); 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, ¶ms); 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 */ |