diff options
Diffstat (limited to 'subversion/libsvn_ra_svn')
-rw-r--r-- | subversion/libsvn_ra_svn/client.c | 329 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/cram.c | 2 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/cyrus_auth.c | 39 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/deprecated.c | 49 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/editorp.c | 120 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/internal_auth.c | 2 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/libsvn_ra_svn.pc.in | 12 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/marshal.c | 900 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/protocol | 22 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/ra_svn.h | 29 | ||||
-rw-r--r-- | subversion/libsvn_ra_svn/streams.c | 149 |
11 files changed, 1103 insertions, 550 deletions
diff --git a/subversion/libsvn_ra_svn/client.c b/subversion/libsvn_ra_svn/client.c index 335f3215a697c..c83f25db231e3 100644 --- a/subversion/libsvn_ra_svn/client.c +++ b/subversion/libsvn_ra_svn/client.c @@ -233,7 +233,7 @@ 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) { - return svn_ra_svn__write_tuple(conn, pool, "w(?c)", mech, mech_arg); + return svn_error_trace(svn_ra_svn__write_tuple(conn, pool, "w(?c)", mech, mech_arg)); } static svn_error_t *handle_auth_request(svn_ra_svn__session_baton_t *sess, @@ -368,15 +368,16 @@ ra_svn_get_reporter(svn_ra_svn__session_baton_t *sess_baton, /* --- RA LAYER IMPLEMENTATION --- */ -/* (Note: *ARGV is an output parameter.) */ +/* (Note: *ARGV_P is an output parameter.) */ static svn_error_t *find_tunnel_agent(const char *tunnel, const char *hostinfo, - const char ***argv, + const char ***argv_p, apr_hash_t *config, apr_pool_t *pool) { svn_config_t *cfg; const char *val, *var, *cmd; char **cmd_argv; + const char **argv; apr_size_t len; apr_status_t status; int n; @@ -430,16 +431,22 @@ static svn_error_t *find_tunnel_agent(const char *tunnel, if (status != APR_SUCCESS) return svn_error_wrap_apr(status, _("Can't tokenize command '%s'"), cmd); - /* Append the fixed arguments to the result. */ + /* Calc number of the fixed arguments. */ for (n = 0; cmd_argv[n] != NULL; n++) ; - *argv = apr_palloc(pool, (n + 4) * sizeof(char *)); - memcpy((void *) *argv, cmd_argv, n * sizeof(char *)); - (*argv)[n++] = svn_path_uri_decode(hostinfo, pool); - (*argv)[n++] = "svnserve"; - (*argv)[n++] = "-t"; - (*argv)[n] = NULL; + argv = apr_palloc(pool, (n + 4) * sizeof(char *)); + + /* Append the fixed arguments to the result. */ + for (n = 0; cmd_argv[n] != NULL; n++) + argv[n] = cmd_argv[n]; + + argv[n++] = svn_path_uri_decode(hostinfo, pool); + argv[n++] = "svnserve"; + argv[n++] = "-t"; + argv[n] = NULL; + + *argv_p = argv; return SVN_NO_ERROR; } @@ -452,13 +459,17 @@ static void handle_child_process_error(apr_pool_t *pool, apr_status_t status, { svn_ra_svn_conn_t *conn; apr_file_t *in_file, *out_file; + svn_stream_t *in_stream, *out_stream; svn_error_t *err; if (apr_file_open_stdin(&in_file, pool) || apr_file_open_stdout(&out_file, pool)) return; - conn = svn_ra_svn_create_conn3(NULL, in_file, out_file, + 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, SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0, pool); err = svn_error_wrap_apr(status, _("Error in child process: %s"), desc); @@ -529,7 +540,11 @@ 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_conn3(NULL, proc->out, proc->in, + *conn = svn_ra_svn_create_conn4(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); err = svn_ra_svn__skip_leading_garbage(*conn, pool); @@ -556,24 +571,54 @@ static svn_error_t *parse_url(const char *url, apr_uri_t *uri, return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Illegal svn repository URL '%s'"), url); - if (! uri->port) - uri->port = SVN_RA_SVN_PORT; - return SVN_NO_ERROR; } +/* This structure is used as a baton for the pool cleanup function to + store tunnel parameters used by the close-tunnel callback. */ +struct tunnel_data_t { + void *tunnel_context; + void *tunnel_baton; + svn_ra_close_tunnel_func_t close_tunnel; + svn_stream_t *request; + svn_stream_t *response; +}; + +/* Pool cleanup function that invokes the close-tunnel callback. */ +static apr_status_t close_tunnel_cleanup(void *baton) +{ + const struct tunnel_data_t *const td = baton; + + if (td->close_tunnel) + td->close_tunnel(td->tunnel_context, td->tunnel_baton); + + svn_error_clear(svn_stream_close(td->request)); + + /* We might have one stream to use for both request and response! */ + if (td->request != td->response) + svn_error_clear(svn_stream_close(td->response)); + + return APR_SUCCESS; /* ignored */ +} + /* Open a session to URL, returning it in *SESS_P, allocating it in POOL. URI is a parsed version of URL. CALLBACKS and CALLBACKS_BATON - are provided by the caller of ra_svn_open. If tunnel_argv is non-null, - it points to a program argument list to use when invoking the tunnel agent. + are provided by the caller of ra_svn_open. If TUNNEL_NAME is not NULL, + it is the name of the tunnel type parsed from the URL scheme. + If TUNNEL_ARGV is not NULL, it points to a program argument list to use + when invoking the tunnel agent. */ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p, const char *url, const apr_uri_t *uri, + const char *tunnel_name, const char **tunnel_argv, + apr_hash_t *config, const svn_ra_callbacks2_t *callbacks, void *callbacks_baton, - apr_pool_t *pool) + svn_auth_baton_t *auth_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_ra_svn__session_baton_t *sess; svn_ra_svn_conn_t *conn; @@ -581,26 +626,67 @@ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p, apr_uint64_t minver, maxver; apr_array_header_t *mechlist, *server_caplist, *repos_caplist; const char *client_string = NULL; + apr_pool_t *pool = result_pool; sess = apr_palloc(pool, sizeof(*sess)); sess->pool = pool; - sess->is_tunneled = (tunnel_argv != NULL); + sess->is_tunneled = (tunnel_name != NULL); sess->url = apr_pstrdup(pool, url); sess->user = uri->user; sess->hostname = uri->hostname; - sess->realm_prefix = apr_psprintf(pool, "<svn://%s:%d>", uri->hostname, - uri->port); + sess->tunnel_name = tunnel_name; sess->tunnel_argv = tunnel_argv; sess->callbacks = callbacks; sess->callbacks_baton = callbacks_baton; sess->bytes_read = sess->bytes_written = 0; + sess->auth_baton = auth_baton; + + if (config) + SVN_ERR(svn_config_copy_config(&sess->config, config, pool)); + else + sess->config = NULL; + + if (tunnel_name) + { + sess->realm_prefix = apr_psprintf(pool, "<svn+%s://%s:%d>", + tunnel_name, + uri->hostname, uri->port); - if (tunnel_argv) - SVN_ERR(make_tunnel(tunnel_argv, &conn, pool)); + if (tunnel_argv) + SVN_ERR(make_tunnel(tunnel_argv, &conn, pool)); + else + { + struct tunnel_data_t *const td = apr_palloc(pool, sizeof(*td)); + + td->tunnel_baton = callbacks->tunnel_baton; + td->close_tunnel = NULL; + + SVN_ERR(callbacks->open_tunnel_func( + &td->request, &td->response, + &td->close_tunnel, &td->tunnel_context, + callbacks->tunnel_baton, tunnel_name, + uri->user, uri->hostname, uri->port, + callbacks->cancel_func, callbacks_baton, + pool)); + + apr_pool_cleanup_register(pool, td, close_tunnel_cleanup, + apr_pool_cleanup_null); + + conn = svn_ra_svn_create_conn4(NULL, td->response, td->request, + SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, + 0, 0, pool); + SVN_ERR(svn_ra_svn__skip_leading_garbage(conn, pool)); + } + } else { - SVN_ERR(make_connection(uri->hostname, uri->port, &sock, pool)); - conn = svn_ra_svn_create_conn3(sock, NULL, NULL, + sess->realm_prefix = apr_psprintf(pool, "<svn://%s:%d>", uri->hostname, + uri->port ? uri->port : SVN_RA_SVN_PORT); + + 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, SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0, pool); } @@ -618,7 +704,7 @@ static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p, &client_string, pool)); if (client_string) sess->useragent = apr_pstrcat(pool, SVN_RA_SVN__DEFAULT_USERAGENT " ", - client_string, (char *)NULL); + client_string, SVN_VA_NULL); else sess->useragent = SVN_RA_SVN__DEFAULT_USERAGENT; @@ -722,10 +808,12 @@ static svn_error_t *ra_svn_open(svn_ra_session_t *session, const char *url, const svn_ra_callbacks2_t *callbacks, void *callback_baton, + svn_auth_baton_t *auth_baton, apr_hash_t *config, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - apr_pool_t *sess_pool = svn_pool_create(pool); + apr_pool_t *sess_pool = svn_pool_create(result_pool); svn_ra_svn__session_baton_t *sess; const char *tunnel, **tunnel_argv; apr_uri_t uri; @@ -737,11 +825,18 @@ static svn_error_t *ra_svn_open(svn_ra_session_t *session, SVN_ERR(parse_url(url, &uri, sess_pool)); - parse_tunnel(url, &tunnel, pool); + parse_tunnel(url, &tunnel, result_pool); - if (tunnel) + /* Use the default tunnel implementation if we got a tunnel name, + but either do not have tunnel handler callbacks installed, or + the handlers don't like the tunnel name. */ + if (tunnel + && (!callbacks->open_tunnel_func + || (callbacks->check_tunnel_func && callbacks->open_tunnel_func + && !callbacks->check_tunnel_func(callbacks->tunnel_baton, + tunnel)))) SVN_ERR(find_tunnel_agent(tunnel, uri.hostinfo, &tunnel_argv, config, - pool)); + result_pool)); else tunnel_argv = NULL; @@ -749,20 +844,37 @@ static svn_error_t *ra_svn_open(svn_ra_session_t *session, ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS) : NULL; - svn_auth_set_parameter(callbacks->auth_baton, + svn_auth_set_parameter(auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG, cfg_client); - svn_auth_set_parameter(callbacks->auth_baton, + svn_auth_set_parameter(auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS, cfg); /* We open the session in a subpool so we can get rid of it if we reparent with a server that doesn't support reparenting. */ - SVN_ERR(open_session(&sess, url, &uri, tunnel_argv, - callbacks, callback_baton, sess_pool)); + SVN_ERR(open_session(&sess, url, &uri, tunnel, tunnel_argv, config, + callbacks, callback_baton, + auth_baton, sess_pool, scratch_pool)); session->priv = sess; return SVN_NO_ERROR; } +static svn_error_t *ra_svn_dup_session(svn_ra_session_t *new_session, + svn_ra_session_t *old_session, + const char *new_session_url, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_svn__session_baton_t *old_sess = old_session->priv; + + SVN_ERR(ra_svn_open(new_session, NULL, new_session_url, + old_sess->callbacks, old_sess->callbacks_baton, + old_sess->auth_baton, old_sess->config, + result_pool, scratch_pool)); + + return SVN_NO_ERROR; +} + static svn_error_t *ra_svn_reparent(svn_ra_session_t *ra_session, const char *url, apr_pool_t *pool) @@ -792,8 +904,9 @@ static svn_error_t *ra_svn_reparent(svn_ra_session_t *ra_session, sess_pool = svn_pool_create(ra_session->pool); err = parse_url(url, &uri, sess_pool); if (! err) - err = open_session(&new_sess, url, &uri, sess->tunnel_argv, - sess->callbacks, sess->callbacks_baton, sess_pool); + err = open_session(&new_sess, url, &uri, sess->tunnel_name, sess->tunnel_argv, + sess->config, sess->callbacks, sess->callbacks_baton, + sess->auth_baton, sess_pool, sess_pool); /* We destroy the new session pool on error, since it is allocated in the main session pool. */ if (err) @@ -954,6 +1067,9 @@ static svn_error_t *ra_svn_end_commit(void *baton) &(commit_info->author), &(commit_info->post_commit_err))); + commit_info->repos_root = apr_pstrdup(ccb->pool, + ccb->sess_baton->conn->repos_root); + if (ccb->callback) SVN_ERR(ccb->callback(commit_info, ccb->callback_baton, ccb->pool)); @@ -986,11 +1102,11 @@ static svn_error_t *ra_svn_commit(svn_ra_session_t *session, "a log message with pre-1.5 servers; " "consider passing an empty one, or upgrading " "the server")); - } + } else if (log_msg == NULL) /* 1.5+ server. Set LOG_MSG to something, since the 'logmsg' argument to the 'commit' protocol command is non-optional; on the server side, - only REVPROP_TABLE will be used, and LOG_MSG will be ignored. The + only REVPROP_TABLE will be used, and LOG_MSG will be ignored. The "svn:log" member of REVPROP_TABLE table is NULL, therefore the commit will have a NULL log message (not just "", really NULL). @@ -1118,13 +1234,13 @@ parse_iproplist(apr_array_header_t **inherited_props, new_iprop->path_or_url = svn_path_url_add_component2(repos_root_url, parent_rel_path, result_pool); - new_iprop->prop_hash = apr_hash_make(result_pool); + new_iprop->prop_hash = svn_hash__make(result_pool); for (hi = apr_hash_first(iterpool, iprops); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - svn_string_t *value = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + svn_string_t *value = apr_hash_this_val(hi); svn_hash_sets(new_iprop->prop_hash, apr_pstrdup(result_pool, name), svn_string_dup(value, result_pool)); @@ -1241,7 +1357,10 @@ static svn_error_t *ra_svn_get_dir(svn_ra_session_t *session, if (dirent_fields & SVN_DIRENT_LAST_AUTHOR) SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_DIRENT_LAST_AUTHOR)); - SVN_ERR(svn_ra_svn__write_tuple(conn, 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 + to see "true" if it is omitted. */ + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)b)", FALSE)); SVN_ERR(handle_auth_request(sess_baton, pool)); SVN_ERR(svn_ra_svn__read_cmd_response(conn, pool, "rll", &rev, &proplist, @@ -1257,7 +1376,7 @@ static svn_error_t *ra_svn_get_dir(svn_ra_session_t *session, return SVN_NO_ERROR; /* Interpret the directory list. */ - *dirents = apr_hash_make(pool); + *dirents = svn_hash__make(pool); for (i = 0; i < dirlist->nelts; i++) { const char *name, *kind, *cdate, *cauthor; @@ -1273,7 +1392,14 @@ static svn_error_t *ra_svn_get_dir(svn_ra_session_t *session, SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "cwnbr(?c)(?c)", &name, &kind, &size, &has_props, &crev, &cdate, &cauthor)); - name = svn_relpath_canonicalize(name, pool); + + /* Nothing to sanitize here. Any multi-segment path is simply + illegal in the hash returned by svn_ra_get_dir2. */ + if (strchr(name, '/')) + return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, + _("Invalid directory entry name '%s'"), + name); + dirent = svn_dirent_create(pool); dirent->kind = svn_node_kind_from_word(kind); dirent->size = size;/* FIXME: svn_filesize_t */ @@ -1345,7 +1471,7 @@ static svn_error_t *ra_svn_get_mergeinfo(svn_ra_session_t *session, *catalog = NULL; if (mergeinfo_tuple->nelts > 0) { - *catalog = apr_hash_make(pool); + *catalog = svn_hash__make(pool); for (i = 0; i < mergeinfo_tuple->nelts; i++) { svn_mergeinfo_t for_path; @@ -1501,6 +1627,10 @@ perform_ra_svn_log(svn_error_t **outer_error, const char *path; char *name; svn_boolean_t want_custom_revprops; + svn_boolean_t want_author = FALSE; + svn_boolean_t want_message = FALSE; + svn_boolean_t want_date = FALSE; + int nreceived = 0; SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "log")); if (paths) @@ -1523,10 +1653,14 @@ perform_ra_svn_log(svn_error_t **outer_error, { name = APR_ARRAY_IDX(revprops, i, char *); SVN_ERR(svn_ra_svn__write_cstring(conn, pool, name)); - if (!want_custom_revprops - && strcmp(name, SVN_PROP_REVISION_AUTHOR) != 0 - && strcmp(name, SVN_PROP_REVISION_DATE) != 0 - && strcmp(name, SVN_PROP_REVISION_LOG) != 0) + + if (strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0) + want_author = TRUE; + else if (strcmp(name, SVN_PROP_REVISION_DATE) == 0) + want_date = TRUE; + else if (strcmp(name, SVN_PROP_REVISION_LOG) == 0) + want_message = TRUE; + else want_custom_revprops = TRUE; } SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); @@ -1534,6 +1668,10 @@ perform_ra_svn_log(svn_error_t **outer_error, else { SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!w())", "all-revprops")); + + want_author = TRUE; + want_date = TRUE; + want_message = TRUE; want_custom_revprops = TRUE; } @@ -1554,7 +1692,6 @@ perform_ra_svn_log(svn_error_t **outer_error, svn_ra_svn_item_t *item; apr_hash_t *cphash; svn_revnum_t rev; - int nreceived; svn_pool_clear(iterpool); SVN_ERR(svn_ra_svn__read_item(conn, iterpool, &item)); @@ -1597,11 +1734,12 @@ perform_ra_svn_log(svn_error_t **outer_error, if (cplist->nelts > 0) { /* Interpret the changed-paths list. */ - cphash = apr_hash_make(iterpool); + cphash = svn_hash__make(iterpool); for (i = 0; i < cplist->nelts; i++) { svn_log_changed_path2_t *change; - const char *copy_path, *action, *cpath, *kind_str; + svn_string_t *cpath; + 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, @@ -1610,14 +1748,19 @@ perform_ra_svn_log(svn_error_t **outer_error, 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__parse_tuple(elt->u.list, iterpool, - "cw(?cr)?(?c?BB)", + SVN_ERR(svn_ra_svn__read_data_log_changed_entry(elt->u.list, &cpath, &action, ©_path, ©_rev, &kind_str, &text_mods, &prop_mods)); - cpath = svn_fspath__canonicalize(cpath, iterpool); - if (copy_path) + + if (!svn_fspath__is_canonical(cpath->data)) + { + cpath->data = svn_fspath__canonicalize(cpath->data, iterpool); + cpath->len = strlen(cpath->data); + } + if (copy_path && !svn_fspath__is_canonical(copy_path)) copy_path = svn_fspath__canonicalize(copy_path, iterpool); + change = svn_log_changed_path2_create(iterpool); change->action = *action; change->copyfrom_path = copy_path; @@ -1625,13 +1768,16 @@ perform_ra_svn_log(svn_error_t **outer_error, change->node_kind = svn_node_kind_from_word(kind_str); change->text_modified = optbool_to_tristate(text_mods); change->props_modified = optbool_to_tristate(prop_mods); - svn_hash_sets(cphash, cpath, change); + apr_hash_set(cphash, cpath->data, cpath->len, change); } } else cphash = NULL; - nreceived = 0; + /* Invoke RECEIVER + - 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)) && ! *outer_error) { @@ -1647,37 +1793,18 @@ perform_ra_svn_log(svn_error_t **outer_error, SVN_ERR(svn_ra_svn__parse_proplist(rplist, iterpool, &log_entry->revprops)); if (log_entry->revprops == NULL) - log_entry->revprops = apr_hash_make(iterpool); - if (revprops == NULL) - { - /* Caller requested all revprops; set author/date/log. */ - if (author) - svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_AUTHOR, - author); - if (date) - svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_DATE, - date); - if (message) - svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_LOG, - message); - } - else - { - /* Caller requested some; maybe set author/date/log. */ - for (i = 0; i < revprops->nelts; i++) - { - name = APR_ARRAY_IDX(revprops, i, char *); - if (author && strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0) - svn_hash_sets(log_entry->revprops, - SVN_PROP_REVISION_AUTHOR, author); - if (date && strcmp(name, SVN_PROP_REVISION_DATE) == 0) - svn_hash_sets(log_entry->revprops, - SVN_PROP_REVISION_DATE, date); - if (message && strcmp(name, SVN_PROP_REVISION_LOG) == 0) - svn_hash_sets(log_entry->revprops, - SVN_PROP_REVISION_LOG, message); - } - } + log_entry->revprops = svn_hash__make(iterpool); + + if (author && want_author) + svn_hash_sets(log_entry->revprops, + SVN_PROP_REVISION_AUTHOR, author); + if (date && want_date) + svn_hash_sets(log_entry->revprops, + SVN_PROP_REVISION_DATE, date); + if (message && want_message) + svn_hash_sets(log_entry->revprops, + SVN_PROP_REVISION_LOG, message); + err = receiver(receiver_baton, log_entry, iterpool); if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) { @@ -1863,7 +1990,7 @@ static svn_error_t *ra_svn_get_locations(svn_ra_session_t *session, /* Read the response. This is so the server would have a chance to * report an error. */ - return svn_ra_svn__read_cmd_response(conn, pool, ""); + return svn_error_trace(svn_ra_svn__read_cmd_response(conn, pool, "")); } static svn_error_t * @@ -2009,7 +2136,7 @@ static svn_error_t *ra_svn_get_file_revs(svn_ra_session_t *session, { svn_stream_t *stream; - if (d_handler) + if (d_handler && d_handler != svn_delta_noop_window_handler) stream = svn_txdelta_parse_svndiff(d_handler, d_baton, TRUE, rev_pool); else @@ -2552,7 +2679,7 @@ static svn_error_t *ra_svn_replay(svn_ra_session_t *session, SVN_ERR(svn_ra_svn_drive_editor2(sess->conn, pool, editor, edit_baton, NULL, TRUE)); - return svn_ra_svn__read_cmd_response(sess->conn, pool, ""); + return svn_error_trace(svn_ra_svn__read_cmd_response(sess->conn, pool, "")); } @@ -2621,7 +2748,7 @@ ra_svn_replay_range(svn_ra_session_t *session, } svn_pool_destroy(iterpool); - return svn_ra_svn__read_cmd_response(sess->conn, pool, ""); + return svn_error_trace(svn_ra_svn__read_cmd_response(sess->conn, pool, "")); } @@ -2687,7 +2814,8 @@ ra_svn_get_deleted_rev(svn_ra_session_t *session, SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool), N_("'get-deleted-rev' not implemented"))); - return svn_ra_svn__read_cmd_response(conn, pool, "r", revision_deleted); + return svn_error_trace(svn_ra_svn__read_cmd_response(conn, pool, "r", + revision_deleted)); } static svn_error_t * @@ -2713,6 +2841,16 @@ 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_boolean_t iprop_capable; + + SVN_ERR(ra_svn_has_capability(session, &iprop_capable, + SVN_RA_CAPABILITY_INHERITED_PROPS, + scratch_pool)); + + /* If we don't support native iprop handling, use the implementation + in libsvn_ra */ + if (!iprop_capable) + return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); SVN_ERR(svn_ra_svn__write_cmd_get_iprops(conn, scratch_pool, path, revision)); @@ -2729,6 +2867,7 @@ static const svn_ra__vtable_t ra_svn_vtable = { ra_svn_get_description, ra_svn_get_schemes, ra_svn_open, + ra_svn_dup_session, ra_svn_reparent, ra_svn_get_session_url, ra_svn_get_latest_rev, diff --git a/subversion/libsvn_ra_svn/cram.c b/subversion/libsvn_ra_svn/cram.c index 1e54ac812e986..b92f37b4a1727 100644 --- a/subversion/libsvn_ra_svn/cram.c +++ b/subversion/libsvn_ra_svn/cram.c @@ -114,7 +114,7 @@ static svn_error_t *fail(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *msg) { SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", msg)); - return svn_ra_svn__flush(conn, pool); + return svn_error_trace(svn_ra_svn__flush(conn, pool)); } /* If we can, make the nonce with random bytes. If we can't... well, diff --git a/subversion/libsvn_ra_svn/cyrus_auth.c b/subversion/libsvn_ra_svn/cyrus_auth.c index 82e33d364274b..ac94a485a44fd 100644 --- a/subversion/libsvn_ra_svn/cyrus_auth.c +++ b/subversion/libsvn_ra_svn/cyrus_auth.c @@ -488,7 +488,7 @@ static svn_error_t *try_auth(svn_ra_svn__session_baton_t *sess, pmech - mechstring); const char *tail = pmech + strlen(mech); - mechstring = apr_pstrcat(pool, head, tail, (char *)NULL); + mechstring = apr_pstrcat(pool, head, tail, SVN_VA_NULL); again = TRUE; } } @@ -704,11 +704,13 @@ static void sasl_timeout_cb(void *baton, apr_interval_time_t interval) svn_ra_svn__stream_timeout(sasl_baton->stream, interval); } -/* Implements ra_svn_pending_fn_t. */ -static svn_boolean_t sasl_pending_cb(void *baton) +/* Implements svn_stream_data_available_fn_t. */ +static svn_error_t * +sasl_data_available_cb(void *baton, svn_boolean_t *data_available) { sasl_baton_t *sasl_baton = baton; - return svn_ra_svn__stream_pending(sasl_baton->stream); + return svn_error_trace(svn_ra_svn__stream_data_available(sasl_baton->stream, + data_available)); } svn_error_t *svn_ra_svn__enable_sasl_encryption(svn_ra_svn_conn_t *conn, @@ -766,10 +768,19 @@ svn_error_t *svn_ra_svn__enable_sasl_encryption(svn_ra_svn_conn_t *conn, /* Wrap the existing stream. */ sasl_baton->stream = conn->stream; - conn->stream = svn_ra_svn__stream_create(sasl_baton, sasl_read_cb, - sasl_write_cb, - sasl_timeout_cb, - sasl_pending_cb, conn->pool); + { + svn_stream_t *sasl_in = svn_stream_create(sasl_baton, conn->pool); + svn_stream_t *sasl_out = svn_stream_create(sasl_baton, conn->pool); + + svn_stream_set_read2(sasl_in, sasl_read_cb, NULL /* use default */); + svn_stream_set_data_available(sasl_in, sasl_data_available_cb); + svn_stream_set_write(sasl_out, sasl_write_cb); + + conn->stream = svn_ra_svn__stream_create(sasl_in, sasl_out, + sasl_baton, + sasl_timeout_cb, + conn->pool); + } /* Yay, we have a security layer! */ conn->encrypted = TRUE; } @@ -807,10 +818,10 @@ svn_error_t *svn_ra_svn__get_addresses(const char **local_addrport, /* Format the IP address and port number like this: a.b.c.d;port */ *local_addrport = apr_pstrcat(pool, local_addr, ";", apr_itoa(pool, (int)local_sa->port), - (char *)NULL); + SVN_VA_NULL); *remote_addrport = apr_pstrcat(pool, remote_addr, ";", apr_itoa(pool, (int)remote_sa->port), - (char *)NULL); + SVN_VA_NULL); } return SVN_NO_ERROR; } @@ -849,14 +860,14 @@ svn_ra_svn__do_cyrus_auth(svn_ra_svn__session_baton_t *sess, mechstring = apr_pstrcat(pool, mechstring, i == 0 ? "" : " ", - elt->u.word, (char *)NULL); + elt->u.word, SVN_VA_NULL); } } realmstring = apr_psprintf(pool, "%s %s", sess->realm_prefix, realm); /* Initialize the credential baton. */ - cred_baton.auth_baton = sess->callbacks->auth_baton; + cred_baton.auth_baton = sess->auth_baton; cred_baton.realmstring = realmstring; cred_baton.pool = pool; @@ -935,8 +946,8 @@ svn_ra_svn__do_cyrus_auth(svn_ra_svn__session_baton_t *sess, the CRAM-MD5 or ANONYMOUS plugins, in which case we can simply use the built-in implementation. In all other cases this call will be useless, but hey, at least we'll get consistent error messages. */ - return svn_ra_svn__do_internal_auth(sess, mechlist, - realm, pool); + return svn_error_trace(svn_ra_svn__do_internal_auth(sess, mechlist, + realm, pool)); } return err; } diff --git a/subversion/libsvn_ra_svn/deprecated.c b/subversion/libsvn_ra_svn/deprecated.c index 8182a4d5a0d88..ad2ad5839a0e1 100644 --- a/subversion/libsvn_ra_svn/deprecated.c +++ b/subversion/libsvn_ra_svn/deprecated.c @@ -21,6 +21,10 @@ * ==================================================================== */ +/* We define this here to remove any further warnings about the usage of + deprecated functions in this file. */ +#define SVN_DEPRECATED + #include "svn_ra_svn.h" #include "private/svn_ra_svn_private.h" @@ -232,3 +236,48 @@ svn_ra_svn_write_cmd_failure(svn_ra_svn_conn_t *conn, { return svn_error_trace(svn_ra_svn__write_cmd_failure(conn, pool, err)); } + +/* From marshal.c */ +svn_ra_svn_conn_t * +svn_ra_svn_create_conn3(apr_socket_t *sock, + apr_file_t *in_file, + apr_file_t *out_file, + int compression_level, + apr_size_t zero_copy_limit, + apr_size_t error_check_interval, + apr_pool_t *pool) +{ + svn_stream_t *in_stream = NULL; + svn_stream_t *out_stream = NULL; + + if (in_file) + in_stream = svn_stream_from_aprfile2(in_file, FALSE, pool); + if (out_file) + out_stream = svn_stream_from_aprfile2(out_file, FALSE, pool); + + return svn_ra_svn_create_conn4(sock, in_stream, out_stream, + compression_level, 0, 0, pool); +} + +svn_ra_svn_conn_t * +svn_ra_svn_create_conn2(apr_socket_t *sock, + apr_file_t *in_file, + apr_file_t *out_file, + int compression_level, + apr_pool_t *pool) +{ + return svn_ra_svn_create_conn3(sock, in_file, out_file, + compression_level, 0, 0, pool); +} + +/* backward-compatible implementation using the default compression level */ +svn_ra_svn_conn_t * +svn_ra_svn_create_conn(apr_socket_t *sock, + apr_file_t *in_file, + apr_file_t *out_file, + apr_pool_t *pool) +{ + return svn_ra_svn_create_conn3(sock, in_file, out_file, + SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0, + pool); +} diff --git a/subversion/libsvn_ra_svn/editorp.c b/subversion/libsvn_ra_svn/editorp.c index cc1d8ab2fd21a..88af89859fc48 100644 --- a/subversion/libsvn_ra_svn/editorp.c +++ b/subversion/libsvn_ra_svn/editorp.c @@ -91,7 +91,7 @@ typedef struct ra_svn_driver_state_t { 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 { - const char *token; + svn_string_t *token; void *baton; svn_boolean_t is_file; svn_stream_t *dstream; /* svndiff stream for apply_textdelta */ @@ -126,6 +126,7 @@ static ra_svn_baton_t *ra_svn_make_baton(svn_ra_svn_conn_t *conn, static svn_error_t * check_for_error_internal(ra_svn_edit_baton_t *eb, apr_pool_t *pool) { + svn_boolean_t available; SVN_ERR_ASSERT(!eb->got_status); /* reset TX counter */ @@ -135,7 +136,8 @@ check_for_error_internal(ra_svn_edit_baton_t *eb, apr_pool_t *pool) eb->conn->may_check_for_error = eb->conn->error_check_interval == 0; /* any incoming data? */ - if (svn_ra_svn__input_waiting(eb->conn, pool)) + SVN_ERR(svn_ra_svn__data_available(eb->conn, &available)); + if (available) { eb->got_status = TRUE; SVN_ERR(svn_ra_svn__write_cmd_abort_edit(eb->conn, pool)); @@ -393,11 +395,13 @@ static svn_error_t *ra_svn_close_edit(void *edit_baton, apr_pool_t *pool) SVN_ERR_ASSERT(!eb->got_status); eb->got_status = TRUE; SVN_ERR(svn_ra_svn__write_cmd_close_edit(eb->conn, pool)); - err = svn_ra_svn__read_cmd_response(eb->conn, pool, ""); + err = svn_error_trace(svn_ra_svn__read_cmd_response(eb->conn, pool, "")); if (err) { - svn_error_clear(svn_ra_svn__write_cmd_abort_edit(eb->conn, pool)); - return err; + return svn_error_compose_create( + err, + svn_error_trace( + svn_ra_svn__write_cmd_abort_edit(eb->conn, pool))); } if (eb->callback) SVN_ERR(eb->callback(eb->callback_baton)); @@ -461,27 +465,31 @@ void svn_ra_svn_get_editor(const svn_delta_editor_t **editor, /* Store a token entry. The token string will be copied into pool. */ static ra_svn_token_entry_t *store_token(ra_svn_driver_state_t *ds, - void *baton, const char *token, + void *baton, + svn_string_t *token, svn_boolean_t is_file, apr_pool_t *pool) { ra_svn_token_entry_t *entry; entry = apr_palloc(pool, sizeof(*entry)); - entry->token = apr_pstrdup(pool, token); + entry->token = svn_string_dup(token, pool); entry->baton = baton; entry->is_file = is_file; entry->dstream = NULL; entry->pool = pool; - svn_hash_sets(ds->tokens, entry->token, entry); + + apr_hash_set(ds->tokens, entry->token->data, entry->token->len, entry); + return entry; } -static svn_error_t *lookup_token(ra_svn_driver_state_t *ds, const char *token, +static svn_error_t *lookup_token(ra_svn_driver_state_t *ds, + svn_string_t *token, svn_boolean_t is_file, ra_svn_token_entry_t **entry) { - *entry = svn_hash_gets(ds->tokens, token); + *entry = apr_hash_get(ds->tokens, token->data, token->len); 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")); @@ -507,10 +515,10 @@ static svn_error_t *ra_svn_handle_open_root(svn_ra_svn_conn_t *conn, { svn_revnum_t rev; apr_pool_t *subpool; - const char *token; + svn_string_t *token; void *root_baton; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)c", &rev, &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)s", &rev, &token)); subpool = svn_pool_create(ds->pool); SVN_CMD_ERR(ds->editor->open_root(ds->edit_baton, rev, subpool, &root_baton)); @@ -523,11 +531,12 @@ static svn_error_t *ra_svn_handle_delete_entry(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *path, *token; + 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)c", + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)s", &path, &rev, &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); path = svn_relpath_canonicalize(path, pool); @@ -540,13 +549,14 @@ static svn_error_t *ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *path, *token, *child_token, *copy_path; + const char *path, *copy_path; + svn_string_t *token, *child_token; svn_revnum_t copy_rev; ra_svn_token_entry_t *entry; apr_pool_t *subpool; void *child_baton; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccc(?cr)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?cr)", &path, &token, &child_token, ©_path, ©_rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); subpool = svn_pool_create(entry->pool); @@ -573,13 +583,14 @@ static svn_error_t *ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *path, *token, *child_token; + const char *path; + svn_string_t *token, *child_token; svn_revnum_t rev; ra_svn_token_entry_t *entry; apr_pool_t *subpool; void *child_baton; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccc(?r)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?r)", &path, &token, &child_token, &rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); subpool = svn_pool_create(entry->pool); @@ -595,11 +606,12 @@ static svn_error_t *ra_svn_handle_change_dir_prop(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token, *name; + 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, "cc(?s)", &token, &name, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "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, @@ -612,16 +624,16 @@ static svn_error_t *ra_svn_handle_close_dir(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token; + 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, "c", &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "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)); - svn_hash_sets(ds->tokens, token, NULL); + apr_hash_set(ds->tokens, token->data, token->len, NULL); svn_pool_destroy(entry->pool); return SVN_NO_ERROR; } @@ -632,11 +644,11 @@ static svn_error_t *ra_svn_handle_absent_dir(svn_ra_svn_conn_t *conn, ra_svn_driver_state_t *ds) { const char *path; - const char *token; + 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, "cc", &path, &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &path, &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); /* Call the editor. */ @@ -649,15 +661,19 @@ static svn_error_t *ra_svn_handle_add_file(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *path, *token, *file_token, *copy_path; + 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, "ccc(?cr)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?cr)", &path, &token, &file_token, ©_path, ©_rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); ds->file_refs++; - path = svn_relpath_canonicalize(path, pool); + + /* The PATH should be canonical .. but never trust incoming data. */ + if (!svn_relpath_is_canonical(path)) + path = svn_relpath_canonicalize(path, pool); /* Some operations pass COPY_PATH as a full URL (commits, etc.). Others (replay, e.g.) deliver an fspath. That's ... annoying. */ @@ -680,15 +696,20 @@ static svn_error_t *ra_svn_handle_open_file(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *path, *token, *file_token; + 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, "ccc(?r)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?r)", &path, &token, &file_token, &rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); ds->file_refs++; - path = svn_relpath_canonicalize(path, pool); + + /* The PATH should be canonical .. but never trust incoming data. */ + if (!svn_relpath_is_canonical(path)) + path = svn_relpath_canonicalize(path, pool); + file_entry = store_token(ds, NULL, file_token, TRUE, ds->file_pool); SVN_CMD_ERR(ds->editor->open_file(path, entry->baton, rev, ds->file_pool, &file_entry->baton)); @@ -700,14 +721,14 @@ static svn_error_t *ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token; + svn_string_t *token; ra_svn_token_entry_t *entry; svn_txdelta_window_handler_t wh; void *wh_baton; char *base_checksum; /* Parse arguments and look up the token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)", + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s(?c)", &token, &base_checksum)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); if (entry->dstream) @@ -725,12 +746,12 @@ static svn_error_t *ra_svn_handle_textdelta_chunk(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token; + 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, "cs", &token, &str)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "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, @@ -744,11 +765,11 @@ static svn_error_t *ra_svn_handle_textdelta_end(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token; + 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, "c", &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s", &token)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); if (!entry->dstream) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, @@ -764,11 +785,11 @@ static svn_error_t *ra_svn_handle_change_file_prop(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token, *name; - svn_string_t *value; + const char *name; + svn_string_t *token, *value; ra_svn_token_entry_t *entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cc(?s)", &token, &name, + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "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)); @@ -780,18 +801,18 @@ static svn_error_t *ra_svn_handle_close_file(svn_ra_svn_conn_t *conn, const apr_array_header_t *params, ra_svn_driver_state_t *ds) { - const char *token; + 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, "c(?c)", + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "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)); - svn_hash_sets(ds->tokens, token, NULL); + apr_hash_set(ds->tokens, token->data, token->len, NULL); if (--ds->file_refs == 0) svn_pool_clear(ds->file_pool); return SVN_NO_ERROR; @@ -803,11 +824,11 @@ static svn_error_t *ra_svn_handle_absent_file(svn_ra_svn_conn_t *conn, ra_svn_driver_state_t *ds) { const char *path; - const char *token; + 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, "cc", &path, &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &path, &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); /* Call the editor. */ @@ -978,7 +999,12 @@ svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, { /* Abort the edit and use non-blocking I/O to write the error. */ if (editor) - svn_error_clear(editor->abort_edit(edit_baton, subpool)); + { + err = svn_error_compose_create( + err, + svn_error_trace(editor->abort_edit(edit_baton, + subpool))); + } svn_ra_svn__set_block_handler(conn, blocked_write, &state); } write_err = svn_ra_svn__write_cmd_failure( @@ -987,7 +1013,7 @@ svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, if (!write_err) write_err = svn_ra_svn__flush(conn, subpool); svn_ra_svn__set_block_handler(conn, NULL, NULL); - svn_error_clear(err); + svn_error_clear(err); /* We just sent this error */ SVN_ERR(write_err); break; } diff --git a/subversion/libsvn_ra_svn/internal_auth.c b/subversion/libsvn_ra_svn/internal_auth.c index eac2ccdec4aec..8e63ab5b5fc3a 100644 --- a/subversion/libsvn_ra_svn/internal_auth.c +++ b/subversion/libsvn_ra_svn/internal_auth.c @@ -95,7 +95,7 @@ svn_ra_svn__do_internal_auth(svn_ra_svn__session_baton_t *sess, { SVN_ERR(svn_auth_first_credentials(&creds, &iterstate, SVN_AUTH_CRED_SIMPLE, realmstring, - sess->callbacks->auth_baton, pool)); + sess->auth_baton, pool)); if (!creds) return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Can't get password")); diff --git a/subversion/libsvn_ra_svn/libsvn_ra_svn.pc.in b/subversion/libsvn_ra_svn/libsvn_ra_svn.pc.in new file mode 100644 index 0000000000000..4d6768986f13d --- /dev/null +++ b/subversion/libsvn_ra_svn/libsvn_ra_svn.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_ra_svn +Description: Subversion SVN Protocol Repository Access Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_delta libsvn_subr +Libs: -L${libdir} -lsvn_ra_svn @SVN_SASL_LIBS@ +Cflags: -I${includedir} diff --git a/subversion/libsvn_ra_svn/marshal.c b/subversion/libsvn_ra_svn/marshal.c index 7cf483f50afc9..55e3c1f1bb6bc 100644 --- a/subversion/libsvn_ra_svn/marshal.c +++ b/subversion/libsvn_ra_svn/marshal.c @@ -40,6 +40,7 @@ #include "svn_ra_svn.h" #include "svn_private_config.h" #include "svn_ctype.h" +#include "svn_sorts.h" #include "svn_time.h" #include "ra_svn.h" @@ -47,6 +48,7 @@ #include "private/svn_string_private.h" #include "private/svn_dep_compat.h" #include "private/svn_error_private.h" +#include "private/svn_subr_private.h" #define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n') @@ -56,6 +58,19 @@ #define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000) +/* 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. + */ +#define MAX_WORD_LENGTH 31 + +/* The generic parsers will use the following value to limit the recursion + * depth to some reasonable value. The current protocol implementation + * actually uses only maximum item nesting level of around 5. So, there is + * plenty of headroom here. + */ +#define ITEM_NESTING_LIMIT 64 + /* 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 @@ -66,19 +81,20 @@ get_timeout(svn_ra_svn_conn_t *conn) /* --- CONNECTION INITIALIZATION --- */ -svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock, - apr_file_t *in_file, - apr_file_t *out_file, +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) + apr_pool_t *result_pool) { svn_ra_svn_conn_t *conn; - void *mem = apr_palloc(pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE); + void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE); conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE); - assert((sock && !in_file && !out_file) || (!sock && in_file && out_file)); + assert((sock && !in_stream && !out_stream) + || (!sock && in_stream && out_stream)); #ifdef SVN_HAVE_SASL conn->sock = sock; conn->encrypted = FALSE; @@ -92,15 +108,15 @@ svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock, conn->may_check_for_error = error_check_interval == 0; conn->block_handler = NULL; conn->block_baton = NULL; - conn->capabilities = apr_hash_make(pool); + conn->capabilities = apr_hash_make(result_pool); conn->compression_level = compression_level; conn->zero_copy_limit = zero_copy_limit; - conn->pool = pool; + conn->pool = result_pool; if (sock != NULL) { apr_sockaddr_t *sa; - conn->stream = svn_ra_svn__stream_from_sock(sock, pool); + conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool); if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS)) conn->remote_ip = NULL; @@ -108,34 +124,14 @@ svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock, } else { - conn->stream = svn_ra_svn__stream_from_files(in_file, out_file, pool); + conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream, + result_pool); conn->remote_ip = NULL; } return conn; } -svn_ra_svn_conn_t *svn_ra_svn_create_conn2(apr_socket_t *sock, - apr_file_t *in_file, - apr_file_t *out_file, - int compression_level, - apr_pool_t *pool) -{ - return svn_ra_svn_create_conn3(sock, in_file, out_file, - compression_level, 0, 0, pool); -} - -/* backward-compatible implementation using the default compression level */ -svn_ra_svn_conn_t *svn_ra_svn_create_conn(apr_socket_t *sock, - apr_file_t *in_file, - apr_file_t *out_file, - apr_pool_t *pool) -{ - return svn_ra_svn_create_conn3(sock, in_file, out_file, - SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0, - pool); -} - svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn, const apr_array_header_t *list) { @@ -155,6 +151,12 @@ svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } +apr_pool_t * +svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn) +{ + return conn->pool; +} + svn_error_t * svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn, svn_delta_shim_callbacks_t *shim_callbacks) @@ -196,10 +198,10 @@ svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn, svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn)); } -svn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn, - apr_pool_t *pool) +svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn, + svn_boolean_t *data_available) { - return svn_ra_svn__stream_pending(conn->stream); + return svn_ra_svn__stream_data_available(conn->stream, data_available); } /* --- WRITE BUFFER MANAGEMENT --- */ @@ -285,20 +287,13 @@ static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t * -writebuf_write_short_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - const char *data, apr_size_t len) -{ - apr_size_t left = sizeof(conn->write_buf) - conn->write_pos; - if (len <= left) - { - memcpy(conn->write_buf + conn->write_pos, data, len); - conn->write_pos += len; - return SVN_NO_ERROR; - } - else - return writebuf_write(conn, pool, data, len); -} +/* Write STRING_LITERAL, which is a string literal argument. + + Note: The purpose of the empty string "" in the macro definition is to + assert that STRING_LITERAL is in fact a string literal. Otherwise, the + string concatenation attempt should produce a compile-time error. */ +#define writebuf_write_literal(conn, pool, string_literal) \ + writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1) static APR_INLINE svn_error_t * writebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data) @@ -389,7 +384,9 @@ 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); - SVN_ERR(writebuf_flush(conn, pool)); + if (conn->write_pos) + SVN_ERR(writebuf_flush(conn, pool)); + len = sizeof(conn->read_buf); SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool)); conn->read_ptr = conn->read_buf; @@ -397,7 +394,11 @@ static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool) return SVN_NO_ERROR; } -static APR_INLINE svn_error_t * +/* This is a hot function calling a cold function. GCC and others tend to + * inline the cold sub-function instead of this hot one. Therefore, be + * very insistent on lining this one. It is not a correctness issue, though. + */ +static SVN__FORCE_INLINE svn_error_t * readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result) { if (conn->read_ptr == conn->read_end) @@ -511,21 +512,32 @@ svn_ra_svn__write_number(svn_ra_svn_conn_t *conn, return write_number(conn, pool, number, ' '); } -svn_error_t * -svn_ra_svn__write_string(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const svn_string_t *str) +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 (str->len < 10) + if (len < 10) { - SVN_ERR(writebuf_writechar(conn, pool, (char)(str->len + '0'))); + SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0'))); SVN_ERR(writebuf_writechar(conn, pool, ':')); } else - SVN_ERR(write_number(conn, pool, str->len, ':')); + SVN_ERR(write_number(conn, pool, len, ':')); - SVN_ERR(writebuf_write(conn, pool, str->data, str->len)); + SVN_ERR(writebuf_write(conn, pool, s, len)); SVN_ERR(writebuf_writechar(conn, pool, ' ')); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_svn__write_string(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_string_t *str) +{ + SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len)); return SVN_NO_ERROR; } @@ -534,19 +546,7 @@ svn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *s) { - apr_size_t len = strlen(s); - - if (len < 10) - { - SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0'))); - SVN_ERR(writebuf_writechar(conn, pool, ':')); - } - else - SVN_ERR(write_number(conn, pool, len, ':')); - - SVN_ERR(writebuf_write(conn, pool, s, len)); - SVN_ERR(writebuf_writechar(conn, pool, ' ')); - + SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s))); return SVN_NO_ERROR; } @@ -555,38 +555,52 @@ svn_ra_svn__write_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *word) { - SVN_ERR(writebuf_write_short_string(conn, pool, word, strlen(word))); + SVN_ERR(writebuf_write(conn, pool, word, strlen(word))); SVN_ERR(writebuf_writechar(conn, pool, ' ')); return SVN_NO_ERROR; } svn_error_t * +svn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_boolean_t value) +{ + if (value) + SVN_ERR(writebuf_write_literal(conn, pool, "true ")); + else + SVN_ERR(writebuf_write_literal(conn, pool, "false ")); + + return SVN_NO_ERROR; +} + +svn_error_t * svn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_hash_t *props) { - apr_pool_t *iterpool; apr_hash_index_t *hi; - const void *key; - void *val; const char *propname; svn_string_t *propval; + apr_size_t len; + /* One might use an iterpool here but that would only be used when the + send buffer gets flushed and only by the CONN's progress callback. + That should happen at most once for typical prop lists and even then + use only a few bytes at best. + */ if (props) - { - iterpool = svn_pool_create(pool); - for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) - { - svn_pool_clear(iterpool); - apr_hash_this(hi, &key, NULL, &val); - propname = key; - propval = val; - SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", - propname, propval)); - } - svn_pool_destroy(iterpool); - } + for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) + { + apr_hash_this(hi, (const void **)&propname, + (apr_ssize_t *)&len, + (void **)&propval); + + SVN_ERR(svn_ra_svn__start_list(conn, pool)); + SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, propname, len)); + SVN_ERR(svn_ra_svn__write_string(conn, pool, propval)); + SVN_ERR(svn_ra_svn__end_list(conn, pool)); + } return SVN_NO_ERROR; } @@ -704,8 +718,7 @@ vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) static svn_error_t * vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) { - const char *cstr = va_arg(*ap, svn_boolean_t) ? "true" : "false"; - return svn_ra_svn__write_word(conn, pool, cstr); + return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t)); } static svn_error_t * @@ -780,8 +793,7 @@ write_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_boolean_t value) { - const char *cstr = value ? "true" : "false"; - return svn_ra_svn__write_word(conn, pool, cstr); + return svn_ra_svn__write_boolean(conn, pool, value); } static svn_error_t * @@ -929,7 +941,6 @@ svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn, 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_stringbuf_t *stringbuf; apr_size_t len = (apr_size_t)len64; apr_size_t readbuf_len; char *dest; @@ -940,58 +951,60 @@ static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("String length larger than maximum")); - /* 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 to - * network data corruption or a DOS attack), we receive a bogus claim that - * a very long string is going to follow. In that case, we 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). */ - readbuf_len = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD - ? len - : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; - stringbuf = svn_stringbuf_create_ensure(readbuf_len, pool); - dest = stringbuf->data; - - /* Read remaining string data directly into the string structure. - * Do it iteratively, if necessary. */ - while (readbuf_len) + /* Shorter strings can be copied directly from the read buffer. */ + if (conn->read_ptr + len <= conn->read_end) { - SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len)); - - stringbuf->len += readbuf_len; - len -= readbuf_len; + item->kind = SVN_RA_SVN_STRING; + item->u.string = svn_string_ncreate(conn->read_ptr, len, pool); + conn->read_ptr += len; + } + else + { + /* 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 + * to network data corruption or a DOS attack), we receive a bogus + * claim that a very long string is going to follow. In that case, we + * 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); + + /* Read string data directly into the string structure. + * Do it iteratively. */ + do + { + /* Determine length of chunk to read and re-alloc the buffer. */ + readbuf_len + = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD + ? len + : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; - /* Early exit. In most cases, strings can be read in the first - * iteration. */ - if (len == 0) - break; + svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len); + dest = stringbuf->data + stringbuf->len; - /* Prepare next iteration: determine length of chunk to read - * and re-alloc the string buffer. */ - readbuf_len - = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD - ? len - : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; + /* read data & update length info */ + SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len)); - svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len); - dest = stringbuf->data + stringbuf->len; - } + stringbuf->len += readbuf_len; + len -= readbuf_len; + } + while (len); - /* zero-terminate the string */ - stringbuf->data[stringbuf->len] = '\0'; + /* zero-terminate the string */ + stringbuf->data[stringbuf->len] = '\0'; - /* 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); + /* 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); + } return SVN_NO_ERROR; } /* Given the first non-whitespace character FIRST_CHAR, read an item * into the already allocated structure ITEM. LEVEL should be set - * to 0 for the first call and is used to enforce a recurssion limit + * 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, @@ -999,12 +1012,11 @@ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { char c = first_char; apr_uint64_t val; - svn_stringbuf_t *str; svn_ra_svn_item_t *listitem; - if (++level >= 64) + if (++level >= ITEM_NESTING_LIMIT) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, - _("Too many nested items")); + _("Items are nested too deeply")); /* Determine the item type and read it in. Make sure that c is the @@ -1022,7 +1034,8 @@ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, break; val = val * 10 + (c - '0'); /* val wrapped past maximum value? */ - if (prev_val >= (APR_UINT64_MAX / 10) && (val / 10) != prev_val) + if ((prev_val >= (APR_UINT64_MAX / 10)) + && (val < APR_UINT64_MAX - 10)) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Number is larger than maximum")); } @@ -1041,18 +1054,28 @@ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } else if (svn_ctype_isalpha(c)) { - /* It's a word. */ - str = svn_stringbuf_create_ensure(16, pool); - svn_stringbuf_appendbyte(str, c); + /* It's a word. Read it into a buffer of limited size. */ + char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1); + char *end = buffer + MAX_WORD_LENGTH; + char *p = buffer + 1; + + buffer[0] = c; while (1) { - SVN_ERR(readbuf_getchar(conn, pool, &c)); - if (!svn_ctype_isalnum(c) && c != '-') + SVN_ERR(readbuf_getchar(conn, pool, p)); + if (!svn_ctype_isalnum(*p) && *p != '-') break; - svn_stringbuf_appendbyte(str, c); + + if (++p == end) + return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, + _("Word is too long")); } + + c = *p; + *p = '\0'; + item->kind = SVN_RA_SVN_WORD; - item->u.word = str->data; + item->u.word = buffer; } else if (c == '(') { @@ -1179,6 +1202,36 @@ svn_ra_svn__read_item(svn_ra_svn_conn_t *conn, return read_item(conn, pool, *item, c, 0); } +/* Drain existing whitespace from the receive buffer of CONN until either + there is no data in the underlying receive socket anymore or we found + a non-whitespace char. Set *HAS_ITEM to TRUE in the latter case. + */ +static svn_error_t * +svn_ra_svn__has_item(svn_boolean_t *has_item, + svn_ra_svn_conn_t *conn, + apr_pool_t *pool) +{ + do + { + if (conn->read_ptr == conn->read_end) + { + svn_boolean_t available; + if (conn->write_pos) + SVN_ERR(writebuf_flush(conn, pool)); + + SVN_ERR(svn_ra_svn__data_available(conn, &available)); + if (!available) + break; + + SVN_ERR(readbuf_fill(conn, pool)); + } + } + while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr); + + *has_item = conn->read_ptr != conn->read_end; + return SVN_NO_ERROR; +} + svn_error_t * svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn, apr_pool_t *pool) @@ -1202,14 +1255,15 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po if (**fmt == '?') (*fmt)++; elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t); - if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER) - *va_arg(*ap, apr_uint64_t *) = elt->u.number; - else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER) - *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number; - else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING) - *va_arg(*ap, svn_string_t **) = elt->u.string; + if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST) + { + (*fmt)++; + SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap)); + } else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING) *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; else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD) *va_arg(*ap, const char **) = elt->u.word; else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD) @@ -1221,6 +1275,10 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po else break; } + else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER) + *va_arg(*ap, apr_uint64_t *) = elt->u.number; + else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER) + *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) @@ -1230,13 +1288,17 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po else break; } - else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST) - *va_arg(*ap, apr_array_header_t **) = elt->u.list; - else if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST) + else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD) { - (*fmt)++; - SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap)); + if (strcmp(elt->u.word, "true") == 0) + *va_arg(*ap, svn_tristate_t *) = svn_tristate_true; + else if (strcmp(elt->u.word, "false") == 0) + *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; else if (**fmt == ')') return SVN_NO_ERROR; else @@ -1268,6 +1330,9 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po case 'n': *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER; break; + case '3': + *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown; + break; case '(': nesting_level++; break; @@ -1337,21 +1402,21 @@ svn_ra_svn__parse_proplist(const apr_array_header_t *list, apr_pool_t *pool, apr_hash_t **props) { - char *name; + svn_string_t *name; svn_string_t *value; svn_ra_svn_item_t *elt; int i; - *props = apr_hash_make(pool); + *props = svn_hash__make(pool); for (i = 0; i < list->nelts; i++) { elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t); 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, "cs", + SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss", &name, &value)); - svn_hash_sets(*props, name, value); + apr_hash_set(*props, name->data, name->len, value); } return SVN_NO_ERROR; @@ -1447,7 +1512,7 @@ svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn, } else if (strcmp(status, "failure") == 0) { - return svn_ra_svn__handle_failure_status(params, pool); + return svn_error_trace(svn_ra_svn__handle_failure_status(params, pool)); } return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, @@ -1456,6 +1521,76 @@ svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn, } svn_error_t * +svn_ra_svn__has_command(svn_boolean_t *has_command, + svn_boolean_t *terminated, + svn_ra_svn_conn_t *conn, + apr_pool_t *pool) +{ + svn_error_t *err = svn_ra_svn__has_item(has_command, conn, pool); + if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) + { + *terminated = TRUE; + svn_error_clear(err); + return SVN_NO_ERROR; + } + + *terminated = FALSE; + return svn_error_trace(err); +} + +svn_error_t * +svn_ra_svn__handle_command(svn_boolean_t *terminate, + apr_hash_t *cmd_hash, + void *baton, + svn_ra_svn_conn_t *conn, + svn_boolean_t error_on_disconnect, + apr_pool_t *pool) +{ + const char *cmdname; + svn_error_t *err, *write_err; + apr_array_header_t *params; + const svn_ra_svn_cmd_entry_t *command; + + *terminate = FALSE; + err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, ¶ms); + if (err) + { + if (!error_on_disconnect + && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) + { + svn_error_clear(err); + *terminate = TRUE; + return SVN_NO_ERROR; + } + return err; + } + + command = svn_hash_gets(cmd_hash, cmdname); + if (command) + { + err = (*command->handler)(conn, pool, params, baton); + *terminate = command->terminate; + } + else + { + err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, + _("Unknown editor command '%s'"), cmdname); + err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL); + } + + if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR) + { + write_err = svn_ra_svn__write_cmd_failure( + conn, pool, + svn_ra_svn__locate_real_error_child(err)); + svn_error_clear(err); + return write_err ? write_err : SVN_NO_ERROR; + } + + return err; +} + +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, @@ -1464,10 +1599,7 @@ svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn, { apr_pool_t *subpool = svn_pool_create(pool); apr_pool_t *iterpool = svn_pool_create(subpool); - const char *cmdname; const svn_ra_svn_cmd_entry_t *command; - svn_error_t *err, *write_err; - apr_array_header_t *params; apr_hash_t *cmd_hash = apr_hash_make(subpool); for (command = commands; command->cmdname; command++) @@ -1475,43 +1607,18 @@ svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn, while (1) { + svn_boolean_t terminate; + svn_error_t *err; svn_pool_clear(iterpool); - err = svn_ra_svn__read_tuple(conn, iterpool, "wl", &cmdname, ¶ms); - if (err) - { - if (!error_on_disconnect - && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) - { - svn_error_clear(err); - svn_pool_destroy(subpool); - return SVN_NO_ERROR; - } - return err; - } - command = svn_hash_gets(cmd_hash, cmdname); - if (command) - err = (*command->handler)(conn, iterpool, params, baton); - else - { - err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, - _("Unknown editor command '%s'"), cmdname); - err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL); - } - - if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR) + err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn, + error_on_disconnect, iterpool); + if (err) { - write_err = svn_ra_svn__write_cmd_failure( - conn, iterpool, - svn_ra_svn__locate_real_error_child(err)); - svn_error_clear(err); - if (write_err) - return write_err; + svn_pool_destroy(subpool); + return svn_error_trace(err); } - else if (err) - return err; - - if (command && command->terminate) + if (terminate) break; } svn_pool_destroy(iterpool); @@ -1524,9 +1631,9 @@ svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( target-rev ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1537,12 +1644,12 @@ svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn, svn_revnum_t rev, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( open-root ( ", 14)); + 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(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1554,13 +1661,13 @@ svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn, svn_revnum_t rev, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( delete-entry ( ", 17)); + 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(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1574,10 +1681,10 @@ svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn, const char *copy_path, svn_revnum_t copy_rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( add-dir ( ", 12)); + SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( ")); SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token, copy_path, copy_rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1590,9 +1697,9 @@ svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn, const char *token, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( open-dir ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( ")); SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1604,9 +1711,9 @@ svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn, const char *name, const svn_string_t *value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-dir-prop ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( ")); SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1616,9 +1723,9 @@ svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( close-dir ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1629,9 +1736,9 @@ svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn, const char *path, const char *parent_token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( absent-dir ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( ")); SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1645,10 +1752,10 @@ svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn, const char *copy_path, svn_revnum_t copy_rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( add-file ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( ")); SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token, copy_path, copy_rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1661,9 +1768,9 @@ svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn, const char *token, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( open-file ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( ")); SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1675,9 +1782,9 @@ svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn, const char *name, const svn_string_t *value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-file-prop ( ", 21)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( ")); SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1688,12 +1795,12 @@ svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn, const char *token, const char *text_checksum) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( close-file ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( ")); SVN_ERR(write_tuple_cstring(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)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1704,9 +1811,9 @@ svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn, const char *path, const char *parent_token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( absent-file ( ", 16)); + SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( ")); SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1717,10 +1824,10 @@ svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn, const char *token, const svn_string_t *chunk) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( textdelta-chunk ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); SVN_ERR(write_tuple_string(conn, pool, chunk)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1730,9 +1837,9 @@ svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( textdelta-end ( ", 18)); + SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1743,12 +1850,12 @@ svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn, const char *token, const char *base_checksum) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( apply-textdelta ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( ")); SVN_ERR(write_tuple_cstring(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)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1757,14 +1864,14 @@ svn_error_t * svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( close-edit ( ) ) ", 19); + return writebuf_write_literal(conn, pool, "( close-edit ( ) ) "); } svn_error_t * svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( abort-edit ( ) ) ", 19); + return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) "); } svn_error_t * @@ -1776,7 +1883,7 @@ svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn, const char *lock_token, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( set-path ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_boolean(conn, pool, start_empty)); @@ -1784,7 +1891,7 @@ svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1794,9 +1901,9 @@ svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *path) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( delete-path ( ", 16)); + SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1811,7 +1918,7 @@ svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn, const char *lock_token, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( link-path ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_cstring(conn, pool, url)); SVN_ERR(write_tuple_revision(conn, pool, rev)); @@ -1820,7 +1927,7 @@ svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1829,14 +1936,14 @@ svn_error_t * svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( finish-report ( ) ) ", 22); + return writebuf_write_literal(conn, pool, "( finish-report ( ) ) "); } svn_error_t * svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( abort-report ( ) ) ", 21); + return writebuf_write_literal(conn, pool, "( abort-report ( ) ) "); } svn_error_t * @@ -1844,9 +1951,9 @@ svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *url) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( reparent ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( ")); SVN_ERR(write_tuple_cstring(conn, pool, url)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1855,7 +1962,7 @@ svn_error_t * svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( get-latest-rev ( ) ) ", 23); + return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) "); } svn_error_t * @@ -1863,9 +1970,9 @@ svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_time_t tm) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-dated-rev ( ", 18)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( ")); SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool))); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1879,7 +1986,7 @@ svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn, svn_boolean_t dont_care, const svn_string_t *old_value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-rev-prop2 ( ", 21)); + 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)); @@ -1889,7 +1996,7 @@ svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_boolean(conn, pool, dont_care)); SVN_ERR(write_tuple_string_opt(conn, pool, old_value)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1901,11 +2008,11 @@ svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn, const char *name, const svn_string_t *value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-rev-prop ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_cstring(conn, pool, name)); SVN_ERR(write_tuple_string_opt(conn, pool, value)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1915,9 +2022,9 @@ svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( rev-proplist ( ", 17)); + SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1928,10 +2035,10 @@ svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn, svn_revnum_t rev, const char *name) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( rev-prop ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_cstring(conn, pool, name)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1944,14 +2051,18 @@ svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn, svn_boolean_t props, svn_boolean_t stream) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-file ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( ")); 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_boolean(conn, pool, props)); SVN_ERR(write_tuple_boolean(conn, pool, stream)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + + /* 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 + to see "true" if it is omitted. */ + SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) ")); return SVN_NO_ERROR; } @@ -1966,7 +2077,7 @@ svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn, svn_boolean_t send_copyfrom_args, svn_boolean_t ignore_ancestry) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( update ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( update ( ")); 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)); @@ -1975,7 +2086,7 @@ svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_depth(conn, pool, depth)); SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args)); SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1991,7 +2102,7 @@ svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn, svn_boolean_t send_copyfrom_args, svn_boolean_t ignore_ancestry) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( switch ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( ")); 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)); @@ -2001,7 +2112,7 @@ svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_depth(conn, pool, depth)); SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args)); SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2014,14 +2125,14 @@ svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn, svn_revnum_t rev, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( status ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( status ( ")); SVN_ERR(write_tuple_cstring(conn, pool, target)); SVN_ERR(write_tuple_boolean(conn, pool, recurse)); 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_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2037,7 +2148,7 @@ svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn, svn_boolean_t text_deltas, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( diff ( ", 9)); + SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( ")); 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)); @@ -2047,7 +2158,7 @@ svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_cstring(conn, pool, versus_url)); SVN_ERR(write_tuple_boolean(conn, pool, text_deltas)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2058,12 +2169,12 @@ svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn, const char *path, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( check-path ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( ")); 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(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2074,12 +2185,12 @@ svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn, const char *path, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( stat ( ", 9)); + SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( ")); 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(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2092,7 +2203,7 @@ svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn, svn_revnum_t end, svn_boolean_t include_merged_revisions) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-file-revs ( ", 18)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, start)); @@ -2101,7 +2212,7 @@ svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_revision_opt(conn, pool, end)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2114,7 +2225,7 @@ svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn, svn_boolean_t steal_lock, svn_revnum_t revnum) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( lock ( ", 9)); + SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_cstring_opt(conn, pool, comment)); @@ -2123,7 +2234,7 @@ svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, revnum)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2135,13 +2246,13 @@ svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn, const char *token, svn_boolean_t break_lock) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( unlock ( ", 11)); + 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_boolean(conn, pool, break_lock)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2151,9 +2262,9 @@ svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *path) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-lock ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2164,12 +2275,12 @@ svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn, const char *path, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-locks ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2181,11 +2292,11 @@ svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn, svn_revnum_t low_water_mark, svn_boolean_t send_deltas) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( replay ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_revision(conn, pool, low_water_mark)); SVN_ERR(write_tuple_boolean(conn, pool, send_deltas)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2198,12 +2309,12 @@ svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn, svn_revnum_t low_water_mark, svn_boolean_t send_deltas) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( replay-range ( ", 17)); + SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( ")); SVN_ERR(write_tuple_revision(conn, pool, start_revision)); SVN_ERR(write_tuple_revision(conn, pool, end_revision)); SVN_ERR(write_tuple_revision(conn, pool, low_water_mark)); SVN_ERR(write_tuple_boolean(conn, pool, send_deltas)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2215,11 +2326,11 @@ svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn, svn_revnum_t peg_revision, svn_revnum_t end_revision) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-deleted-rev ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_revision(conn, pool, peg_revision)); SVN_ERR(write_tuple_revision(conn, pool, end_revision)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2230,12 +2341,12 @@ svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn, const char *path, svn_revnum_t revision) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-iprops ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, revision)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2244,7 +2355,7 @@ svn_error_t * svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( finish-replay ( ) ) ", 22); + return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) "); } svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, @@ -2254,7 +2365,7 @@ svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, va_list ap; svn_error_t *err; - SVN_ERR(writebuf_write_short_string(conn, pool, "( success ", 10)); + SVN_ERR(writebuf_write_literal(conn, pool, "( success ")); va_start(ap, fmt); err = vwrite_tuple(conn, pool, fmt, &ap); va_end(ap); @@ -2262,10 +2373,11 @@ svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, } svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, svn_error_t *err) + apr_pool_t *pool, + const svn_error_t *err) { char buffer[128]; - SVN_ERR(writebuf_write_short_string(conn, pool, "( failure ( ", 12)); + SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( ")); for (; err; err = err->child) { const char *msg; @@ -2285,5 +2397,217 @@ svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn, err->file ? err->file : "", (apr_uint64_t) err->line)); } - return writebuf_write_short_string(conn, pool, ") ) ", 4); + return writebuf_write_literal(conn, pool, ") ) "); +} + +svn_error_t * +svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const char *path, + char action, + const char *copyfrom_path, + svn_revnum_t copyfrom_rev, + svn_node_kind_t node_kind, + svn_boolean_t text_modified, + svn_boolean_t props_modified) +{ + SVN_ERR(write_tuple_start_list(conn, pool)); + + 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)); + + return writebuf_write_literal(conn, pool, ") ) "); +} + +svn_error_t * +svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_revnum_t revision, + const svn_string_t *author, + const svn_string_t *date, + const svn_string_t *message, + svn_boolean_t has_children, + svn_boolean_t invalid_revnum, + 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_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)); + + return SVN_NO_ERROR; +} + +/* If condition COND is not met, return a "malformed network data" error. + */ +#define CHECK_PROTOCOL_COND(cond)\ + if (!(cond)) \ + return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \ + _("Malformed network data")); + +/* 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, + int idx, + svn_string_t **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING); + *result = elt->u.string; + + return SVN_NO_ERROR; +} + +/* 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, + int idx, + const char **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING); + *result = elt->u.string->data; + + return SVN_NO_ERROR; +} + +/* 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, + int idx, + const char **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD); + *result = elt->u.word; + + return SVN_NO_ERROR; +} + +/* 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, + int idx, + svn_revnum_t *result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER); + *result = (svn_revnum_t)elt->u.number; + + return SVN_NO_ERROR; +} + +/* 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, + int idx, + apr_uint64_t *result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD); + if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0) + *result = TRUE; + else if (strcmp(elt->u.word, "false") == 0) + *result = FALSE; + else + CHECK_PROTOCOL_COND(FALSE); + + return SVN_NO_ERROR; +} + +/* 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, + int idx, + const apr_array_header_t **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_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, + int min, + int max) +{ + CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items, + svn_string_t **cpath, + const char **action, + const char **copy_path, + svn_revnum_t *copy_rev, + const char **kind_str, + apr_uint64_t *text_mods, + apr_uint64_t *prop_mods) +{ + const apr_array_header_t *sub_items; + + /* initialize optional values */ + *copy_path = NULL; + *copy_rev = SVN_INVALID_REVNUM; + *kind_str = NULL; + *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER; + *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER; + + /* top-level elements (mandatory) */ + SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX)); + SVN_ERR(svn_ra_svn__read_string(items, 0, cpath)); + SVN_ERR(svn_ra_svn__read_word(items, 1, action)); + + /* first sub-structure (mandatory) */ + SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items)); + if (sub_items->nelts) + { + SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2)); + SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path)); + SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev)); + } + + /* second sub-structure (optional) */ + if (items->nelts >= 4) + { + SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items)); + switch (MIN(3, sub_items->nelts)) + { + case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods)); + case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods)); + case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str)); + default: break; + } + } + + return SVN_NO_ERROR; } diff --git a/subversion/libsvn_ra_svn/protocol b/subversion/libsvn_ra_svn/protocol index 4d98b2bb19d56..dfc1f3dc16591 100644 --- a/subversion/libsvn_ra_svn/protocol +++ b/subversion/libsvn_ra_svn/protocol @@ -303,18 +303,20 @@ second place for auth-request point as noted below. get-file params: ( path:string [ rev:number ] want-props:bool want-contents:bool - [ want-iprops:bool ] ) + ? want-iprops:bool ) response: ( [ checksum:string ] rev:number props:proplist [ inherited-props:iproplist ] ) If want-contents is specified, then after sending response, server sends file contents as a series of strings, terminated by the empty string, followed by a second empty command response to indicate whether an error occurred during the sending of the file. - NOTE: the standard client never sends want-iprops, it uses get-iprops. + NOTE: the standard client doesn't send want-iprops as true, it uses + get-iprops, but does send want-iprops as false to workaround a server + bug in 1.8.0-1.8.8. get-dir params: ( path:string [ rev:number ] want-props:bool want-contents:bool - ? ( field:dirent-field ... ) [ want-iprops:bool ] ) + ? ( field:dirent-field ... ) ? want-iprops:bool ) response: ( rev:number props:proplist ( entry:dirent ... ) [ inherited-props:iproplist ] )] dirent: ( name:string kind:node-kind size:number has-props:bool @@ -322,7 +324,9 @@ second place for auth-request point as noted below. [ last-author:string ] ) dirent-field: kind | size | has-props | created-rev | time | last-author | word - NOTE: the standard client never sends want-iprops, it uses get-iprops. + NOTE: the standard client doesn't send want-iprops as true, it uses + get-iprops, but does send want-iprops as false to workaround a server + bug in 1.8.0-1.8.8. check-path params: ( path:string [ rev:number ] ) @@ -339,7 +343,7 @@ second place for auth-request point as noted below. get-mergeinfo params: ( ( path:string ... ) [ rev:number ] inherit:word - descendents:bool) + descendants:bool) response: ( ( ( path:string merge-info:string ) ... ) ) New in svn 1.5. If no paths are specified, an empty response is returned. If rev is not specified, the youngest revision is used. @@ -597,7 +601,13 @@ desirability: * The protocol version may be bumped. Clients and servers can then choose to any range of protocol versions. -4.1. Extending existing commands +4.1. Limitations + +The current implementation limits the length of a word to 31 characters. +Longer words, such as capability names, will be cause an error on the +receiver side. + +4.2. Extending existing commands Extending an existing command is normally done by indicating that its tuple is allowed to end where it currently ends, for backwards diff --git a/subversion/libsvn_ra_svn/ra_svn.h b/subversion/libsvn_ra_svn/ra_svn.h index dc70eb72fb31f..d9fe1b2758c9d 100644 --- a/subversion/libsvn_ra_svn/ra_svn.h +++ b/subversion/libsvn_ra_svn/ra_svn.h @@ -123,13 +123,16 @@ struct svn_ra_svn__session_baton_t { apr_pool_t *pool; svn_ra_svn_conn_t *conn; svn_boolean_t is_tunneled; + svn_auth_baton_t *auth_baton; const char *url; const char *user; const char *hostname; /* The remote hostname. */ const char *realm_prefix; + const char *tunnel_name; const char **tunnel_argv; const svn_ra_callbacks2_t *callbacks; void *callbacks_baton; + apr_hash_t *config; apr_off_t bytes_read, bytes_written; /* apr_off_t's because that's what the callback interface uses */ const char *useragent; @@ -145,8 +148,8 @@ void svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn, void *baton); /* Return true if there is input waiting on conn. */ -svn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn, - apr_pool_t *pool); +svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn, + svn_boolean_t *data_available); /* CRAM-MD5 client implementation. */ svn_error_t *svn_ra_svn__cram_client(svn_ra_svn_conn_t *conn, apr_pool_t *pool, @@ -169,20 +172,20 @@ svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params, svn_ra_svn__stream_t *svn_ra_svn__stream_from_sock(apr_socket_t *sock, apr_pool_t *pool); -/* Returns a stream that reads from IN_FILE and writes to OUT_FILE. */ -svn_ra_svn__stream_t *svn_ra_svn__stream_from_files(apr_file_t *in_file, - apr_file_t *out_file, - apr_pool_t *pool); +/* Returns a stream that reads from IN_STREAM and writes to OUT_STREAM, + creating a timeout callback for OUT_STREAM if possible */ +svn_ra_svn__stream_t *svn_ra_svn__stream_from_streams(svn_stream_t *in_stream, + svn_stream_t *out_stream, + apr_pool_t *pool); /* Create an svn_ra_svn__stream_t using READ_CB, WRITE_CB, TIMEOUT_CB, * PENDING_CB, and BATON. */ -svn_ra_svn__stream_t *svn_ra_svn__stream_create(void *baton, - svn_read_fn_t read_cb, - svn_write_fn_t write_cb, +svn_ra_svn__stream_t *svn_ra_svn__stream_create(svn_stream_t *in_stream, + svn_stream_t *out_stream, + void *timeout_baton, ra_svn_timeout_fn_t timeout_cb, - ra_svn_pending_fn_t pending_cb, - apr_pool_t *pool); + apr_pool_t *result_pool); /* Write *LEN bytes from DATA to STREAM, returning the number of bytes * written in *LEN. @@ -208,7 +211,9 @@ void svn_ra_svn__stream_timeout(svn_ra_svn__stream_t *stream, apr_interval_time_t interval); /* Return whether or not there is data pending on STREAM. */ -svn_boolean_t svn_ra_svn__stream_pending(svn_ra_svn__stream_t *stream); +svn_error_t * +svn_ra_svn__stream_data_available(svn_ra_svn__stream_t *stream, + svn_boolean_t *data_available); /* Respond to an auth request and perform authentication. Use the Cyrus * SASL library for mechanism negotiation and for creating authentication diff --git a/subversion/libsvn_ra_svn/streams.c b/subversion/libsvn_ra_svn/streams.c index 4ae93d5750030..3ad792bcdeb50 100644 --- a/subversion/libsvn_ra_svn/streams.c +++ b/subversion/libsvn_ra_svn/streams.c @@ -33,12 +33,14 @@ #include "svn_io.h" #include "svn_private_config.h" +#include "private/svn_io_private.h" + #include "ra_svn.h" struct svn_ra_svn__stream_st { - svn_stream_t *stream; - void *baton; - ra_svn_pending_fn_t pending_fn; + svn_stream_t *in_stream; + svn_stream_t *out_stream; + void *timeout_baton; ra_svn_timeout_fn_t timeout_fn; }; @@ -47,11 +49,6 @@ typedef struct sock_baton_t { apr_pool_t *pool; } sock_baton_t; -typedef struct file_baton_t { - apr_file_t *in_file; - apr_file_t *out_file; - apr_pool_t *pool; -} file_baton_t; /* Returns TRUE if PFD has pending data, FALSE otherwise. */ static svn_boolean_t pending(apr_pollfd_t *pfd, apr_pool_t *pool) @@ -67,65 +64,34 @@ static svn_boolean_t pending(apr_pollfd_t *pfd, apr_pool_t *pool) /* Functions to implement a file backed svn_ra_svn__stream_t. */ -/* Implements svn_read_fn_t */ -static svn_error_t * -file_read_cb(void *baton, char *buffer, apr_size_t *len) -{ - file_baton_t *b = baton; - apr_status_t status = apr_file_read(b->in_file, buffer, len); - - if (status && !APR_STATUS_IS_EOF(status)) - return svn_error_wrap_apr(status, _("Can't read from connection")); - if (*len == 0) - return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL); - return SVN_NO_ERROR; -} - -/* Implements svn_write_fn_t */ -static svn_error_t * -file_write_cb(void *baton, const char *buffer, apr_size_t *len) -{ - file_baton_t *b = baton; - apr_status_t status = apr_file_write(b->out_file, buffer, len); - if (status) - return svn_error_wrap_apr(status, _("Can't write to connection")); - return SVN_NO_ERROR; -} - /* Implements ra_svn_timeout_fn_t */ static void file_timeout_cb(void *baton, apr_interval_time_t interval) { - file_baton_t *b = baton; - apr_file_pipe_timeout_set(b->out_file, interval); -} - -/* Implements ra_svn_pending_fn_t */ -static svn_boolean_t -file_pending_cb(void *baton) -{ - file_baton_t *b = baton; - apr_pollfd_t pfd; - - pfd.desc_type = APR_POLL_FILE; - pfd.desc.f = b->in_file; + apr_file_t *f = baton; - return pending(&pfd, b->pool); + if (f) + apr_file_pipe_timeout_set(f, interval); } svn_ra_svn__stream_t * -svn_ra_svn__stream_from_files(apr_file_t *in_file, - apr_file_t *out_file, - apr_pool_t *pool) +svn_ra_svn__stream_from_streams(svn_stream_t *in_stream, + svn_stream_t *out_stream, + apr_pool_t *pool) { - file_baton_t *b = apr_palloc(pool, sizeof(*b)); + apr_file_t *file; + + /* If out_stream is backed by an apr_file (e.g. an PIPE) we + provide a working callback, otherwise the callback ignores + the timeout. - b->in_file = in_file; - b->out_file = out_file; - b->pool = pool; + The callback is used to make the write non-blocking on + some error scenarios. ### This (legacy) usage + breaks the stream promise */ + file = svn_stream__aprfile(out_stream); - return svn_ra_svn__stream_create(b, file_read_cb, file_write_cb, - file_timeout_cb, file_pending_cb, + return svn_ra_svn__stream_create(in_stream, out_stream, + file, file_timeout_cb, pool); } @@ -155,8 +121,6 @@ sock_read_cb(void *baton, char *buffer, apr_size_t *len) if (status && !APR_STATUS_IS_EOF(status)) return svn_error_wrap_apr(status, _("Can't read from connection")); - if (*len == 0) - return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL); return SVN_NO_ERROR; } @@ -179,9 +143,10 @@ sock_timeout_cb(void *baton, apr_interval_time_t interval) apr_socket_timeout_set(b->sock, interval); } -/* Implements ra_svn_pending_fn_t */ -static svn_boolean_t -sock_pending_cb(void *baton) +/* Implements svn_stream_data_available_fn_t */ +static svn_error_t * +sock_pending_cb(void *baton, + svn_boolean_t *data_available) { sock_baton_t *b = baton; apr_pollfd_t pfd; @@ -189,41 +154,45 @@ sock_pending_cb(void *baton) pfd.desc_type = APR_POLL_SOCKET; pfd.desc.s = b->sock; - return pending(&pfd, b->pool); + *data_available = pending(&pfd, b->pool); + + svn_pool_clear(b->pool); + + return SVN_NO_ERROR; } svn_ra_svn__stream_t * svn_ra_svn__stream_from_sock(apr_socket_t *sock, - apr_pool_t *pool) + apr_pool_t *result_pool) { - sock_baton_t *b = apr_palloc(pool, sizeof(*b)); + sock_baton_t *b = apr_palloc(result_pool, sizeof(*b)); + svn_stream_t *sock_stream; b->sock = sock; - b->pool = pool; + b->pool = svn_pool_create(result_pool); - return svn_ra_svn__stream_create(b, sock_read_cb, sock_write_cb, - sock_timeout_cb, sock_pending_cb, - pool); + sock_stream = svn_stream_create(b, result_pool); + + svn_stream_set_read2(sock_stream, sock_read_cb, NULL /* use default */); + svn_stream_set_write(sock_stream, sock_write_cb); + svn_stream_set_data_available(sock_stream, sock_pending_cb); + + return svn_ra_svn__stream_create(sock_stream, sock_stream, + b, sock_timeout_cb, result_pool); } svn_ra_svn__stream_t * -svn_ra_svn__stream_create(void *baton, - svn_read_fn_t read_cb, - svn_write_fn_t write_cb, +svn_ra_svn__stream_create(svn_stream_t *in_stream, + svn_stream_t *out_stream, + void *timeout_baton, ra_svn_timeout_fn_t timeout_cb, - ra_svn_pending_fn_t pending_cb, apr_pool_t *pool) { svn_ra_svn__stream_t *s = apr_palloc(pool, sizeof(*s)); - s->stream = svn_stream_empty(pool); - svn_stream_set_baton(s->stream, baton); - if (read_cb) - svn_stream_set_read(s->stream, read_cb); - if (write_cb) - svn_stream_set_write(s->stream, write_cb); - s->baton = baton; + s->in_stream = in_stream; + s->out_stream = out_stream; + s->timeout_baton = timeout_baton; s->timeout_fn = timeout_cb; - s->pending_fn = pending_cb; return s; } @@ -231,25 +200,33 @@ svn_error_t * svn_ra_svn__stream_write(svn_ra_svn__stream_t *stream, const char *data, apr_size_t *len) { - return svn_stream_write(stream->stream, data, len); + return svn_error_trace(svn_stream_write(stream->out_stream, data, len)); } svn_error_t * svn_ra_svn__stream_read(svn_ra_svn__stream_t *stream, char *data, apr_size_t *len) { - return svn_stream_read(stream->stream, data, len); + SVN_ERR(svn_stream_read2(stream->in_stream, data, len)); + + if (*len == 0) + return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL); + + return SVN_NO_ERROR; } void svn_ra_svn__stream_timeout(svn_ra_svn__stream_t *stream, apr_interval_time_t interval) { - stream->timeout_fn(stream->baton, interval); + stream->timeout_fn(stream->timeout_baton, interval); } -svn_boolean_t -svn_ra_svn__stream_pending(svn_ra_svn__stream_t *stream) +svn_error_t * +svn_ra_svn__stream_data_available(svn_ra_svn__stream_t *stream, + svn_boolean_t *data_available) { - return stream->pending_fn(stream->baton); + return svn_error_trace( + svn_stream_data_available(stream->in_stream, + data_available)); } |