diff options
Diffstat (limited to 'subversion/svnserve/serve.c')
-rw-r--r-- | subversion/svnserve/serve.c | 979 |
1 files changed, 660 insertions, 319 deletions
diff --git a/subversion/svnserve/serve.c b/subversion/svnserve/serve.c index 91f1d5af961c6..5192e7c9a66f1 100644 --- a/subversion/svnserve/serve.c +++ b/subversion/svnserve/serve.c @@ -87,6 +87,9 @@ typedef struct log_baton_t { const char *fs_path; svn_ra_svn_conn_t *conn; int stack_depth; + + /* Set to TRUE when at least one changed path has been sent. */ + svn_boolean_t started; } log_baton_t; typedef struct file_revs_baton_t { @@ -223,8 +226,8 @@ load_pwdb_config(repository_t *repository, pwdb_path = svn_dirent_internal_style(pwdb_path, pool); pwdb_path = svn_dirent_join(repository->base, pwdb_path, pool); - err = svn_repos__config_pool_get(&repository->pwdb, NULL, config_pool, - pwdb_path, TRUE, FALSE, + err = svn_repos__config_pool_get(&repository->pwdb, config_pool, + pwdb_path, TRUE, repository->repos, pool); if (err) { @@ -282,8 +285,8 @@ canonicalize_access_file(const char **access_file, repository_t *repository, return SVN_NO_ERROR; } -/* Load the authz database for the listening server through AUTHZ_POOL - based on the entries in the SERVER struct. +/* Load the authz database for the listening server based on the entries + in the SERVER struct. SERVER and CONN must not be NULL. The real errors will be logged with SERVER and CONN but return generic errors to the client. */ @@ -291,7 +294,6 @@ static svn_error_t * load_authz_config(repository_t *repository, const char *repos_root, svn_config_t *cfg, - svn_repos__authz_pool_t *authz_pool, apr_pool_t *pool) { const char *authzdb_path; @@ -319,9 +321,9 @@ load_authz_config(repository_t *repository, repos_root, pool); if (!err) - err = svn_repos__authz_pool_get(&repository->authzdb, authz_pool, - authzdb_path, groupsdb_path, TRUE, - repository->repos, pool); + err = svn_repos_authz_read3(&repository->authzdb, authzdb_path, + groupsdb_path, TRUE, repository->repos, + pool, pool); if (err) return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, err, NULL); @@ -443,7 +445,7 @@ static svn_error_t *authz_check_access(svn_boolean_t *allowed, absolute path. Passing such a malformed path to the authz routines throws them into an infinite loop and makes them miss ACLs. */ - if (path) + if (path && *path != '/') path = svn_fspath__canonicalize(path, pool); /* If we have a username, and we've not yet used it + any username @@ -627,11 +629,12 @@ create_fs_access(server_baton_t *b, apr_pool_t *pool) * On authentication failure, report failure to the client and set * *success to FALSE. On communications failure, return an error. * If NEEDS_USERNAME is TRUE, don't allow anonymous authentication. */ -static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, +static svn_error_t *auth(svn_boolean_t *success, + svn_ra_svn_conn_t *conn, const char *mech, const char *mecharg, server_baton_t *b, enum access_type required, svn_boolean_t needs_username, - svn_boolean_t *success) + apr_pool_t *scratch_pool) { const char *user; *success = FALSE; @@ -640,10 +643,10 @@ static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, && b->client_info->tunnel_user && strcmp(mech, "EXTERNAL") == 0) { if (*mecharg && strcmp(mecharg, b->client_info->tunnel_user) != 0) - return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", + return svn_ra_svn__write_tuple(conn, scratch_pool, "w(c)", "failure", "Requested username does not match"); b->client_info->user = b->client_info->tunnel_user; - SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success")); + SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w()", "success")); *success = TRUE; return SVN_NO_ERROR; } @@ -651,7 +654,7 @@ static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, if (b->repository->anon_access >= required && strcmp(mech, "ANONYMOUS") == 0 && ! needs_username) { - SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success")); + SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w()", "success")); *success = TRUE; return SVN_NO_ERROR; } @@ -659,13 +662,13 @@ static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, if (b->repository->auth_access >= required && b->repository->pwdb && strcmp(mech, "CRAM-MD5") == 0) { - SVN_ERR(svn_ra_svn_cram_server(conn, pool, b->repository->pwdb, + SVN_ERR(svn_ra_svn_cram_server(conn, scratch_pool, b->repository->pwdb, &user, success)); b->client_info->user = apr_pstrdup(b->pool, user); return SVN_NO_ERROR; } - return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", + return svn_ra_svn__write_tuple(conn, scratch_pool, "w(c)", "failure", "Must authenticate with listed mechanism"); } @@ -677,19 +680,26 @@ internal_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { svn_boolean_t success; const char *mech, *mecharg; + apr_pool_t *iterpool; SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); SVN_ERR(send_mechs(conn, pool, b, required, needs_username)); SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->repository->realm)); + + iterpool = svn_pool_create(pool); do { + svn_pool_clear(iterpool); + SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?c)", &mech, &mecharg)); if (!*mech) break; - SVN_ERR(auth(conn, pool, mech, mecharg, b, required, needs_username, - &success)); + SVN_ERR(auth(&success, conn, mech, mecharg, b, required, + needs_username, iterpool)); } while (!success); + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; } @@ -830,7 +840,7 @@ static svn_error_t *must_have_access(svn_ra_svn_conn_t *conn, */ static svn_error_t *set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) + svn_ra_svn__list_t *params, void *baton) { report_driver_baton_t *b = baton; const char *path, *lock_token, *depth_word; @@ -839,9 +849,9 @@ static svn_error_t *set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_depth_t depth = svn_depth_infinity; svn_boolean_t start_empty; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crb?(?c)?w", - &path, &rev, &start_empty, &lock_token, - &depth_word)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "crb?(?c)?w", + &path, &rev, &start_empty, &lock_token, + &depth_word)); if (depth_word) depth = svn_depth_from_word(depth_word); path = svn_relpath_canonicalize(path, pool); @@ -857,12 +867,12 @@ static svn_error_t *set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } static svn_error_t *delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) + svn_ra_svn__list_t *params, void *baton) { report_driver_baton_t *b = baton; const char *path; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &path)); path = svn_relpath_canonicalize(path, pool); if (!b->err) b->err = svn_repos_delete_path(b->report_baton, path, pool); @@ -870,7 +880,7 @@ static svn_error_t *delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } static svn_error_t *link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) + svn_ra_svn__list_t *params, void *baton) { report_driver_baton_t *b = baton; const char *path, *url, *lock_token, *fs_path, *depth_word; @@ -879,7 +889,7 @@ static svn_error_t *link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, /* Default to infinity, for old clients that don't send depth. */ svn_depth_t depth = svn_depth_infinity; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccrb?(?c)?w", + SVN_ERR(svn_ra_svn__parse_tuple(params, "ccrb?(?c)?w", &path, &url, &rev, &start_empty, &lock_token, &depth_word)); @@ -901,7 +911,7 @@ static svn_error_t *link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } static svn_error_t *finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) + svn_ra_svn__list_t *params, void *baton) { report_driver_baton_t *b = baton; @@ -912,8 +922,11 @@ static svn_error_t *finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *abort_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +abort_report(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { report_driver_baton_t *b = baton; @@ -922,12 +935,12 @@ static svn_error_t *abort_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static const svn_ra_svn_cmd_entry_t report_commands[] = { +static const svn_ra_svn__cmd_entry_t report_commands[] = { { "set-path", set_path }, { "delete-path", delete_path }, { "link-path", link_path }, - { "finish-report", finish_report, TRUE }, - { "abort-report", abort_report, TRUE }, + { "finish-report", finish_report, NULL, TRUE }, + { "abort-report", abort_report, NULL, TRUE }, { NULL } }; @@ -1092,14 +1105,17 @@ get_props(apr_hash_t **props, } /* Set BATON->FS_PATH for the repository URL found in PARAMS. */ -static svn_error_t *reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +reparent(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; const char *url; const char *fs_path; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &url)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &url)); url = svn_uri_canonicalize(url, pool); SVN_ERR(trivial_auth_request(conn, pool, b)); SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, pool), @@ -1111,8 +1127,11 @@ static svn_error_t *reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +get_latest_rev(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -1125,15 +1144,18 @@ static svn_error_t *get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +get_dated_rev(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; apr_time_t tm; const char *timestr; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", ×tr)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "c", ×tr)); SVN_ERR(log_command(b, conn, pool, "get-dated-rev %s", timestr)); SVN_ERR(trivial_auth_request(conn, pool, b)); @@ -1171,8 +1193,11 @@ static svn_error_t *do_change_rev_prop(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *change_rev_prop2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +change_rev_prop2(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -1182,7 +1207,7 @@ static svn_error_t *change_rev_prop2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_string_t *old_value; svn_boolean_t dont_care; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc(?s)(b?s)", + SVN_ERR(svn_ra_svn__parse_tuple(params, "rc(?s)(b?s)", &rev, &name, &value, &dont_care, &old_value)); @@ -1208,8 +1233,11 @@ static svn_error_t *change_rev_prop2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *change_rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +change_rev_prop(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -1218,15 +1246,18 @@ static svn_error_t *change_rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, /* Because the revprop value was at one time mandatory, the usual optional element pattern "(?s)" isn't used. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc?s", &rev, &name, &value)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "rc?s", &rev, &name, &value)); SVN_ERR(do_change_rev_prop(conn, b, rev, name, NULL, value, pool)); return SVN_NO_ERROR; } -static svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +rev_proplist(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -1236,7 +1267,7 @@ static svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, ab.server = b; ab.conn = conn; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "r", &rev)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "r", &rev)); SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_proplist(rev, pool))); SVN_ERR(trivial_auth_request(conn, pool, b)); @@ -1250,8 +1281,11 @@ static svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +rev_prop(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -1262,7 +1296,7 @@ static svn_error_t *rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, ab.server = b; ab.conn = conn; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc", &rev, &name)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "rc", &rev, &name)); SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_prop(rev, name, pool))); @@ -1291,14 +1325,15 @@ static svn_error_t *commit_done(const svn_commit_info_t *commit_info, /* Add the LOCK_TOKENS (if any) to the filesystem access context, * checking path authorizations using the state in SB as we go. - * LOCK_TOKENS is an array of svn_ra_svn_item_t structs. Return a + * LOCK_TOKENS is an array of svn_ra_svn__item_t structs. Return a * client error if LOCK_TOKENS is not a list of lists. If a lock * violates the authz configuration, return SVN_ERR_RA_NOT_AUTHORIZED * to the client. Use POOL for temporary allocations only. */ -static svn_error_t *add_lock_tokens(const apr_array_header_t *lock_tokens, - server_baton_t *sb, - apr_pool_t *pool) +static svn_error_t * +add_lock_tokens(const svn_ra_svn__list_t *lock_tokens, + server_baton_t *sb, + apr_pool_t *pool) { int i; svn_fs_access_t *fs_access; @@ -1312,24 +1347,23 @@ static svn_error_t *add_lock_tokens(const apr_array_header_t *lock_tokens, for (i = 0; i < lock_tokens->nelts; ++i) { const char *path, *token, *full_path; - svn_ra_svn_item_t *path_item, *token_item; - svn_ra_svn_item_t *item = &APR_ARRAY_IDX(lock_tokens, i, - svn_ra_svn_item_t); + svn_ra_svn__item_t *path_item, *token_item; + svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(lock_tokens, i); if (item->kind != SVN_RA_SVN_LIST) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, "Lock tokens aren't a list of lists"); - path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t); + path_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 0); if (path_item->kind != SVN_RA_SVN_STRING) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, "Lock path isn't a string"); - token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t); + token_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 1); if (token_item->kind != SVN_RA_SVN_STRING) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, "Lock token isn't a string"); - path = path_item->u.string->data; + path = path_item->u.string.data; full_path = svn_fspath__join(sb->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -1338,7 +1372,7 @@ static svn_error_t *add_lock_tokens(const apr_array_header_t *lock_tokens, return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL, sb); - token = token_item->u.string->data; + token = token_item->u.string.data; SVN_ERR(svn_fs_access_add_lock_token2(fs_access, path, token)); } @@ -1361,10 +1395,11 @@ lock_cb(void *baton, } /* Unlock the paths with lock tokens in LOCK_TOKENS, ignoring any errors. - LOCK_TOKENS contains svn_ra_svn_item_t elements, assumed to be lists. */ -static svn_error_t *unlock_paths(const apr_array_header_t *lock_tokens, - server_baton_t *sb, - apr_pool_t *pool) + LOCK_TOKENS contains svn_ra_svn__item_t elements, assumed to be lists. */ +static svn_error_t * +unlock_paths(const svn_ra_svn__list_t *lock_tokens, + server_baton_t *sb, + apr_pool_t *pool) { int i; apr_pool_t *subpool = svn_pool_create(pool); @@ -1373,18 +1408,18 @@ static svn_error_t *unlock_paths(const apr_array_header_t *lock_tokens, for (i = 0; i < lock_tokens->nelts; ++i) { - svn_ra_svn_item_t *item, *path_item, *token_item; + svn_ra_svn__item_t *item, *path_item, *token_item; const char *path, *token, *full_path; - item = &APR_ARRAY_IDX(lock_tokens, i, svn_ra_svn_item_t); - path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t); - token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t); + item = &SVN_RA_SVN__LIST_ITEM(lock_tokens, i); + path_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 0); + token_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 1); - path = path_item->u.string->data; + path = path_item->u.string.data; full_path = svn_fspath__join(sb->repository->fs_path->data, svn_relpath_canonicalize(path, subpool), subpool); - token = token_item->u.string->data; + token = token_item->u.string.data; svn_hash_sets(targets, full_path, token); } @@ -1401,17 +1436,20 @@ static svn_error_t *unlock_paths(const apr_array_header_t *lock_tokens, return SVN_NO_ERROR; } -static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +commit(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; const char *log_msg, *date = NULL, *author = NULL, *post_commit_err = NULL; - apr_array_header_t *lock_tokens; + svn_ra_svn__list_t *lock_tokens; svn_boolean_t keep_locks; - apr_array_header_t *revprop_list; + svn_ra_svn__list_t *revprop_list; apr_hash_t *revprop_table; const svn_delta_editor_t *editor; void *edit_baton; @@ -1427,7 +1465,7 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { /* Clients before 1.2 don't send lock-tokens, keep-locks, and rev-props fields. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &log_msg)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &log_msg)); lock_tokens = NULL; keep_locks = TRUE; revprop_list = NULL; @@ -1435,7 +1473,7 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, else { /* Clients before 1.5 don't send the rev-props field. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "clb?l", &log_msg, + SVN_ERR(svn_ra_svn__parse_tuple(params, "clb?l", &log_msg, &lock_tokens, &keep_locks, &revprop_list)); } @@ -1513,8 +1551,11 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +get_file(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; const char *path, *full_path, *hex_digest; @@ -1537,7 +1578,7 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, ab.conn = conn; /* Parse arguments. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?B", &path, &rev, + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)bb?B", &path, &rev, &want_props, &want_contents, &wants_inherited_props)); @@ -1637,35 +1678,26 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +/* Translate all the words in DIRENT_FIELDS_LIST into the flags in + * DIRENT_FIELDS_P. If DIRENT_FIELDS_LIST is NULL, set all flags. */ +static svn_error_t * +parse_dirent_fields(apr_uint32_t *dirent_fields_p, + svn_ra_svn__list_t *dirent_fields_list) { - server_baton_t *b = baton; - const char *path, *full_path; - svn_revnum_t rev; - apr_hash_t *entries, *props = NULL; - apr_array_header_t *inherited_props; - apr_hash_index_t *hi; - svn_fs_root_t *root; - apr_pool_t *subpool; - svn_boolean_t want_props, want_contents; - apr_uint64_t wants_inherited_props; - apr_uint64_t dirent_fields; - apr_array_header_t *dirent_fields_list = NULL; - svn_ra_svn_item_t *elt; - int i; - authz_baton_t ab; - - ab.server = b; - ab.conn = conn; - - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?l?B", &path, &rev, - &want_props, &want_contents, - &dirent_fields_list, - &wants_inherited_props)); - - if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) - wants_inherited_props = FALSE; + static const svn_string_t str_kind + = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_KIND); + static const svn_string_t str_size + = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_SIZE); + static const svn_string_t str_has_props + = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_HAS_PROPS); + static const svn_string_t str_created_rev + = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_CREATED_REV); + static const svn_string_t str_time + = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_TIME); + static const svn_string_t str_last_author + = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_LAST_AUTHOR); + + apr_uint32_t dirent_fields; if (! dirent_fields_list) { @@ -1673,31 +1705,70 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } else { + int i; dirent_fields = 0; for (i = 0; i < dirent_fields_list->nelts; ++i) { - elt = &APR_ARRAY_IDX(dirent_fields_list, i, svn_ra_svn_item_t); + svn_ra_svn__item_t *elt + = &SVN_RA_SVN__LIST_ITEM(dirent_fields_list, i); if (elt->kind != SVN_RA_SVN_WORD) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, - "Dirent field not a string"); + "Dirent field not a word"); - if (strcmp(SVN_RA_SVN_DIRENT_KIND, elt->u.word) == 0) + if (svn_string_compare(&str_kind, &elt->u.word)) dirent_fields |= SVN_DIRENT_KIND; - else if (strcmp(SVN_RA_SVN_DIRENT_SIZE, elt->u.word) == 0) + else if (svn_string_compare(&str_size, &elt->u.word)) dirent_fields |= SVN_DIRENT_SIZE; - else if (strcmp(SVN_RA_SVN_DIRENT_HAS_PROPS, elt->u.word) == 0) + else if (svn_string_compare(&str_has_props, &elt->u.word)) dirent_fields |= SVN_DIRENT_HAS_PROPS; - else if (strcmp(SVN_RA_SVN_DIRENT_CREATED_REV, elt->u.word) == 0) + else if (svn_string_compare(&str_created_rev, &elt->u.word)) dirent_fields |= SVN_DIRENT_CREATED_REV; - else if (strcmp(SVN_RA_SVN_DIRENT_TIME, elt->u.word) == 0) + else if (svn_string_compare(&str_time, &elt->u.word)) dirent_fields |= SVN_DIRENT_TIME; - else if (strcmp(SVN_RA_SVN_DIRENT_LAST_AUTHOR, elt->u.word) == 0) + else if (svn_string_compare(&str_last_author, &elt->u.word)) dirent_fields |= SVN_DIRENT_LAST_AUTHOR; } } + *dirent_fields_p = dirent_fields; + return SVN_NO_ERROR; +} + +static svn_error_t * +get_dir(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) +{ + server_baton_t *b = baton; + const char *path, *full_path; + svn_revnum_t rev; + apr_hash_t *entries, *props = NULL; + apr_array_header_t *inherited_props; + apr_hash_index_t *hi; + svn_fs_root_t *root; + apr_pool_t *subpool; + svn_boolean_t want_props, want_contents; + apr_uint64_t wants_inherited_props; + apr_uint32_t dirent_fields; + svn_ra_svn__list_t *dirent_fields_list = NULL; + int i; + authz_baton_t ab; + + ab.server = b; + ab.conn = conn; + + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)bb?l?B", &path, &rev, + &want_props, &want_contents, + &dirent_fields_list, + &wants_inherited_props)); + + if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) + wants_inherited_props = FALSE; + + SVN_ERR(parse_dirent_fields(&dirent_fields, dirent_fields_list)); full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -1773,7 +1844,7 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, entry_kind = fsent->kind; if (dirent_fields & SVN_DIRENT_SIZE) - if (entry_kind != svn_node_dir) + if (fsent->kind != svn_node_dir) SVN_CMD_ERR(svn_fs_file_length(&entry_size, root, file_path, subpool)); @@ -1838,8 +1909,11 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return svn_ra_svn__write_tuple(conn, pool, "!))"); } -static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +update(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -1853,7 +1927,7 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_boolean_t is_checkout; /* Parse the arguments. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?w3?3", &rev, &target, + SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cb?w3?3", &rev, &target, &recurse, &depth_word, &send_copyfrom_args, &ignore_ancestry)); target = svn_relpath_canonicalize(target, pool); @@ -1893,8 +1967,11 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +switch_cmd(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -1908,7 +1985,7 @@ static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_tristate_t ignore_ancestry; /* Optional; default TRUE */ /* Parse the arguments. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?33", &rev, &target, + SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cbc?w?33", &rev, &target, &recurse, &switch_url, &depth_word, &send_copyfrom_args, &ignore_ancestry)); target = svn_relpath_canonicalize(target, pool); @@ -1943,8 +2020,11 @@ static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, (ignore_ancestry != svn_tristate_false)); } -static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +status(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -1955,7 +2035,7 @@ static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_depth_t depth = svn_depth_unknown; /* Parse the arguments. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cb?(?r)?w", + SVN_ERR(svn_ra_svn__parse_tuple(params, "cb?(?r)?w", &target, &recurse, &rev, &depth_word)); target = svn_relpath_canonicalize(target, pool); @@ -1979,8 +2059,11 @@ static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, depth, FALSE, FALSE); } -static svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +diff(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -1995,14 +2078,14 @@ static svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool, if (params->nelts == 5) { /* Clients before 1.4 don't send the text_deltas boolean or depth. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbc", &rev, &target, + SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cbbc", &rev, &target, &recurse, &ignore_ancestry, &versus_url)); text_deltas = TRUE; depth_word = NULL; } else { - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbcb?w", + SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cbbcb?w", &rev, &target, &recurse, &ignore_ancestry, &versus_url, &text_deltas, &depth_word)); @@ -2039,31 +2122,92 @@ static svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } +/* Baton type to be used with mergeinfo_receiver. */ +typedef struct mergeinfo_receiver_baton_t +{ + /* Send the response over this connection. */ + svn_ra_svn_conn_t *conn; + + /* Start path of the query; report paths relative to this one. */ + const char *fs_path; + + /* Did we already send the opening sequence? */ + svn_boolean_t starting_tuple_sent; +} mergeinfo_receiver_baton_t; + +/* Utility method sending the start of the "get m/i" response once + over BATON->CONN. */ +static svn_error_t * +send_mergeinfo_starting_tuple(mergeinfo_receiver_baton_t *baton, + apr_pool_t *scratch_pool) +{ + if (baton->starting_tuple_sent) + return SVN_NO_ERROR; + + SVN_ERR(svn_ra_svn__write_tuple(baton->conn, scratch_pool, + "w((!", "success")); + baton->starting_tuple_sent = TRUE; + + return SVN_NO_ERROR; +} + +/* Implements svn_repos_mergeinfo_receiver_t, sending the MERGEINFO + * out over the connection in the mergeinfo_receiver_baton_t * BATON. */ +static svn_error_t * +mergeinfo_receiver(const char *path, + svn_mergeinfo_t mergeinfo, + void *baton, + apr_pool_t *scratch_pool) +{ + mergeinfo_receiver_baton_t *b = baton; + svn_string_t *mergeinfo_string; + + /* Delay starting the response until we checked that the initial + request went through. We are at that point now b/c we've got + the first results in. */ + SVN_ERR(send_mergeinfo_starting_tuple(b, scratch_pool)); + + /* Adjust the path info and send the m/i. */ + path = svn_fspath__skip_ancestor(b->fs_path, path); + SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, mergeinfo, + scratch_pool)); + SVN_ERR(svn_ra_svn__write_tuple(b->conn, scratch_pool, "cs", path, + mergeinfo_string)); + + return SVN_NO_ERROR; +} + /* Regardless of whether a client's capabilities indicate an understanding of this command (by way of SVN_RA_SVN_CAP_MERGEINFO), we provide a response. ASSUMPTION: When performing a 'merge' with two URLs at different revisions, the client will call this command more than once. */ -static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +get_mergeinfo(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; - apr_array_header_t *paths, *canonical_paths; - svn_mergeinfo_catalog_t mergeinfo; + svn_ra_svn__list_t *paths; + apr_array_header_t *canonical_paths; int i; - apr_hash_index_t *hi; const char *inherit_word; svn_mergeinfo_inheritance_t inherit; svn_boolean_t include_descendants; - apr_pool_t *iterpool; authz_baton_t ab; + mergeinfo_receiver_baton_t mergeinfo_baton; ab.server = b; ab.conn = conn; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)wb", &paths, &rev, + mergeinfo_baton.conn = conn; + mergeinfo_baton.fs_path = b->repository->fs_path->data; + mergeinfo_baton.starting_tuple_sent = FALSE; + + SVN_ERR(svn_ra_svn__parse_tuple(params, "l(?r)wb", &paths, &rev, &inherit_word, &include_descendants)); inherit = svn_inheritance_from_word(inherit_word); @@ -2071,13 +2215,13 @@ static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, canonical_paths = apr_array_make(pool, paths->nelts, sizeof(const char *)); for (i = 0; i < paths->nelts; i++) { - svn_ra_svn_item_t *item = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t); + svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(paths, i); const char *full_path; if (item->kind != SVN_RA_SVN_STRING) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Path is not a string")); - full_path = svn_relpath_canonicalize(item->u.string->data, pool); + full_path = svn_relpath_canonicalize(item->u.string.data, pool); full_path = svn_fspath__join(b->repository->fs_path->data, full_path, pool); APR_ARRAY_PUSH(canonical_paths, const char *) = full_path; } @@ -2088,42 +2232,76 @@ static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, pool))); SVN_ERR(trivial_auth_request(conn, pool, b)); - SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repository->repos, - canonical_paths, rev, - inherit, - include_descendants, - authz_check_access_cb_func(b), &ab, - pool)); - SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(&mergeinfo, mergeinfo, - b->repository->fs_path->data, pool)); - SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); - iterpool = svn_pool_create(pool); - for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) - { - const char *key = apr_hash_this_key(hi); - svn_mergeinfo_t value = apr_hash_this_val(hi); - svn_string_t *mergeinfo_string; - svn_pool_clear(iterpool); + SVN_CMD_ERR(svn_repos_fs_get_mergeinfo2(b->repository->repos, + canonical_paths, rev, + inherit, + include_descendants, + authz_check_access_cb_func(b), &ab, + mergeinfo_receiver, + &mergeinfo_baton, + pool)); + + /* We might not have sent anything + => ensure to begin the response in any case. */ + SVN_ERR(send_mergeinfo_starting_tuple(&mergeinfo_baton, pool)); + SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); + + return SVN_NO_ERROR; +} - SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, value, iterpool)); - SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", key, - mergeinfo_string)); +/* Send a changed paths list entry to the client. + This implements svn_repos_path_change_receiver_t. */ +static svn_error_t * +path_change_receiver(void *baton, + svn_repos_path_change_t *change, + apr_pool_t *scratch_pool) +{ + const char symbol[] = "MADR"; + + log_baton_t *b = baton; + svn_ra_svn_conn_t *conn = b->conn; + + /* Sanitize and convert change kind to ra-svn level action. + + Pushing that conversion down into libsvn_ra_svn would add yet another + API dependency there. */ + char action = ( change->change_kind < svn_fs_path_change_modify + || change->change_kind > svn_fs_path_change_replace) + ? 0 + : symbol[change->change_kind]; + + /* Open lists once: LOG_ENTRY and LOG_ENTRY->CHANGED_PATHS. */ + if (!b->started) + { + SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool)); + SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool)); + b->started = TRUE; } - svn_pool_destroy(iterpool); - SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); + + /* Serialize CHANGE. */ + SVN_ERR(svn_ra_svn__write_data_log_changed_path( + conn, scratch_pool, + &change->path, + action, + change->copyfrom_path, + change->copyfrom_rev, + change->node_kind, + change->text_mod, + change->prop_mod)); return SVN_NO_ERROR; } -/* Send a log entry to the client. */ -static svn_error_t *log_receiver(void *baton, - svn_log_entry_t *log_entry, - apr_pool_t *pool) +/* Send a the meta data and the revpros for LOG_ENTRY to the client. + This implements svn_log_entry_receiver_t. */ +static svn_error_t * +revision_receiver(void *baton, + svn_repos_log_entry_t *log_entry, + apr_pool_t *scratch_pool) { log_baton_t *b = baton; svn_ra_svn_conn_t *conn = b->conn; - apr_hash_index_t *h; svn_boolean_t invalid_revnum = FALSE; const svn_string_t *author, *date, *message; unsigned revprop_count; @@ -2144,55 +2322,52 @@ static svn_error_t *log_receiver(void *baton, svn_compat_log_revprops_out_string(&author, &date, &message, log_entry->revprops); - svn_compat_log_revprops_clear(log_entry->revprops); - if (log_entry->revprops) - revprop_count = apr_hash_count(log_entry->revprops); - else - revprop_count = 0; - /* send LOG_ENTRY */ - SVN_ERR(svn_ra_svn__start_list(conn, pool)); + /* Revprops list filtering is somewhat expensive. + Avoid doing that for the 90% case where only the standard revprops + have been requested and delivered. */ + if (author && date && message && apr_hash_count(log_entry->revprops) == 3) + { + revprop_count = 0; + } + else + { + svn_compat_log_revprops_clear(log_entry->revprops); + if (log_entry->revprops) + revprop_count = apr_hash_count(log_entry->revprops); + else + revprop_count = 0; + } - /* send LOG_ENTRY->CHANGED_PATHS2 */ - SVN_ERR(svn_ra_svn__start_list(conn, pool)); - if (log_entry->changed_paths2) + /* Open lists once: LOG_ENTRY and LOG_ENTRY->CHANGED_PATHS. */ + if (!b->started) { - for (h = apr_hash_first(pool, log_entry->changed_paths2); h; - h = apr_hash_next(h)) - { - const char *path = apr_hash_this_key(h); - svn_log_changed_path2_t *change = apr_hash_this_val(h); - - SVN_ERR(svn_ra_svn__write_data_log_changed_path( - conn, pool, - path, - change->action, - change->copyfrom_path, - change->copyfrom_rev, - change->node_kind, - /* text_modified and props_modified are never unknown */ - change->text_modified == svn_tristate_true, - change->props_modified == svn_tristate_true)); - } + SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool)); + SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool)); } - SVN_ERR(svn_ra_svn__end_list(conn, pool)); + + /* Close LOG_ENTRY->CHANGED_PATHS. */ + SVN_ERR(svn_ra_svn__end_list(conn, scratch_pool)); + b->started = FALSE; /* send LOG_ENTRY main members */ - SVN_ERR(svn_ra_svn__write_data_log_entry(conn, pool, + SVN_ERR(svn_ra_svn__write_data_log_entry(conn, scratch_pool, log_entry->revision, author, date, message, log_entry->has_children, invalid_revnum, revprop_count)); /* send LOG_ENTRY->REVPROPS */ - SVN_ERR(svn_ra_svn__start_list(conn, pool)); + SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool)); if (revprop_count) - SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops)); - SVN_ERR(svn_ra_svn__end_list(conn, pool)); + SVN_ERR(svn_ra_svn__write_proplist(conn, scratch_pool, + log_entry->revprops)); + SVN_ERR(svn_ra_svn__end_list(conn, scratch_pool)); /* send LOG_ENTRY members that were added in later SVN releases */ - SVN_ERR(svn_ra_svn__write_boolean(conn, pool, log_entry->subtractive_merge)); - SVN_ERR(svn_ra_svn__end_list(conn, pool)); + SVN_ERR(svn_ra_svn__write_boolean(conn, scratch_pool, + log_entry->subtractive_merge)); + SVN_ERR(svn_ra_svn__end_list(conn, scratch_pool)); if (log_entry->has_children) b->stack_depth++; @@ -2200,17 +2375,21 @@ static svn_error_t *log_receiver(void *baton, return SVN_NO_ERROR; } -static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +log_cmd(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { svn_error_t *err, *write_err; server_baton_t *b = baton; svn_revnum_t start_rev, end_rev; const char *full_path; svn_boolean_t send_changed_paths, strict_node, include_merged_revisions; - apr_array_header_t *paths, *full_paths, *revprop_items, *revprops; + apr_array_header_t *full_paths, *revprops; + svn_ra_svn__list_t *paths, *revprop_items; char *revprop_word; - svn_ra_svn_item_t *elt; + svn_ra_svn__item_t *elt; int i; apr_uint64_t limit, include_merged_revs_param; log_baton_t lb; @@ -2219,7 +2398,7 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, ab.server = b; ab.conn = conn; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl", &paths, + SVN_ERR(svn_ra_svn__parse_tuple(params, "l(?r)(?r)bb?n?Bwl", &paths, &start_rev, &end_rev, &send_changed_paths, &strict_node, &limit, &include_merged_revs_param, @@ -2243,11 +2422,11 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, sizeof(char *)); for (i = 0; i < revprop_items->nelts; i++) { - elt = &APR_ARRAY_IDX(revprop_items, i, svn_ra_svn_item_t); + elt = &SVN_RA_SVN__LIST_ITEM(revprop_items, i); if (elt->kind != SVN_RA_SVN_STRING) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Log revprop entry not a string")); - APR_ARRAY_PUSH(revprops, const char *) = elt->u.string->data; + APR_ARRAY_PUSH(revprops, const char *) = elt->u.string.data; } } else @@ -2265,11 +2444,11 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, full_paths = apr_array_make(pool, paths->nelts, sizeof(const char *)); for (i = 0; i < paths->nelts; i++) { - elt = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t); + elt = &SVN_RA_SVN__LIST_ITEM(paths, i); if (elt->kind != SVN_RA_SVN_STRING) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Log path entry not a string")); - full_path = svn_relpath_canonicalize(elt->u.string->data, pool), + full_path = svn_relpath_canonicalize(elt->u.string.data, pool), full_path = svn_fspath__join(b->repository->fs_path->data, full_path, pool); APR_ARRAY_PUSH(full_paths, const char *) = full_path; @@ -2286,11 +2465,14 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, lb.fs_path = b->repository->fs_path->data; lb.conn = conn; lb.stack_depth = 0; - err = svn_repos_get_logs4(b->repository->repos, full_paths, start_rev, - end_rev, (int) limit, send_changed_paths, + lb.started = FALSE; + err = svn_repos_get_logs5(b->repository->repos, full_paths, start_rev, + end_rev, (int) limit, strict_node, include_merged_revisions, revprops, authz_check_access_cb_func(b), &ab, - log_receiver, &lb, pool); + send_changed_paths ? path_change_receiver : NULL, + send_changed_paths ? &lb : NULL, + revision_receiver, &lb, pool); write_err = svn_ra_svn__write_word(conn, pool, "done"); if (write_err) @@ -2303,8 +2485,11 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +check_path(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -2312,7 +2497,7 @@ static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_fs_root_t *root; svn_node_kind_t kind; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev)); full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -2333,8 +2518,11 @@ static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +stat_cmd(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_revnum_t rev; @@ -2342,7 +2530,7 @@ static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_fs_root_t *root; svn_dirent_t *dirent; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev)); full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -2380,14 +2568,18 @@ static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +get_locations(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { svn_error_t *err, *write_err; server_baton_t *b = baton; svn_revnum_t revision; - apr_array_header_t *location_revisions, *loc_revs_proto; - svn_ra_svn_item_t *elt; + apr_array_header_t *location_revisions; + svn_ra_svn__list_t *loc_revs_proto; + svn_ra_svn__item_t *elt; int i; const char *relative_path; svn_revnum_t peg_revision; @@ -2399,7 +2591,7 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, ab.conn = conn; /* Parse the arguments. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crl", &relative_path, + SVN_ERR(svn_ra_svn__parse_tuple(params, "crl", &relative_path, &peg_revision, &loc_revs_proto)); relative_path = svn_relpath_canonicalize(relative_path, pool); @@ -2411,7 +2603,7 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, sizeof(svn_revnum_t)); for (i = 0; i < loc_revs_proto->nelts; i++) { - elt = &APR_ARRAY_IDX(loc_revs_proto, i, svn_ra_svn_item_t); + elt = &SVN_RA_SVN__LIST_ITEM(loc_revs_proto, i); if (elt->kind != SVN_RA_SVN_NUMBER) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, "Get-locations location revisions entry " @@ -2479,10 +2671,11 @@ static svn_error_t *gls_receiver(svn_location_segment_t *segment, segment->path); } -static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - apr_array_header_t *params, - void *baton) +static svn_error_t * +get_location_segments(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { svn_error_t *err, *write_err; server_baton_t *b = baton; @@ -2495,7 +2688,7 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, ab.conn = conn; /* Parse the arguments. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)(?r)", + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)(?r)(?r)", &relative_path, &peg_revision, &start_rev, &end_rev)); relative_path = svn_relpath_canonicalize(relative_path, pool); @@ -2538,7 +2731,7 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, if (end_rev > start_rev) { err = svn_ra_svn__write_word(conn, pool, "done"); - err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, err, + err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, err, "Get-location-segments end revision must not be " "younger than start revision"); return log_fail_and_flush(err, b, conn, pool); @@ -2547,7 +2740,7 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, if (start_rev > peg_revision) { err = svn_ra_svn__write_word(conn, pool, "done"); - err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, err, + err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, err, "Get-location-segments start revision must not " "be younger than peg revision"); return log_fail_and_flush(err, b, conn, pool); @@ -2628,15 +2821,9 @@ static svn_error_t *file_rev_handler(void *baton, const char *path, svn_stream_set_write(stream, svndiff_handler); svn_stream_set_close(stream, svndiff_close_handler); - /* If the connection does not support SVNDIFF1 or if we don't want to use - * compression, use the non-compressing "version 0" implementation */ - if ( svn_ra_svn_compression_level(frb->conn) > 0 - && svn_ra_svn_has_capability(frb->conn, SVN_RA_SVN_CAP_SVNDIFF1)) - svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 1, - svn_ra_svn_compression_level(frb->conn), pool); - else - svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 0, - svn_ra_svn_compression_level(frb->conn), pool); + svn_txdelta_to_svndiff3(d_handler, d_baton, stream, + svn_ra_svn__svndiff_version(frb->conn), + svn_ra_svn_compression_level(frb->conn), pool); } else SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, "")); @@ -2644,8 +2831,11 @@ static svn_error_t *file_rev_handler(void *baton, const char *path, return SVN_NO_ERROR; } -static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +get_file_revs(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_error_t *err, *write_err; @@ -2661,7 +2851,7 @@ static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, ab.conn = conn; /* Parse arguments. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)?B", + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)(?r)?B", &path, &start_rev, &end_rev, &include_merged_revs_param)); path = svn_relpath_canonicalize(path, pool); @@ -2697,8 +2887,11 @@ static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +lock(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; const char *path; @@ -2708,7 +2901,7 @@ static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t current_rev; svn_lock_t *l; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment, + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?c)b(?r)", &path, &comment, &steal_lock, ¤t_rev)); full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -2771,11 +2964,14 @@ clear_lock_result_hash(apr_hash_t *results, } } -static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +lock_many(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; - apr_array_header_t *path_revs; + svn_ra_svn__list_t *path_revs; const char *comment; svn_boolean_t steal_lock; int i; @@ -2786,7 +2982,7 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_hash_index_t *hi; struct lock_many_baton_t lmb; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock, + SVN_ERR(svn_ra_svn__parse_tuple(params, "(?c)bl", &comment, &steal_lock, &path_revs)); subpool = svn_pool_create(pool); @@ -2802,8 +2998,7 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { const char *path, *full_path; svn_revnum_t current_rev; - svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i, - svn_ra_svn_item_t); + svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(path_revs, i); svn_fs_lock_target_t *target; svn_pool_clear(subpool); @@ -2812,7 +3007,7 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, "Lock requests should be list of lists"); - SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?r)", &path, + SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list, "c(?r)", &path, ¤t_rev)); full_path = svn_fspath__join(b->repository->fs_path->data, @@ -2866,14 +3061,13 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { const char *path, *full_path; svn_revnum_t current_rev; - svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i, - svn_ra_svn_item_t); + svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(path_revs, i); struct lock_result_t *result; svn_pool_clear(subpool); - write_err = svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?r)", &path, - ¤t_rev); + write_err = svn_ra_svn__parse_tuple(&item->u.list, "c(?r)", + &path, ¤t_rev); if (write_err) break; @@ -2927,14 +3121,17 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +unlock(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; const char *path, *token, *full_path; svn_boolean_t break_lock; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?c)b", &path, &token, &break_lock)); full_path = svn_fspath__join(b->repository->fs_path->data, @@ -2954,12 +3151,15 @@ static svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +unlock_many(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; svn_boolean_t break_lock; - apr_array_header_t *unlock_tokens; + svn_ra_svn__list_t *unlock_tokens; int i; apr_pool_t *subpool; svn_error_t *err = SVN_NO_ERROR, *write_err = SVN_NO_ERROR; @@ -2968,8 +3168,7 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_hash_index_t *hi; struct lock_many_baton_t lmb; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "bl", &break_lock, - &unlock_tokens)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "bl", &break_lock, &unlock_tokens)); /* Username required unless break_lock was specified. */ SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock)); @@ -2979,8 +3178,7 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, /* Parse the unlock requests from PATH_REVS into TARGETS. */ for (i = 0; i < unlock_tokens->nelts; i++) { - svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i, - svn_ra_svn_item_t); + svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(unlock_tokens, i); const char *path, *full_path, *token; svn_pool_clear(subpool); @@ -2989,7 +3187,7 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, "Unlock request should be a list of lists"); - SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path, + SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list, "c(?c)", &path, &token)); if (!token) token = ""; @@ -3042,14 +3240,13 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, for (i = 0; i < unlock_tokens->nelts; ++i) { const char *path, *token, *full_path; - svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i, - svn_ra_svn_item_t); + svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(unlock_tokens, i); struct lock_result_t *result; svn_pool_clear(subpool); - write_err = svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path, - &token); + write_err = svn_ra_svn__parse_tuple(&item->u.list, "c(?c)", + &path, &token); if (write_err) break; @@ -3096,15 +3293,18 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +get_lock(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; const char *path; const char *full_path; svn_lock_t *l; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &path)); full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -3124,8 +3324,11 @@ static svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +get_locks(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { server_baton_t *b = baton; const char *path; @@ -3140,7 +3343,7 @@ static svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool, ab.server = b; ab.conn = conn; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c?(?w)", &path, &depth_word)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "c?(?w)", &path, &depth_word)); depth = depth_word ? svn_depth_from_word(depth_word) : svn_depth_infinity; if ((depth != svn_depth_empty) && @@ -3213,14 +3416,17 @@ static svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn, return svn_ra_svn__write_cmd_finish_replay(conn, pool); } -static svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +replay(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { svn_revnum_t rev, low_water_mark; svn_boolean_t send_deltas; server_baton_t *b = baton; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrb", &rev, &low_water_mark, + SVN_ERR(svn_ra_svn__parse_tuple(params, "rrb", &rev, &low_water_mark, &send_deltas)); SVN_ERR(trivial_auth_request(conn, pool, b)); @@ -3233,8 +3439,11 @@ static svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, void *baton) +static svn_error_t * +replay_range(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) { svn_revnum_t start_rev, end_rev, rev, low_water_mark; svn_boolean_t send_deltas; @@ -3245,7 +3454,7 @@ static svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, ab.server = b; ab.conn = conn; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrrb", &start_rev, + SVN_ERR(svn_ra_svn__parse_tuple(params, "rrrb", &start_rev, &end_rev, &low_water_mark, &send_deltas)); @@ -3281,7 +3490,7 @@ static svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, static svn_error_t * get_deleted_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, + svn_ra_svn__list_t *params, void *baton) { server_baton_t *b = baton; @@ -3290,7 +3499,7 @@ get_deleted_rev(svn_ra_svn_conn_t *conn, svn_revnum_t end_revision; svn_revnum_t revision_deleted; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crr", + SVN_ERR(svn_ra_svn__parse_tuple(params, "crr", &path, &peg_revision, &end_revision)); full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, pool), pool); @@ -3305,7 +3514,7 @@ get_deleted_rev(svn_ra_svn_conn_t *conn, static svn_error_t * get_inherited_props(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - apr_array_header_t *params, + svn_ra_svn__list_t *params, void *baton) { server_baton_t *b = baton; @@ -3316,12 +3525,13 @@ get_inherited_props(svn_ra_svn_conn_t *conn, int i; apr_pool_t *iterpool = svn_pool_create(pool); authz_baton_t ab; + svn_node_kind_t node_kind; ab.server = b; ab.conn = conn; /* Parse arguments. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, iterpool, "c(?r)", &path, &rev)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev)); full_path = svn_fspath__join(b->repository->fs_path->data, svn_relpath_canonicalize(path, iterpool), @@ -3331,15 +3541,18 @@ get_inherited_props(svn_ra_svn_conn_t *conn, SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read, full_path, FALSE)); - if (!SVN_IS_VALID_REVNUM(rev)) - SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); - SVN_ERR(log_command(b, conn, pool, "%s", svn_log__get_inherited_props(full_path, rev, iterpool))); /* Fetch the properties and a stream for the contents. */ SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, iterpool)); + SVN_CMD_ERR(svn_fs_check_path(&node_kind, root, full_path, pool)); + if (node_kind == svn_node_none) + { + SVN_CMD_ERR(svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, + _("'%s' path not found"), full_path)); + } SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool)); /* Send successful command response with revision and props. */ @@ -3365,7 +3578,115 @@ get_inherited_props(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static const svn_ra_svn_cmd_entry_t main_commands[] = { +/* Baton type to be used with list_receiver. */ +typedef struct list_receiver_baton_t +{ + /* Send the data through this connection. */ + svn_ra_svn_conn_t *conn; + + /* Send the field selected by these flags. */ + apr_uint32_t dirent_fields; +} list_receiver_baton_t; + +/* Implements svn_repos_dirent_receiver_t, sending DIRENT and PATH to the + * client. BATON must be a list_receiver_baton_t. */ +static svn_error_t * +list_receiver(const char *path, + svn_dirent_t *dirent, + void *baton, + apr_pool_t *pool) +{ + list_receiver_baton_t *b = baton; + return svn_error_trace(svn_ra_svn__write_dirent(b->conn, pool, path, dirent, + b->dirent_fields)); +} + +static svn_error_t * +list(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_ra_svn__list_t *params, + void *baton) +{ + server_baton_t *b = baton; + const char *path, *full_path; + svn_revnum_t rev; + svn_depth_t depth; + apr_array_header_t *patterns = NULL; + svn_fs_root_t *root; + const char *depth_word; + svn_boolean_t path_info_only; + svn_ra_svn__list_t *dirent_fields_list = NULL; + svn_ra_svn__list_t *patterns_list = NULL; + int i; + list_receiver_baton_t rb; + svn_error_t *err, *write_err; + + authz_baton_t ab; + ab.server = b; + ab.conn = conn; + + /* Read the command parameters. */ + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)w?l?l", &path, &rev, + &depth_word, &dirent_fields_list, + &patterns_list)); + + rb.conn = conn; + SVN_ERR(parse_dirent_fields(&rb.dirent_fields, dirent_fields_list)); + + depth = svn_depth_from_word(depth_word); + full_path = svn_fspath__join(b->repository->fs_path->data, + svn_relpath_canonicalize(path, pool), pool); + + /* Read the patterns list. */ + if (patterns_list) + { + patterns = apr_array_make(pool, 0, sizeof(const char *)); + for (i = 0; i < patterns_list->nelts; ++i) + { + svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(patterns_list, i); + + if (elt->kind != SVN_RA_SVN_STRING) + return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, + "Pattern field not a string"); + + APR_ARRAY_PUSH(patterns, const char *) = elt->u.string.data; + } + } + + /* Check authorizations */ + SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, + full_path, FALSE)); + + if (!SVN_IS_VALID_REVNUM(rev)) + SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); + + SVN_ERR(log_command(b, conn, pool, "%s", + svn_log__list(full_path, rev, patterns, depth, + rb.dirent_fields, pool))); + + /* Fetch the root of the appropriate revision. */ + SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); + + /* Fetch the directory entries if requested and send them immediately. */ + path_info_only = (rb.dirent_fields & ~SVN_DIRENT_KIND) == 0; + err = svn_repos_list(root, full_path, patterns, depth, path_info_only, + authz_check_access_cb_func(b), &ab, list_receiver, + &rb, NULL, NULL, pool); + + + /* Finish response. */ + write_err = svn_ra_svn__write_word(conn, pool, "done"); + if (write_err) + { + svn_error_clear(err); + return write_err; + } + SVN_CMD_ERR(err); + + return svn_error_trace(svn_ra_svn__write_cmd_response(conn, pool, "")); +} + +static const svn_ra_svn__cmd_entry_t main_commands[] = { { "reparent", reparent }, { "get-latest-rev", get_latest_rev }, { "get-dated-rev", get_dated_rev }, @@ -3397,6 +3718,7 @@ static const svn_ra_svn_cmd_entry_t main_commands[] = { { "replay-range", replay_range }, { "get-deleted-rev", get_deleted_rev }, { "get-iprops", get_inherited_props }, + { "list", list }, { NULL } }; @@ -3457,8 +3779,7 @@ repos_path_valid(const char *path) * and fs_path fields of REPOSITORY. VHOST and READ_ONLY flags are the * same as in the server baton. * - * CONFIG_POOL and AUTHZ_POOL shall be used to load any object of the - * respective type. + * CONFIG_POOL shall be used to load config objects. * * Use SCRATCH_POOL for temporary allocations. * @@ -3471,13 +3792,13 @@ find_repos(const char *url, svn_config_t *cfg, repository_t *repository, svn_repos__config_pool_t *config_pool, - svn_repos__authz_pool_t *authz_pool, apr_hash_t *fs_config, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *path, *full_path, *fs_path, *hooks_env; svn_stringbuf_t *url_buf; + svn_boolean_t sasl_requested; /* Skip past the scheme and authority part. */ path = skip_scheme_part(url); @@ -3540,25 +3861,27 @@ find_repos(const char *url, { repository->base = svn_repos_conf_dir(repository->repos, result_pool); - SVN_ERR(svn_repos__config_pool_get(&cfg, NULL, config_pool, + SVN_ERR(svn_repos__config_pool_get(&cfg, config_pool, svn_repos_svnserve_conf (repository->repos, result_pool), - FALSE, FALSE, repository->repos, + FALSE, repository->repos, result_pool)); } SVN_ERR(load_pwdb_config(repository, cfg, config_pool, result_pool)); SVN_ERR(load_authz_config(repository, repository->repos_root, cfg, - authz_pool, result_pool)); + result_pool)); -#ifdef SVN_HAVE_SASL + /* Should we use Cyrus SASL? */ + SVN_ERR(svn_config_get_bool(cfg, &sasl_requested, + SVN_CONFIG_SECTION_SASL, + SVN_CONFIG_OPTION_USE_SASL, FALSE)); + if (sasl_requested) { +#ifdef SVN_HAVE_SASL const char *val; - /* Should we use Cyrus SASL? */ - SVN_ERR(svn_config_get_bool(cfg, &repository->use_sasl, - SVN_CONFIG_SECTION_SASL, - SVN_CONFIG_OPTION_USE_SASL, FALSE)); + repository->use_sasl = sasl_requested; svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL, SVN_CONFIG_OPTION_MIN_SSF, "0"); @@ -3567,8 +3890,18 @@ find_repos(const char *url, svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL, SVN_CONFIG_OPTION_MAX_SSF, "256"); SVN_ERR(svn_cstring_atoui(&repository->max_ssf, val)); +#else /* !SVN_HAVE_SASL */ + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("SASL requested but not compiled in; " + "set '%s' to 'false' or recompile " + "svnserve with SASL support"), + SVN_CONFIG_OPTION_USE_SASL); +#endif /* SVN_HAVE_SASL */ + } + else + { + repository->use_sasl = FALSE; } -#endif /* Use the repository UUID as the default realm. */ SVN_ERR(svn_fs_get_uuid(repository->fs, &repository->realm, scratch_pool)); @@ -3771,7 +4104,7 @@ construct_server_baton(server_baton_t **baton, svn_error_t *err, *io_err; apr_uint64_t ver; const char *client_url, *ra_client_string, *client_string; - apr_array_header_t *caplist; + svn_ra_svn__list_t *caplist; apr_pool_t *conn_pool = svn_ra_svn__get_pool(conn); server_baton_t *b = apr_pcalloc(conn_pool, sizeof(*b)); fs_warning_baton_t *warn_baton; @@ -3796,10 +4129,11 @@ construct_server_baton(server_baton_t **baton, * send an empty mechlist. */ if (params->compression_level > 0) SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool, - "nn()(wwwwwwwwwww)", + "nn()(wwwwwwwwwwwww)", (apr_uint64_t) 2, (apr_uint64_t) 2, SVN_RA_SVN_CAP_EDIT_PIPELINE, SVN_RA_SVN_CAP_SVNDIFF1, + SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED, SVN_RA_SVN_CAP_ABSENT_ENTRIES, SVN_RA_SVN_CAP_COMMIT_REVPROPS, SVN_RA_SVN_CAP_DEPTH, @@ -3808,11 +4142,12 @@ construct_server_baton(server_baton_t **baton, SVN_RA_SVN_CAP_PARTIAL_REPLAY, SVN_RA_SVN_CAP_INHERITED_PROPS, SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, - SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE + SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE, + SVN_RA_SVN_CAP_LIST )); else SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool, - "nn()(wwwwwwwwww)", + "nn()(wwwwwwwwwww)", (apr_uint64_t) 2, (apr_uint64_t) 2, SVN_RA_SVN_CAP_EDIT_PIPELINE, SVN_RA_SVN_CAP_ABSENT_ENTRIES, @@ -3823,7 +4158,8 @@ construct_server_baton(server_baton_t **baton, SVN_RA_SVN_CAP_PARTIAL_REPLAY, SVN_RA_SVN_CAP_INHERITED_PROPS, SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, - SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE + SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE, + SVN_RA_SVN_CAP_LIST )); /* Read client response, which we assume to be in version 2 format: @@ -3837,7 +4173,7 @@ construct_server_baton(server_baton_t **baton, return SVN_NO_ERROR; client_url = svn_uri_canonicalize(client_url, conn_pool); - SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist)); + SVN_ERR(svn_ra_svn__set_capabilities(conn, caplist)); /* All released versions of Subversion support edit-pipeline, * so we do not accept connections from clients that do not. */ @@ -3848,22 +4184,25 @@ construct_server_baton(server_baton_t **baton, they get handed to the start-commit hook). While we could add a new interface to re-retrieve them from conn and convert the result to a list, it's simpler to just convert caplist by hand - here, since we already have it and turning 'svn_ra_svn_item_t's + here, since we already have it and turning 'svn_ra_svn__item_t's into 'const char *'s is pretty easy. We only record capabilities we care about. The client may report more (because it doesn't know what the server cares about). */ { int i; - svn_ra_svn_item_t *item; + svn_ra_svn__item_t *item; b->repository->capabilities = apr_array_make(conn_pool, 1, sizeof(const char *)); for (i = 0; i < caplist->nelts; i++) { - item = &APR_ARRAY_IDX(caplist, i, svn_ra_svn_item_t); + static const svn_string_t str_cap_mergeinfo + = SVN__STATIC_STRING(SVN_RA_SVN_CAP_MERGEINFO); + + item = &SVN_RA_SVN__LIST_ITEM(caplist, i); /* ra_svn_set_capabilities() already type-checked for us */ - if (strcmp(item->u.word, SVN_RA_SVN_CAP_MERGEINFO) == 0) + if (svn_string_compare(&item->u.word, &str_cap_mergeinfo)) { APR_ARRAY_PUSH(b->repository->capabilities, const char *) = SVN_RA_CAPABILITY_MERGEINFO; @@ -3871,14 +4210,14 @@ construct_server_baton(server_baton_t **baton, /* Save for operational log. */ if (cap_log->len > 0) svn_stringbuf_appendcstr(cap_log, " "); - svn_stringbuf_appendcstr(cap_log, item->u.word); + svn_stringbuf_appendcstr(cap_log, item->u.word.data); } } err = handle_config_error(find_repos(client_url, params->root, b->vhost, b->read_only, params->cfg, b->repository, params->config_pool, - params->authz_pool, params->fs_config, + params->fs_config, conn_pool, scratch_pool), b); if (!err) @@ -3980,7 +4319,7 @@ serve_interruptable(svn_boolean_t *terminate_p, { svn_boolean_t terminate = FALSE; svn_error_t *err = NULL; - const svn_ra_svn_cmd_entry_t *command; + const svn_ra_svn__cmd_entry_t *command; apr_pool_t *iterpool = svn_pool_create(pool); /* Prepare command parser. */ @@ -4009,10 +4348,12 @@ serve_interruptable(svn_boolean_t *terminate_p, /* create the connection, configure ports etc. */ connection->conn - = svn_ra_svn_create_conn4(connection->usock, NULL, NULL, + = svn_ra_svn_create_conn5(connection->usock, NULL, NULL, connection->params->compression_level, connection->params->zero_copy_limit, connection->params->error_check_interval, + connection->params->max_request_size, + connection->params->max_response_size, connection->pool); /* Construct server baton and open the repository for the first time. */ |