diff options
author | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 |
---|---|---|
committer | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 |
commit | 3faf8d6bffc5d0fb2525ba37bb504c53366caf9d (patch) | |
tree | 7e47911263e75034b767fe34b2f8d3d17e91f66d /subversion/svnserve | |
parent | a55fb3c0d5eca7d887798125d5b95942b1f01d4b (diff) |
Diffstat (limited to 'subversion/svnserve')
-rw-r--r-- | subversion/svnserve/cyrus_auth.c | 56 | ||||
-rw-r--r-- | subversion/svnserve/serve.c | 979 | ||||
-rw-r--r-- | subversion/svnserve/server.h | 11 | ||||
-rw-r--r-- | subversion/svnserve/svnserve.c | 112 |
4 files changed, 786 insertions, 372 deletions
diff --git a/subversion/svnserve/cyrus_auth.c b/subversion/svnserve/cyrus_auth.c index 40f4228a999f0..f02a6f14073fc 100644 --- a/subversion/svnserve/cyrus_auth.c +++ b/subversion/svnserve/cyrus_auth.c @@ -112,11 +112,12 @@ static svn_error_t *initialize(void *baton, apr_pool_t *pool) /* The second parameter tells SASL to look for a configuration file named subversion.conf. */ - result = sasl_server_init(callbacks, SVN_RA_SVN_SASL_NAME); + result = svn_sasl__server_init(callbacks, SVN_RA_SVN_SASL_NAME); if (result != SASL_OK) { - svn_error_t *err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, - sasl_errstring(result, NULL, NULL)); + svn_error_t *err = svn_error_create( + SVN_ERR_RA_NOT_AUTHORIZED, NULL, + svn_sasl__errstring(result, NULL, NULL)); return svn_error_quick_wrap(err, _("Could not initialize the SASL library")); } @@ -135,7 +136,7 @@ svn_error_t *cyrus_init(apr_pool_t *pool) static svn_error_t * fail_auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, sasl_conn_t *sasl_ctx) { - const char *msg = sasl_errdetail(sasl_ctx); + const char *msg = svn_sasl__errdetail(sasl_ctx); SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", msg)); return svn_ra_svn__flush(conn, pool); } @@ -156,7 +157,7 @@ static svn_error_t * fail_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, sasl_conn_t *sasl_ctx) { svn_error_t *err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, - sasl_errdetail(sasl_ctx)); + svn_sasl__errdetail(sasl_ctx)); SVN_ERR(write_failure(conn, pool, &err)); return svn_ra_svn__flush(conn, pool); } @@ -192,16 +193,17 @@ static svn_error_t *try_auth(svn_ra_svn_conn_t *conn, return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Initial token is too long")); - result = sasl_server_start(sasl_ctx, mech, - in ? in->data : NULL, - in ? (unsigned int) in->len : 0, &out, &outlen); + result = svn_sasl__server_start(sasl_ctx, mech, + in ? in->data : NULL, + in ? (unsigned int) in->len : 0, + &out, &outlen); if (result != SASL_OK && result != SASL_CONTINUE) return fail_auth(conn, pool, sasl_ctx); while (result == SASL_CONTINUE) { - svn_ra_svn_item_t *item; + svn_ra_svn__item_t *item; arg = svn_string_ncreate(out, outlen, pool); /* Encode what we send to the client. */ @@ -215,7 +217,7 @@ static svn_error_t *try_auth(svn_ra_svn_conn_t *conn, if (item->kind != SVN_RA_SVN_STRING) return SVN_NO_ERROR; - in = item->u.string; + in = &item->u.string; if (use_base64) in = svn_base64_decode_string(in, pool); @@ -223,8 +225,9 @@ static svn_error_t *try_auth(svn_ra_svn_conn_t *conn, return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Step response is too long")); - result = sasl_server_step(sasl_ctx, in->data, (unsigned int) in->len, - &out, &outlen); + result = svn_sasl__server_step(sasl_ctx, in->data, + (unsigned int) in->len, + &out, &outlen); } if (result != SASL_OK) @@ -246,7 +249,7 @@ static svn_error_t *try_auth(svn_ra_svn_conn_t *conn, static apr_status_t sasl_dispose_cb(void *data) { sasl_conn_t *sasl_ctx = (sasl_conn_t*) data; - sasl_dispose(&sasl_ctx); + svn_sasl__dispose(&sasl_ctx); return APR_SUCCESS; } @@ -278,15 +281,16 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, /* Create a SASL context. SASL_SUCCESS_DATA tells SASL that the protocol supports sending data along with the final "success" message. */ - result = sasl_server_new(SVN_RA_SVN_SASL_NAME, - hostname, b->repository->realm, - localaddrport, remoteaddrport, - NULL, SASL_SUCCESS_DATA, - &sasl_ctx); + result = svn_sasl__server_new(SVN_RA_SVN_SASL_NAME, + hostname, b->repository->realm, + localaddrport, remoteaddrport, + NULL, SASL_SUCCESS_DATA, + &sasl_ctx); if (result != SASL_OK) { - svn_error_t *err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, - sasl_errstring(result, NULL, NULL)); + svn_error_t *err = svn_error_create( + SVN_ERR_RA_NOT_AUTHORIZED, NULL, + svn_sasl__errstring(result, NULL, NULL)); SVN_ERR(write_failure(conn, pool, &err)); return svn_ra_svn__flush(conn, pool); } @@ -307,20 +311,20 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, secprops.max_ssf = b->repository->max_ssf; /* Set security properties. */ - result = sasl_setprop(sasl_ctx, SASL_SEC_PROPS, &secprops); + result = svn_sasl__setprop(sasl_ctx, SASL_SEC_PROPS, &secprops); if (result != SASL_OK) return fail_cmd(conn, pool, sasl_ctx); /* SASL needs to know if we are externally authenticated. */ if (b->client_info->tunnel_user) - result = sasl_setprop(sasl_ctx, SASL_AUTH_EXTERNAL, - b->client_info->tunnel_user); + result = svn_sasl__setprop(sasl_ctx, SASL_AUTH_EXTERNAL, + b->client_info->tunnel_user); if (result != SASL_OK) return fail_cmd(conn, pool, sasl_ctx); /* Get the list of mechanisms. */ - result = sasl_listmech(sasl_ctx, NULL, NULL, " ", NULL, - &mechlist, NULL, &mech_count); + result = svn_sasl__listmech(sasl_ctx, NULL, NULL, " ", NULL, + &mechlist, NULL, &mech_count); if (result != SASL_OK) return fail_cmd(conn, pool, sasl_ctx); @@ -356,7 +360,7 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn, const void *user; /* Get the authenticated username. */ - result = sasl_getprop(sasl_ctx, SASL_USERNAME, &user); + result = svn_sasl__getprop(sasl_ctx, SASL_USERNAME, &user); if (result != SASL_OK) return fail_cmd(conn, pool, sasl_ctx); 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. */ diff --git a/subversion/svnserve/server.h b/subversion/svnserve/server.h index d366e0cd4c6ec..c26a76a1aae2e 100644 --- a/subversion/svnserve/server.h +++ b/subversion/svnserve/server.h @@ -65,8 +65,10 @@ typedef struct repository_t { enum username_case_type username_case; /* Case-normalize the username? */ svn_boolean_t use_sasl; /* Use Cyrus SASL for authentication; always false if SVN_HAVE_SASL not defined */ +#ifdef SVN_HAVE_SASL unsigned min_ssf; /* min-encryption SASL parameter */ unsigned max_ssf; /* max-encryption SASL parameter */ +#endif enum access_type auth_access; /* access granted to authenticated users */ enum access_type anon_access; /* access granted to annonymous users */ @@ -125,9 +127,6 @@ typedef struct serve_params_t { /* all configurations should be opened through this factory */ svn_repos__config_pool_t *config_pool; - /* all authz data should be opened through this factory */ - svn_repos__authz_pool_t *authz_pool; - /* The FS configuration to be applied to all repositories. It mainly contains things like cache settings. */ apr_hash_t *fs_config; @@ -152,6 +151,12 @@ typedef struct serve_params_t { coming in from the client. */ apr_size_t error_check_interval; + /* If not 0, error out on requests exceeding this value. */ + apr_uint64_t max_request_size; + + /* If not 0, stop sending a response once it exceeds this value. */ + apr_uint64_t max_response_size; + /* Use virtual-host-based path to repo. */ svn_boolean_t vhost; } serve_params_t; diff --git a/subversion/svnserve/svnserve.c b/subversion/svnserve/svnserve.c index f3a483dbd9208..b4ad64fdbf67b 100644 --- a/subversion/svnserve/svnserve.c +++ b/subversion/svnserve/svnserve.c @@ -155,6 +155,15 @@ enum run_mode { */ #define ACCEPT_BACKLOG 128 +/* Default limit to the client request size in MBytes. This effectively + * limits the size of a paths and individual property values to about + * this value. + * + * Note that (MAX_REQUEST_SIZE + 4M) * THREADPOOL_MAX_SIZE is roughly + * the peak memory usage of the RA layer. + */ +#define MAX_REQUEST_SIZE 16 + #ifdef WIN32 static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET; @@ -210,6 +219,20 @@ void winservice_notify_stop(void) #define SVNSERVE_OPT_MIN_THREADS 271 #define SVNSERVE_OPT_MAX_THREADS 272 #define SVNSERVE_OPT_BLOCK_READ 273 +#define SVNSERVE_OPT_MAX_REQUEST 274 +#define SVNSERVE_OPT_MAX_RESPONSE 275 +#define SVNSERVE_OPT_CACHE_NODEPROPS 276 + +/* Text macro because we can't use #ifdef sections inside a N_("...") + macro expansion. */ +#ifdef CONNECTION_HAVE_THREAD_OPTION +#define ONLY_AVAILABLE_WITH_THEADS \ + "\n" \ + " "\ + "[used only with --threads]" +#else +#define ONLY_AVAILABLE_WITH_THEADS "" +#endif static const apr_getopt_option_t svnserve__options[] = { @@ -296,6 +319,12 @@ static const apr_getopt_option_t svnserve__options[] = "Default is no.\n" " " "[used for FSFS and FSX repositories only]")}, + {"cache-nodeprops", SVNSERVE_OPT_CACHE_NODEPROPS, 1, + N_("enable or disable caching of node properties\n" + " " + "Default is yes.\n" + " " + "[used for FSFS repositories only]")}, {"client-speed", SVNSERVE_OPT_CLIENT_SPEED, 1, N_("Optimize network handling based on the assumption\n" " " @@ -317,34 +346,46 @@ static const apr_getopt_option_t svnserve__options[] = * ### this option never exists when --service exists. */ {"threads", 'T', 0, N_("use threads instead of fork " "[mode: daemon]")}, +#endif +#if APR_HAS_THREADS {"min-threads", SVNSERVE_OPT_MIN_THREADS, 1, N_("Minimum number of server threads, even if idle.\n" " " "Capped to max-threads; minimum value is 0.\n" " " - "Default is 1.\n" - " " - "[used only with --threads]")}, -#if (APR_SIZEOF_VOIDP <= 4) + "Default is 1." + ONLY_AVAILABLE_WITH_THEADS)}, {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1, N_("Maximum number of server threads, even if there\n" " " "are more connections. Minimum value is 1.\n" " " - "Default is 64.\n" + "Default is " APR_STRINGIFY(THREADPOOL_MAX_SIZE) "." + ONLY_AVAILABLE_WITH_THEADS)}, +#endif + {"max-request-size", SVNSERVE_OPT_MAX_REQUEST, 1, + N_("Maximum acceptable size of a client request in MB.\n" " " - "[used only with --threads]")}, -#else - {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1, - N_("Maximum number of server threads, even if there\n" + "This implicitly limits the length of paths and\n" " " - "are more connections. Minimum value is 1.\n" + "property values that can be sent to the server.\n" " " - "Default is 256.\n" + "Also the peak memory usage for protocol handling\n" " " - "[used only with --threads]")}, -#endif -#endif + "per server thread or sub-process.\n" + " " + "0 disables the size check; default is " + APR_STRINGIFY(MAX_REQUEST_SIZE) ".")}, + {"max-response-size", SVNSERVE_OPT_MAX_RESPONSE, 1, + N_("Maximum acceptable server response size in MB.\n" + " " + "Longer responses get truncated and return an\n" + " " + "error. This limits the server load e.g. when\n" + " " + "checking out at the wrong path level.\n" + " " + "Default is 0 (disabled).")}, {"foreground", SVNSERVE_OPT_FOREGROUND, 0, N_("run in foreground (useful for debugging)\n" " " @@ -680,6 +721,7 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) svn_boolean_t is_multi_threaded; enum connection_handling_mode handling_mode = CONNECTION_DEFAULT; svn_boolean_t cache_fulltexts = TRUE; + svn_boolean_t cache_nodeprops = TRUE; svn_boolean_t cache_txdeltas = TRUE; svn_boolean_t cache_revprops = FALSE; svn_boolean_t use_block_read = FALSE; @@ -710,6 +752,9 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) /* Initialize the FS library. */ SVN_ERR(svn_fs_initialize(pool)); + /* Initialize the efficient Authz support. */ + SVN_ERR(svn_repos_authz_initialize(pool)); + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); params.root = "/"; @@ -721,13 +766,14 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; params.logger = NULL; params.config_pool = NULL; - params.authz_pool = NULL; params.fs_config = NULL; params.vhost = FALSE; params.username_case = CASE_ASIS; params.memory_cache_size = (apr_uint64_t)-1; params.zero_copy_limit = 0; params.error_check_interval = 4096; + params.max_request_size = MAX_REQUEST_SIZE * 0x100000; + params.max_response_size = 0; while (1) { @@ -855,7 +901,12 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) break; case 'M': - params.memory_cache_size = 0x100000 * apr_strtoi64(arg, NULL, 0); + { + apr_uint64_t sz_val; + SVN_ERR(svn_cstring_atoui64(&sz_val, arg)); + + params.memory_cache_size = 0x100000 * sz_val; + } break; case SVNSERVE_OPT_CACHE_TXDELTAS: @@ -870,6 +921,10 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) cache_revprops = svn_tristate__from_word(arg) == svn_tristate_true; break; + case SVNSERVE_OPT_CACHE_NODEPROPS: + cache_nodeprops = svn_tristate__from_word(arg) == svn_tristate_true; + break; + case SVNSERVE_OPT_BLOCK_READ: use_block_read = svn_tristate__from_word(arg) == svn_tristate_true; break; @@ -891,6 +946,14 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) } break; + case SVNSERVE_OPT_MAX_REQUEST: + params.max_request_size = 0x100000 * apr_strtoi64(arg, NULL, 0); + break; + + case SVNSERVE_OPT_MAX_RESPONSE: + params.max_response_size = 0x100000 * apr_strtoi64(arg, NULL, 0); + break; + case SVNSERVE_OPT_MIN_THREADS: min_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0); break; @@ -980,6 +1043,8 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) cache_txdeltas ? "1" :"0"); svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, cache_fulltexts ? "1" :"0"); + svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_NODEPROPS, + cache_nodeprops ? "1" :"0"); svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, cache_revprops ? "2" :"0"); svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, @@ -988,10 +1053,6 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) SVN_ERR(svn_repos__config_pool_create(¶ms.config_pool, is_multi_threaded, pool)); - SVN_ERR(svn_repos__authz_pool_create(¶ms.authz_pool, - params.config_pool, - is_multi_threaded, - pool)); /* If a configuration file is specified, load it and any referenced * password and authorization files. */ @@ -999,11 +1060,10 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { params.base = svn_dirent_dirname(config_filename, pool); - SVN_ERR(svn_repos__config_pool_get(¶ms.cfg, NULL, + SVN_ERR(svn_repos__config_pool_get(¶ms.cfg, params.config_pool, config_filename, TRUE, /* must_exist */ - FALSE, /* names_case_sensitive */ NULL, pool)); } @@ -1030,17 +1090,21 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null, redirect_stdout); - SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool)); + /* We are an interactive server, i.e. can't use APR buffering on + * stdin. */ + SVN_ERR(svn_stream_for_stdin2(&stdin_stream, FALSE, pool)); SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); /* Use a subpool for the connection to ensure that if SASL is used * the pool cleanup handlers that call sasl_dispose() (connection_pool) * and sasl_done() (pool) are run in the right order. See issue #3664. */ connection_pool = svn_pool_create(pool); - conn = svn_ra_svn_create_conn4(NULL, stdin_stream, stdout_stream, + conn = svn_ra_svn_create_conn5(NULL, stdin_stream, stdout_stream, params.compression_level, params.zero_copy_limit, params.error_check_interval, + params.max_request_size, + params.max_response_size, connection_pool); err = serve(conn, ¶ms, connection_pool); svn_pool_destroy(connection_pool); |